Commit graph

2319 commits

Author SHA1 Message Date
Charlie Marsh
dbb34804a4
Change Option<Result> to Result<Option> in importer (#7089) 2023-09-03 16:23:42 +00:00
Charlie Marsh
3c7486817b
Use symbol import for NPY003 replacement (#7083) 2023-09-03 16:53:28 +01:00
Charlie Marsh
3c3c5b5c57
Support length-2 lists in dictionary comprehension rewrites (#7081) 2023-09-03 13:34:10 +00:00
Charlie Marsh
b0d171ac19
Supported starred exceptions in length-one tuple detection (#7080) 2023-09-03 13:31:13 +00:00
Charlie Marsh
b70dde4a77
Avoid attempting to fix invalid Optional annotations (#7079) 2023-09-03 13:23:36 +00:00
Dhruv Manilawala
4eaa412370
Update LibCST (#7062)
## Summary

This PR updates the revision of `LibCST` dependency to 9c263aa897
inorder to fix https://github.com/astral-sh/ruff/issues/4899

## Test Plan

The test case including the carriage return (`\r`) character was added for
`F504` and then `cargo test`.

fixes: #4899
2023-09-03 09:11:24 +05:30
Charlie Marsh
577280c8be
Rename ruff_python_formatter/README.md to CONTRIBUTING.md (#7065) 2023-09-02 16:25:23 +00:00
Charlie Marsh
45680bbb44
Avoid duplicate fixes for multi-import imports in RUF017 (#7063)
If a user has `import collections, functools, operator`, and we try to
import from `functools` and `operator`, we end up adding two identical
synthetic edits to preserve that import statement. We need to dedupe
them.

Closes https://github.com/astral-sh/ruff/issues/7059.
2023-09-02 12:58:18 +01:00
Justin Prieto
71ff6f911d
Fix getattr calls on int literals (#7057) 2023-09-02 11:45:35 +00:00
Micha Reiser
c05e4628b1
Introduce Token element (#7048) 2023-09-02 10:05:47 +02:00
Charlie Marsh
2f3a950f6f
Bump version to 0.0.287 (#7038) 2023-09-01 17:32:26 +01:00
Charlie Marsh
dea65536e9
Fix placement for comments within f-strings concatenations (#7047)
## Summary

Restores the dangling comment handling for f-strings, which broke with
the parenthesized expression code.

Closes https://github.com/astral-sh/ruff/issues/6898.

## Test Plan

`cargo test`

No change in any of the similarity indexes or changed file counts:

| project | similarity index | total files | changed files |

|--------------|------------------:|------------------:|------------------:|
| cpython | 0.76083 | 1789 | 1632 |
| django | 0.99957 | 2760 | 67 |
| transformers | 0.99927 | 2587 | 468 |
| twine | 0.99982 | 33 | 1 |
| typeshed | 0.99978 | 3496 | 2173 |
| warehouse | 0.99818 | 648 | 24 |
| zulip | 0.99942 | 1437 | 32 |
2023-09-01 16:27:32 +00:00
Sergey Chudov
33806b8b7c
Fixed panic in missing_copyright_notice (#7029) 2023-09-01 13:58:48 +00:00
Charlie Marsh
afcd00da56
Create ruff_notebook crate (#7039)
## Summary

This PR moves `ruff/jupyter` into its own `ruff_notebook` crate. Beyond
the move itself, there were a few challenges:

1. `ruff_notebook` relies on the source map abstraction. I've moved the
source map into `ruff_diagnostics`, since it doesn't have any
dependencies on its own and is used alongside diagnostics.
2. `ruff_notebook` has a couple tests for end-to-end linting and
autofixing. I had to leave these tests in `ruff` itself.
3. We had code in `ruff/jupyter` that relied on Python lexing, in order
to provide a more targeted error message in the event that a user saves
a `.py` file with a `.ipynb` extension. I removed this in order to avoid
a dependency on the parser, it felt like it wasn't worth retaining just
for that dependency.

## Test Plan

`cargo test`
2023-09-01 13:56:44 +00:00
Charlie Marsh
08e246764f
Refactor ruff_cli's run method to return on each branch (#7040)
## Summary

I think the fallthrough here for some branches is a little confusing.
Now each branch either runs a command that returns `Result<ExitStatus>`,
or runs a command that returns `Result<()>` and then explicitly returns
`Ok(ExitStatus::SUCCESS)`.
2023-09-01 14:15:38 +01:00
Chris Pryer
0489bbc54c
Match Black's formatting of trailing comments containing NBSP (#7030) 2023-09-01 14:52:59 +02:00
Charlie Marsh
60132da7bb
Add a NotebookError type to avoid returning Diagnostics on error (#7035)
## Summary

This PR refactors the error-handling cases around Jupyter notebooks to
use errors rather than `Box<Diagnostics>`, which creates some oddities
in the downstream handling. So, instead of formatting errors as
diagnostics _eagerly_ (in the notebook methods), we now return errors
and convert those errors to diagnostics at the last possible moment (in
`diagnostics.rs`). This is more ergonomic, as errors can be composed and
reported-on in different ways, whereas diagnostics require a `Printer`,
etc.

See, e.g.,
https://github.com/astral-sh/ruff/pull/7013#discussion_r1311136301.

## Test Plan

Ran `cargo run` over a Python file labeled with a `.ipynb` suffix, and
saw:

```
foo.ipynb:1:1: E999 SyntaxError: Expected a Jupyter Notebook, which must be internally stored as JSON, but found a Python source file: expected value at line 1 column 1
```
2023-09-01 11:08:05 +00:00
Chris Pryer
17a44c0078
Exclude pragma comments from measured line width (#7008)
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-09-01 06:34:51 +00:00
Charlie Marsh
376d3caf47
Treat empty-line separated comments as trailing statement comments (#6999)
## Summary

This PR modifies our between-statement comment handling such that
comments that are not separated by a statement by any newlines continue
to be treated as leading comments on the statement, but comments that
_are_ separated are instead formatted as trailing comments on the
preceding statement.

See, e.g., the originating snippet:

```python
DEFAULT_TEMPLATE = "flatpages/default.html"

# This view is called from FlatpageFallbackMiddleware.process_response
# when a 404 is raised, which often means CsrfViewMiddleware.process_view
# has not been called even if CsrfViewMiddleware is installed. So we need
# to use @csrf_protect, in case the template needs {% csrf_token %}.
# However, we can't just wrap this view; if no matching flatpage exists,
# or a redirect is required for authentication, the 404 needs to be returned
# without any CSRF checks. Therefore, we only
# CSRF protect the internal implementation.


def flatpage(request, url):
    pass
```

Here, we need to ensure that the `def flatpage` is precede by two empty
lines. However, we want those two empty lines to be enforced from the
_end_ of the comment block, _unless_ the comments are directly atop the
`def flatpage`.

I played with this a bit, and I think the simplest conceptual model and
implementation is to instead treat those as trailing comments on the
preceding node. The main difficulty with this approach is that, in order
to be fully compatible with Black, we'd sometimes need to insert
newlines _between_ the preceding node and its trailing comments. See,
e.g.:

```python
def func():
    ...
# comment

x = 1
```

In this case, we'd need to insert two blank lines between `def func():
...` and `# comment`, but `# comment` is trailing comment on `def
func(): ...`. So, we'd need to take this case into account in the
various nodes that _require_ newlines after them: functions, classes,
and imports. After some discussion, we've opted _not_ to support this,
and just treat these as trailing comments -- so we won't insert newlines
there. This means our handling is still identical to Black's on
Black-formatted code, but avoids moving such trailing comments on
unformatted code.

I dislike that the empty handling is so complex, and that it's split
between so many different nodes, but this is really tricky. Continuing
to treat these as leading comments is very difficult too, since we'd
need to do similar tricks for the leading comment handling in those
nodes, and influencing leading comments is even harder, since they're
all formatted _before_ the node itself.

Closes https://github.com/astral-sh/ruff/issues/6761.

## Test Plan

`cargo test`

Surprisingly, it doesn't change the similarity at all (apart from a
0.00001 change in CPython), but I manually confirmed that it did fix the
originating issue in Django.

Before:

| project      | similarity index |
|--------------|------------------|
| cpython      | 0.76082          |
| django       | 0.99921          |
| transformers | 0.99854          |
| twine        | 0.99982          |
| typeshed     | 0.99953          |
| warehouse    | 0.99648          |
| zulip        | 0.99928          |


After:

| project      | similarity index |
|--------------|------------------|
| cpython      | 0.76081          |
| django       | 0.99921          |
| transformers | 0.99854          |
| twine        | 0.99982          |
| typeshed     | 0.99953          |
| warehouse    | 0.99648          |
| zulip        | 0.99928          |
2023-08-31 20:55:05 +00:00
Charlie Marsh
51d69b448c
Improve compatibility between multi-statement PYI rules (#7024)
## Summary

This PR modifies a few of our rules related to which statements (and how
many) are allowed in function bodies within `.pyi` files, to improve
compatibility with flake8-pyi and improve the interplay dynamics between
them. Each change fixes a deviation from flake8-pyi:

- We now always trigger the multi-statement rule (PYI048) regardless of
whether one of the statements is a docstring.
- We no longer trigger the `...` rule (PYI010) if the single statement
is a docstring or a `pass` (since those are covered by other rules).
- We no longer trigger the `...` rule (PYI010) if the function body
contains multiple statements (since that's covered by PYI048).

Closes https://github.com/astral-sh/ruff/issues/7021.

## Test Plan

`cargo test`
2023-08-31 21:45:26 +01:00
Charlie Marsh
68f605e80a
Fix WithItem ranges for parenthesized, non-as items (#6782)
## Summary

This PR attempts to address a problem in the parser related to the
range's of `WithItem` nodes in certain contexts -- specifically,
`WithItem` nodes in parentheses that do not have an `as` token after
them.

For example,
[here](https://play.ruff.rs/71be2d0b-2a04-4c7e-9082-e72bff152679):

```python
with (a, b):
    pass
```

The range of the `WithItem` `a` is set to the range of `(a, b)`, as is
the range of the `WithItem` `b`. In other words, when we have this kind
of sequence, we use the range of the entire parenthesized context,
rather than the ranges of the items themselves.

Note that this also applies to cases
[like](https://play.ruff.rs/c551e8e9-c3db-4b74-8cc6-7c4e3bf3713a):

```python
with (a, b, c as d):
    pass
```

You can see the issue in the parser here:

```rust
#[inline]
WithItemsNoAs: Vec<ast::WithItem> = {
    <location:@L> <all:OneOrMore<Test<"all">>> <end_location:@R> => {
        all.into_iter().map(|context_expr| ast::WithItem { context_expr, optional_vars: None, range: (location..end_location).into() }).collect()
    },
}
```

Fixing this issue is... very tricky. The naive approach is to use the
range of the `context_expr` as the range for the `WithItem`, but that
range will be incorrect when the `context_expr` is itself parenthesized.
For example, _that_ solution would fail here, since the range of the
first `WithItem` would be that of `a`, rather than `(a)`:

```python
with ((a), b):
    pass
```

The `with` parsing in general is highly precarious due to ambiguities in
the grammar. Changing it in _any_ way seems to lead to an ambiguous
grammar that LALRPOP fails to translate. Consensus seems to be that we
don't really understand _why_ the current grammar works (i.e., _how_ it
avoids these ambiguities as-is).

The solution implemented here is to avoid changing the grammar itself,
and instead change the shape of the nodes returned by various rules in
the grammar. Specifically, everywhere that we return `Expr`, we instead
return `ParenthesizedExpr`, which includes a parenthesized range and the
underlying `Expr` itself. (If an `Expr` isn't parenthesized, the ranges
will be equivalent.) In `WithItemsNoAs`, we can then use the
parenthesized range as the range for the `WithItem`.
2023-08-31 16:21:29 +01:00
Zanie Blue
96a9717c1a
Add hidden --preview / --no-preview options to ruff check (#7009)
Per discussion at https://github.com/astral-sh/ruff/discussions/6998

<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Adds a `--preview` and `--no-preview` option to the CLI for `ruff check`
and corresponding settings. The CLI options are hidden for now.

Available in the settings as `preview = true` or `preview = false`.

Does not include environment variable configuration, although we may add
it in the future.

## Test Plan

<!-- How was it tested? -->

`cargo build`

Future work will build on this setting, such as toggling the mode during
a test.
2023-08-31 09:51:59 -05:00
magic-akari
f4ba0ea144
Allow tab_width to be configable (#7016) 2023-08-31 07:40:03 +00:00
Micha Reiser
92143afeee
Group binary operators with same precedence only (#7010) 2023-08-31 09:19:45 +02:00
Micha Reiser
eb552da8a9
Avoid parenthesizing multiline strings in binary expressions (#6973) 2023-08-30 16:03:17 +02:00
Charlie Marsh
e2b2b1759f
Handle keyword comments between = and value (#6883)
## Summary

This PR adds comment handling for comments between the `=` and the
`value` for keywords, as in the following cases:

```python
func(
    x  # dangling
    =  # dangling
    # dangling
    1,
    **  # dangling
    y
)
```

(Comments after the `**` were already handled in some cases, but I've
unified the handling with the `=` handling.)

Note that, previously, comments between the `**` and its value were
rendered as trailing comments on the value (so they'd appear after `y`).
This struck me as odd since it effectively re-ordered the comment with
respect to its closest AST node (the value). I've made them leading
comments, though I don't know that that's a significant improvement. I
could also imagine us leaving them where they are.
2023-08-30 09:52:51 -04:00
Chris Pryer
a3f4d7745a
Use reserved width to include line suffix measurement (#6901)
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-08-30 08:07:11 +00:00
Micha Reiser
edfd888bd6
Add unicode benchmark (#7002) 2023-08-30 09:57:57 +02:00
Micha Reiser
9c382e8291
Show changed files (#7003) 2023-08-30 06:47:45 +00:00
Charlie Marsh
eb2b226142
Unset after_class_docstring state on every iteration (#7001) 2023-08-30 08:20:28 +02:00
Victor Hugo Gomes
31947af6a3
Don't "flatten" nested if expressions when formatting (#6996) 2023-08-30 04:11:58 +00:00
Charlie Marsh
b404e54f33
Remove unnecessary Comment#slice calls (#6997) 2023-08-30 00:44:11 +00:00
qdegraaf
34e8de738e
[perflint] Expand PERF401 and PERF402 with type checks (#6994)
## Summary

Attempt at a small improvement to two `perflint` rules using the new
type inference capabilities to only flag `PERF401` and `PERF402` for
values we infer to be lists. This makes the rule more conservative, as
it only flags values that we _know_ to be lists, but it's overall a
desirable change, as it favors false negatives over false positives for
a "nice-to-have" rule.

Closes https://github.com/astral-sh/ruff/issues/6995.

## Test Plan

Add non-list value cases and make sure all old cases are still caught.
2023-08-29 19:15:29 -04:00
qdegraaf
1550a6bfe7
[perflint] Chore: correct PERF401 fixture comments (#6993)
## Summary
Fixes comments in PERF401 fixture to correctly reflect how rule works
2023-08-29 17:16:31 -04:00
Anselm Hahn
dc4db39f78
docs: 📝 Updating the example for assert(S101) (#6986)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Rewriting the `if`-comparison to focus on the meaning of rule ` assert
S101`.

Fixes #6984

## Test Plan

<!-- How was it tested? -->

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
2023-08-29 16:08:08 -05:00
qdegraaf
f3aaf84a28
Move refurb/helpers utils to ruff_python_semantic for broader use (#6990)
## Summary

The utils added for `refurb` in its `helpers.rs` file could be useful
for many other plugins. (Such as the PERF4XX codes, see e.g.
https://github.com/astral-sh/ruff/pull/6132 ).

This PR moves them to `ruff_python_semantic::analyzers::typing` as
suggested in
https://github.com/astral-sh/ruff/pull/6132#issuecomment-1697910093

## Test Plan

Confirmed `refurb` and all other tests still work
2023-08-29 14:45:09 -04:00
Charlie Marsh
5de95d7054
Reuse FormatResult and FormatterIterationError in format_stdin.rs (#6985)
## Summary

Ensures that we use the same error types and messages. Also renames
those struct to `FormatCommand*` for consistency, and removes the
`FormatCommandResult::Skipped` variant in favor of skipping in the
iterator directly.
2023-08-29 17:41:53 +00:00
Charlie Marsh
8d1610d960
Implement Display on formatter structs (#6983)
Feedback from
https://github.com/astral-sh/ruff/pull/6948#discussion_r1308260021.
2023-08-29 16:57:26 +00:00
Charlie Marsh
fad23bbe60
Add a --check flag to the formatter CLI (#6982)
## Summary

Returns an exit code of 1 if any files would be reformatted:

```
ruff on  charlie/format-check:main [$?⇡] is 📦 v0.0.286 via 🐍 v3.11.2 via 🦀 v1.72.0
❯ cargo run -p ruff_cli -- format foo.py --check
   Compiling ruff_cli v0.0.286 (/Users/crmarsh/workspace/ruff/crates/ruff_cli)
    Finished dev [unoptimized + debuginfo] target(s) in 1.69s
     Running `target/debug/ruff format foo.py --check`
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
1 file would be reformatted
ruff on  charlie/format-check:main [$?⇡] is 📦 v0.0.286 via 🐍 v3.11.2 via 🦀 v1.72.0 took 2s
❯ echo $?
1
```

Closes #6966.
2023-08-29 12:40:00 -04:00
Charlie Marsh
25c374856a
Move stdin formatting to its own command file (#6981)
## Summary

This is similar to `commands::check` vs. `commands::check_stdin`, and
gets the logic out of the parent file (`lib.rs`). It also ensures that
we avoid formatting files that should be excluded when `--force-exclude`
is provided.
2023-08-29 16:06:10 +00:00
Charlie Marsh
34221346c1
Rename run.rs command to check.rs (#6980)
The CLI command is called "check", so this is more consistent (and
consistent with the pattern used in other commands).
2023-08-29 15:52:06 +00:00
Chris Pryer
924f10186f
Update dynamic_text builder doc comment (#6978) 2023-08-29 16:29:11 +02:00
Charlie Marsh
b7634b6ede
Fix typo in banned-from (#6977)
Oops...
2023-08-29 09:39:09 -04:00
Dhruv Manilawala
4d49d5e845
Add eat_char2 for the lexer (#6968)
## Summary

This PR adds a new helper method on the `Cursor` called `eat_char2`
which is similar to `eat_char` but accepts 2 characters instead of 1. It'll
`bump` the cursor twice if both characters are found on lookahead.

## Test Plan

`cargo test`
2023-08-29 17:18:02 +05:30
Micha Reiser
715d86dae9
Remove Comprehension priority (#6947) 2023-08-29 08:30:15 +02:00
Micha Reiser
adb48692d6
Use optional parentheses for tuples in return statements (#6875) 2023-08-29 08:30:05 +02:00
Charlie Marsh
19ccf1d073
Make RUF100 a suggested fix (#6967)
I made this automatic when I removed the deprecated "unspecified"
method, but I think suggested is actually more appropriate.
2023-08-29 04:28:52 +00:00
Charlie Marsh
e3c36465ec
Base parameter-find lookup on range, rather than name (#6960)
## Summary

This simplifies the signature a bit and is more reliable in the
(unusual? invalid?) case of duplicate parameters.
2023-08-29 01:39:38 +00:00
Valeriy Savchenko
7e36284684
[refurb] Implement check-and-remove-from-set rule (FURB132) (#6904)
## Summary

This PR is a continuation of #6897 and #6702 and me replicating `refurb`
rules (#1348). It adds support for
[FURB132](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/set_discard.py)

## Test Plan

I included a new test + checked that all other tests pass.
2023-08-29 01:17:26 +00:00
Valeriy Savchenko
c448b4086a
[refurb] Implement delete-full-slice rule (FURB131) (#6897)
## Summary

This PR is a continuation of #6702 and me replicating `refurb` rules
(#1348). It adds support for
[FURB131](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/no_del.py.

## Test Plan

I included a new test + checked that all other tests pass.
2023-08-28 20:52:38 -04:00