Commit Graph

2218 Commits

Author SHA1 Message Date
konsti ba4c27598a
Document IO Error (#6712)
`IOError` is special, it is not actually a lint but an error before
linting. I'm not entirely sure how to document it since it does not
match the general lint rule pattern (`Checks that the file can be read
in its entirety.` is imho worse).

I added the in my experience two most common reasons for io errors on
unix systems and linked two tutorials on how to fix them.

See https://github.com/astral-sh/ruff/issues/2646

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-08-22 11:46:18 +02:00
Victor Hugo Gomes 0f9ccfcad9
Format `PatternMatchSingleton` (#6741) 2023-08-22 08:23:47 +02:00
Charlie Marsh fa32cd9b6f
Truncate some messages in diagnostics (#6748)
## Summary

I noticed this in the ecosystem CI check from
https://github.com/astral-sh/ruff/pull/6742. If we include source code
directly in a diagnostic, we need to be careful to avoid rendering
multi-line diagnostics or even excessively long diagnostics.

## Test Plan

`cargo test`
2023-08-21 23:46:24 -04:00
Victor Hugo Gomes 0aad0c41f6
[`pylint`] Implement `no-self-use` (`R6301`) (#6574) 2023-08-22 03:44:38 +00:00
Charlie Marsh 424b8d4ad2
Use a single node hierarchy to track statements and expressions (#6709)
## Summary

This PR is a follow-up to the suggestion in
https://github.com/astral-sh/ruff/pull/6345#discussion_r1285470953 to
use a single stack to store all statements and expressions, rather than
using separate vectors for each, which gives us something closer to a
full-fidelity chain. (We can then generalize this concept to include all
other AST nodes too.)

This is in part made possible by the removal of the hash map from
`&Stmt` to `StatementId` (#6694), which makes it much cheaper to store
these using a single interface (since doing so no longer introduces the
requirement that we hash all expressions).

I'll follow-up with some profiling, but a few notes on how the data
requirements have changed:

- We now store a `BranchId` for every expression, not just every
statement, so that's an extra `u32`.
- We now store a single `NodeId` on every snapshot, rather than separate
`StatementId` and `ExpressionId` IDs, so that's one fewer `u32` for each
snapshot.
- We're probably doing a few more lookups in general, since any calls to
`current_statement()` etc. now have to iterate up the node hierarchy
until they identify the first statement.

## Test Plan

`cargo test`
2023-08-21 21:32:57 -04:00
Charlie Marsh abc5065fc7
Avoid E231 if comma is at end-of-line (#6747)
## Summary

I don't know how this could come up in valid Python, but anyway...

Closes https://github.com/astral-sh/ruff/issues/6738.
2023-08-21 20:47:20 -04:00
Victor Hugo Gomes 37f4920e1e
Don't trigger `eq-without-hash` when `__hash__` is explicitly set to `None` (#6739) 2023-08-21 23:51:21 +00:00
Charlie Marsh c0df99b965
Avoid attempting to fix unconventional submodule imports (#6745)
## Summary

Avoid attempting to rewrite `import matplotlib.pyplot` as `import
matplotlib.pyplot as plt`. We can't support these right now, since we
don't track references at the attribute level (like
`matplotlib.pyplot`).

Closes https://github.com/astral-sh/ruff/issues/6719.
2023-08-21 23:45:32 +00:00
Charlie Marsh 7650c6ee45
Support C419 autofixes for set comprehensions (#6744)
Closes https://github.com/astral-sh/ruff/issues/6713.
2023-08-21 23:41:13 +00:00
Charlie Marsh 7b14d17e39
Ignore star imports when importing symbols in fixes (#6743)
## Summary

Given:

```python
from sys import *

exit(0)
```

We can't add `exit` to `from sys import *`, so we should just ignore it.
Ideally, we'd just resolve `exit` in the first place (since it's
imported from `from sys import *`), but as long as we don't support
wildcard imports, this is more consistent.

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

## Test Plan

`cargo test`
2023-08-21 23:31:30 +00:00
Charlie Marsh 4678f7dafe
Remove parenthesis lexing in RSE102 (#6732)
## Summary

Now that we have an `Arguments` node, we can just use the range of the
arguments directly to find the parentheses in `raise Error()`.
2023-08-21 20:59:06 +00:00
konsti b182368008
Simplify suite formatting (#6722)
Avoid the nesting in a macro by using the new `WithNodeLevel` to
`PyFormatter` deref. No changes otherwise.

I wanted to follow this up with quickly fixing the typeshed empty line
rules but they turned out a lot more complex than i had anticipated.
2023-08-21 21:01:51 +02:00
Charlie Marsh e032fbd2e7
Remove `remove_super_arguments` (#6735)
Now that we have an `Arguments` node, we can use it directly to get the
range.
2023-08-21 13:04:07 -04:00
Micha Reiser 17a26e6ff3
Fix `fmt:skip` for function with return type (#6733) 2023-08-21 17:45:23 +02:00
Charlie Marsh d5a51b4e45
Allow `ctypes.WinError()` in flake8-raise (#6731)
Closes https://github.com/astral-sh/ruff/issues/6730.
2023-08-21 14:57:34 +00:00
Charlie Marsh 83f68891e0
Allow next in FBT exclusions (#6729)
Closes https://github.com/astral-sh/ruff/issues/6711.
2023-08-21 14:56:38 +00:00
konsti aafde6db28
Remove some indexing (#6728)
**Summary** A common pattern in the code used to be
```rust
if statements.len() != 1 {
    return;
}
use_single_entry(statements[0])?;
```
which can be better expressed as
```rust
let [statement] = statements else {
    return;
};
use_single_entry(statements)?;
```

Direct indexing can cause panics if you don't manually take care of
checking the length, while matching (such as if-let or let-else) can
never panic.

This isn't a complete refactor, i've just removed some of the obvious
cases. I've specifically looked for `.len() != 1` and fixed those.

**Test Plan** No functional changes
2023-08-21 16:56:15 +02:00
Charlie Marsh 2405536d03
Remove unnecessary LibCST usage in key-in-dict (#6727)
## Summary

We're using LibCST to ensure that we return the full parenthesized range
of an expression, for display purposes. We can just use
`parenthesized_range` which is more efficient and removes one LibCST
dependency.

## Test Plan

`cargo test`
2023-08-21 10:32:09 -04:00
Micha Reiser f017555d53
Parenthesize NamedExpr if target breaks (#6714) 2023-08-21 16:29:26 +02:00
Charlie Marsh be96e0041a
Accept empty inner calls in C414 (#6725)
Closes https://github.com/astral-sh/ruff/issues/6716.
2023-08-21 14:05:09 +00:00
Harutaka Kawamura 3c2dd5e42e
Remove confusing comment on `get_parametrize_name_range` (#6724) 2023-08-21 08:52:48 -04:00
Micha Reiser 8b347cdaa9
Simplify IfRequired needs parentheses condition (#6678) 2023-08-21 07:11:31 +00:00
Tom Kuson 2a8d24dd4b
Format function and class definitions into a single line if its body is an ellipsis (#6592) 2023-08-21 09:02:23 +02:00
Charlie Marsh bb5fbb1b5c
Use simple lexer for argument removal (#6710) 2023-08-21 04:16:29 +00:00
Harutaka Kawamura 086e11087f
[`flake8-pytest-style`] Autofix `PT014` (#6698) 2023-08-21 03:45:12 +00:00
Charlie Marsh 1b7e4a12a9
Refactor `remove_unused_variable` to take `&Binding` (#6707) 2023-08-20 15:50:57 +00:00
Charlie Marsh da1697121e
Add `BranchId` to the model snapshot (#6706)
This _probably_ never matters given the set of rules we support and in
fact I'm having trouble thinking of a test-case for it, but it's
definitely incorrect _not_ to pass on the `BranchId` here.
2023-08-20 15:35:49 +00:00
Harutaka Kawamura 419615f29b
Add docs for `E275`, `E231`, `E251`, and `E252` (#6700) 2023-08-20 14:51:50 +00:00
Charlie Marsh a742a562fd
Ignore multi-comparisons in `repeated-equality-comparison-target` (#6705)
Given `foo == "a" == "b" or foo == "c"`, we were suggesting `foo in
{"a", "b", "c"}`.
2023-08-20 14:41:10 +00:00
Harutaka Kawamura 129b19050a
Refactor `flake8_pytest_style/rules/parametrize.rs` (#6703) 2023-08-20 14:30:26 +00:00
Konrad Listwan-Ciesielski 0dc23da1d0
Add docs for `DTZ011` and `DTZ012` (#6688) 2023-08-20 10:21:10 -04:00
Harutaka Kawamura c62e544cba
Add doc for `E999` (#6699) 2023-08-20 14:14:22 +00:00
Charlie Marsh 7e9023b6f8
Use `typing_extensions.TypeAlias` for PYI026 fixes on pre-3.10 (#6696)
Closes https://github.com/astral-sh/ruff/issues/6695.
2023-08-19 22:16:44 +00:00
Harutaka Kawamura a489b96a65
[`flake8-pie`] Implement `unnecessary-range-start` (`PIE808`) (#6690) 2023-08-19 21:59:11 +00:00
Charlie Marsh 17af12e57c
Add branch detection to the semantic model (#6694)
## Summary

We have a few rules that rely on detecting whether two statements are in
different branches -- for example, different arms of an `if`-`else`.
Historically, the way this was implemented is that, given two statement
IDs, we'd find the common parent (by traversing upwards via our
`Statements` abstraction); then identify branches "manually" by matching
the parents against `try`, `if`, and `match`, and returning iterators
over the arms; then check if there's an arm for which one of the
statements is a child, and the other is not.

This has a few drawbacks:

1. First, the code is generally a bit hard to follow (Konsti mentioned
this too when working on the `ElifElseClause` refactor).

2. Second, this is the only place in the codebase where we need to go
from `&Stmt` to `StatementID` -- _everywhere_ else, we only need to go
in the _other_ direction. Supporting these lookups means we need to
maintain a mapping from `&Stmt` to `StatementID` that includes every
`&Stmt` in the program. (We _also_ end up maintaining a `depth` level
for every statement.) I'd like to get rid of these requirements to
improve efficiency, reduce complexity, and enable us to treat AST modes
more generically in the future. (When I looked at adding the `&Expr` to
our existing statement-tracking infrastructure, maintaining a hash map
with all the statements noticeably hurt performance.)

The solution implemented here instead makes branches a first-class
concept in the semantic model. Like with `Statements`, we now have a
`Branches` abstraction, where each branch points to its optional parent.
When we store statements, we store the `BranchID` alongside each
statement. When we need to detect whether two statements are in the same
branch, we just realize each statement's branch path and compare the
two. (Assuming that the two statements are in the same scope, then
they're on the same branch IFF one branch path is a subset of the other,
starting from the top.) We then add some calls to the visitor to push
and pop branches in the appropriate places, for `if`, `try`, and `match`
statements.

Note that a branch is not 1:1 with a statement; instead, each branch is
closer to a suite, but not _every_ suite is a branch. For example, each
arm in an `if`-`elif`-`else` is a branch, but the `else` in a `for` loop
is not considered a branch.

In addition to being much simpler, this should also be more efficient,
since we've shed the entire `&Stmt` hash map, plus the `depth` that we
track on `StatementWithParent` in favor of a single `Option<BranchID>`
on `StatementWithParent` plus a single vector for all branches. The
lookups should be faster too, since instead of doing a bunch of jumps
around with the hash map + repeated recursive calls to find the common
parents, we instead just do a few simple lookups in the `Branches`
vector to realize and compare the branch paths.

## Test Plan

`cargo test` -- we have a lot of coverage for this, which we inherited
from PyFlakes
2023-08-19 21:28:17 +00:00
Chris Pryer 648333b8b2
`ruff_formatter` crate doc comment fixes (#6677) 2023-08-19 17:42:02 +01:00
Charlie Marsh 3849fa0cf1
Rewrite `yield-in-for-loop` to avoid recursing over body (#6692)
## Summary

This is much simpler and avoids (1) multiple passes over the entire
function body, (2) requiring the rule to do its own binding tracking (we
can just use the semantic model), and (3) a usage of `StatementKey`.

In general, where we can, we should try to remove these kinds of custom
visitors that track name references, and instead rely on the semantic
model.

## Test Plan

`cargo test`
2023-08-19 11:25:29 -04:00
Victor Hugo Gomes 59e533047a
Fix typo in `ruff_python_formatter` documentation (#6687)
## Summary

In the documentation was written `Javascript` but we are working with
`Python` here :)

## Test Plan

n/a
2023-08-18 19:16:09 -04:00
Charlie Marsh 053b1145f0
Avoid panic in unused arguments rule for parameter-free lambda (#6679)
## Summary

This was just a mistake in pattern-matching with no test coverage.

## Test Plan

`cargo test`
2023-08-18 18:29:31 +00:00
Charlie Marsh 6a5acde226
Make `Parameters` an optional field on `ExprLambda` (#6669)
## Summary

If a lambda doesn't contain any parameters, or any parameter _tokens_
(like `*`), we can use `None` for the parameters. This feels like a
better representation to me, since, e.g., what should the `TextRange` be
for a non-existent set of parameters? It also allows us to remove
several sites where we check if the `Parameters` is empty by seeing if
it contains any arguments, so semantically, we're already trying to
detect and model around this elsewhere.

Changing this also fixes a number of issues with dangling comments in
parameter-less lambdas, since those comments are now automatically
marked as dangling on the lambda. (As-is, we were also doing something
not-great whereby the lambda was responsible for formatting dangling
comments on the parameters, which has been removed.)

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

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

## Test Plan

`cargo test`
2023-08-18 15:34:54 +00:00
Micha Reiser ea72d5feba
Refactor `SourceKind` to store file content (#6640) 2023-08-18 13:45:38 +00:00
Charlie Marsh 2aeb27334d
Avoid cloning source code multiple times (#6629)
## Summary

In working on https://github.com/astral-sh/ruff/pull/6628, I noticed
that we clone the source code contents, potentially multiple times,
prior to linting. The issue is that `SourceKind::Python` takes a
`String`, so we first have to provide it with a `String`. In the stdin
case, that means cloning. However, on top of this, we then have to clone
`source_kind.contents()` because `SourceKind` gets mutated. So for
stdin, we end up cloning twice. For non-stdin, we end up cloning once,
but unnecessarily (since the _contents_ don't get mutated, only the
kind).

This PR removes the `String` from `source_kind`, instead requiring that
we parse it out elsewhere. It reduces the number of clones down to 1 for
Jupyter Notebooks, and zero otherwise.
2023-08-18 09:32:18 -04:00
Micha Reiser 0cea4975fc
Rename Comments methods (#6649) 2023-08-18 06:37:01 +00:00
Charlie Marsh 3ceb6fbeb0
Remove some unnecessary ampersands in the formatter (#6667) 2023-08-18 04:18:26 +00:00
Charlie Marsh 8e18f8018f
Remove some trailing commas in write calls (#6666) 2023-08-18 00:14:44 -04:00
Charlie Marsh 8228429a70
Convert comment to rustdoc in placement.rs (#6665) 2023-08-18 04:11:38 +00:00
Charlie Marsh 1811312722
Improve `with` statement comment handling and expression breaking (#6621)
## Summary

The motivating code here was:

```python
with test as (
    # test
foo):
    pass
```

Which we were formatting as:

```python
with test as
# test
(foo):
    pass
```

`with` statements are oddly difficult. This PR makes a bunch of subtle
modifications and adds a more extensive test suite. For example, we now
only preserve parentheses if there's more than one `WithItem` _or_ a
trailing comma; before, we always preserved.

Our formatting is_not_ the same as Black, but here's a diff of our
formatted code vs. Black's for the `with.py` test suite. The primary
difference is that we tend to break parentheses when they contain
comments rather than move them to the end of the life (this is a
consistent difference that we make across the codebase):

```diff
diff --git a/crates/ruff_python_formatter/foo.py b/crates/ruff_python_formatter/foo.py
index 85e761080..31625c876 100644
--- a/crates/ruff_python_formatter/foo.py
+++ b/crates/ruff_python_formatter/foo.py
@@ -1,6 +1,4 @@
-with (
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-), aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
+with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
     ...
     # trailing
 
@@ -16,28 +14,33 @@ with (
     # trailing
 
 
-with a, b:  # a  # comma  # c  # colon
+with (
+    a,  # a  # comma
+    b,  # c
+):  # colon
     ...
 
 
 with (
-    a as  # a  # as
-    # own line
-    b,  # b  # comma
+    a as (  # a  # as
+        # own line
+        b
+    ),  # b  # comma
     c,  # c
 ):  # colon
     ...  # body
     # body trailing own
 
-with (
-    a as  # a  # as
+with a as (  # a  # as
     # own line
-    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb  # b
-):
+    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+):  # b
     pass
 
 
-with (a,):  # magic trailing comma
+with (
+    a,
+):  # magic trailing comma
     ...
 
 
@@ -47,6 +50,7 @@ with a:  # should remove brackets
 with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c:
     ...
 
+
 with (
     # leading comment
     a
@@ -74,8 +78,7 @@ with (
 with (
     a  # trailing same line comment
     # trailing own line comment
-    as b
-):
+) as b:
     ...
 
 with (
@@ -87,7 +90,9 @@ with (
 with (
     a
     # trailing own line comment
-) as b:  # trailing as same line comment  # trailing b same line comment
+) as (  # trailing as same line comment
+    b
+):  # trailing b same line comment
     ...
 
 with (
@@ -124,18 +129,24 @@ with (  # comment
     ...
 
 with (  # outer comment
-    CtxManager1() as example1,  # inner comment
+    (  # inner comment
+        CtxManager1()
+    ) as example1,
     CtxManager2() as example2,
     CtxManager3() as example3,
 ):
     ...
 
-with CtxManager() as example:  # outer comment
+with (  # outer comment
+    CtxManager()
+) as example:
     ...
 
 with (  # outer comment
     CtxManager()
-) as example, CtxManager2() as example2:  # inner comment
+) as example, (  # inner comment
+    CtxManager2()
+) as example2:
     ...
 
 with (  # outer comment
@@ -145,7 +156,9 @@ with (  # outer comment
     ...
 
 with (  # outer comment
-    (CtxManager1()),  # inner comment
+    (  # inner comment
+        CtxManager1()
+    ),
     CtxManager2(),
 ) as example:
     ...
@@ -179,7 +192,9 @@ with (
 ):
     pass
 
-with a as (b):  # foo
+with a as (  # foo
+    b
+):
     pass
 
 with f(
@@ -209,17 +224,13 @@ with f(
 ) as b, c as d:
     pass
 
-with (
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-) as b:
+with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b:
     pass
 
 with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b:
     pass
 
-with (
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-) as b, c as d:
+with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b, c as d:
     pass
 
 with (
@@ -230,6 +241,8 @@ with (
     pass
 
 with (
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-) as b, c as d:
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+    + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b,
+    c as d,
+):
     pass
```

Closes https://github.com/astral-sh/ruff/issues/6600.
## Test Plan

Before:

| project      | similarity index |
|--------------|------------------|
| cpython      | 0.75473          |
| django       | 0.99804          |
| transformers | 0.99618          |
| twine        | 0.99876          |
| typeshed     | 0.74292          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |

After:

| project      | similarity index |
|--------------|------------------|
| cpython      | 0.75473          |
| django       | 0.99804          |
| transformers | 0.99618          |
| twine        | 0.99876          |
| typeshed     | 0.74292          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |

`cargo test`
2023-08-18 03:30:38 +00:00
Charlie Marsh 26bba11be6
Manually format comments around `:=` in named expressions (#6634)
## Summary

Attaches comments around the `:=` operator in a named expression as
dangling, and formats them manually in the `named_expr.rs` formatter.

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

## Test Plan

`cargo test`
2023-08-18 03:10:45 +00:00
Shantanu a128fe5148
Apply RUF017 when start is passed via position (#6664)
As discussed in
https://github.com/astral-sh/ruff/pull/6489#discussion_r1297858919.
Linking https://github.com/astral-sh/ruff/issues/5073
2023-08-17 20:10:07 -04:00
Zanie Blue 5892c691ea
Bump version to 0.0.285 (#6660)
Requires
- https://github.com/astral-sh/ruff/pull/6655
- https://github.com/astral-sh/ruff/pull/6657
2023-08-17 15:46:28 -05:00
Zanie Blue 82e0a97b34
Clarify behavior of `PLW3201` (#6657)
Otherwise it is unclear that violations will be raised for methods like
`_foo_`
2023-08-17 14:41:55 -05:00
Charlie Marsh 1050142a58
Expand expressions to include parentheses in E712 (#6575)
## Summary

This PR exposes our `is_expression_parenthesized` logic such that we can
use it to expand expressions when autofixing to include their
parenthesized ranges.

This solution has a few drawbacks: (1) we need to compute parenthesized
ranges in more places, which also relies on backwards lexing; and (2) we
need to make use of this in any relevant fixes.

However, I still think it's worth pursuing. On (1), the implementation
is very contained, so IMO we can easily swap this out for a more
performant solution in the future if needed. On (2), this improves
correctness and fixes some bad syntax errors detected by fuzzing, which
means it has value even if it's not as robust as an _actual_
`ParenthesizedExpression` node in the AST itself.

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

## Test Plan

`cargo test` with new cases that previously failed the fuzzer.
2023-08-17 15:51:09 +00:00
Charlie Marsh db1c556508
Implement `Ranged` on more structs (#6639)
## Summary

I noticed some inconsistencies around uses of `.range.start()`, structs
that have a `TextRange` field but don't implement `Ranged`, etc.

## Test Plan

`cargo test`
2023-08-17 11:22:39 -04:00
Charlie Marsh a70807e1e1
Expand `NamedExpr` range to include full range of parenthesized value (#6632)
## Summary

Given:

```python
if (
    x
    :=
    (  # 4
        y # 5
    )  # 6
):
    pass
```

It turns out the parser ended the range of the `NamedExpr` at the end of
`y`, rather than the end of the parenthesis that encloses `y`. This just
seems like a bug -- the range should be from the start of the name on
the left, to the end of the parenthesized node on the right.

## Test Plan

`cargo test`
2023-08-17 14:34:05 +00:00
Zanie Blue d0f2a8e424
Add support for nested replacements inside format specifications (#6616)
Closes https://github.com/astral-sh/ruff/issues/6442

Python string formatting like `"hello {place}".format(place="world")`
supports format specifications for replaced content such as `"hello
{place:>10}".format(place="world")` which will align the text to the
right in a container filled up to ten characters.

Ruff parses formatted strings into `FormatPart`s each of which is either
a `Field` (content in `{...}`) or a `Literal` (the normal content).
Fields are parsed into name and format specifier sections (we'll ignore
conversion specifiers for now).

There are a myriad of specifiers that can be used in a `FormatSpec`.
Unfortunately for linters, the specifier values can be dynamically set.
For example, `"hello {place:{align}{width}}".format(place="world",
align=">", width=10)` and `"hello {place:{fmt}}".format(place="world",
fmt=">10")` will yield the same string as before but variables can be
used to determine the formatting. In this case, when parsing the format
specifier we can't know what _kind_ of specifier is being used as their
meaning is determined by both position and value.

Ruff does not support nested replacements and our current data model
does not support the concept. Here the data model is updated to support
this concept, although linting of specifications with replacements will
be inherently limited. We could split format specifications into two
types, one without any replacements that we can perform lints with and
one with replacements that we cannot inspect. However, it seems
excessive to drop all parsing of format specifiers due to the presence
of a replacement. Instead, I've opted to parse replacements eagerly and
ignore their possible effect on other format specifiers. This will allow
us to retain a simple interface for `FormatSpec` and most syntax checks.
We may need to add some handling to relax errors if a replacement was
seen previously.

It's worth noting that the nested replacement _can_ also include a
format specification although it may fail at runtime if you produce an
invalid outer format specification. For example, `"hello
{place:{fmt:<2}}".format(place="world", fmt=">10")` is valid so we need
to represent each nested replacement as a full `FormatPart`.

## Test plan

Adding unit tests for `FormatSpec` parsing and snapshots for PLE1300
2023-08-17 09:07:30 -05:00
Charlie Marsh 1334232168
Introduce `ExpressionRef` (#6637)
## Summary

This PR revives the `ExpressionRef` concept introduced in
https://github.com/astral-sh/ruff/pull/5644, motivated by the change we
want to make in https://github.com/astral-sh/ruff/pull/6575 to narrow
the type of the expression that can be passed to `parenthesized_range`.

## Test Plan

`cargo test`
2023-08-17 10:07:16 -04:00
Micha Reiser fa7442da2f
Support `fmt: skip` on compound statements (#6593) 2023-08-17 06:05:41 +00:00
Micha Reiser 4dc32a00d0
Support `fmt: skip` for simple-statements and decorators (#6561) 2023-08-17 05:58:19 +00:00
Evan Rittenhouse e3ecbe660e
[`ruff`] Implement `quadratic-list-summation` rule (`RUF017`) (#6489)
## Summary

Adds `RUF017`. Closes #5073 

## Test Plan

`cargo t`
2023-08-16 23:13:05 -04:00
Harutaka Kawamura 8c3a8c4fc6
Support glob patterns for `raises_require_match_for` and `raises_require_match_for` (#6635)
## Summary

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

Support glob patterns for `raises_require_match_for` and
`raises_require_match_for`. Resolve #6473

## Test Plan

New tests + existing tests
2023-08-17 02:15:50 +00:00
Charlie Marsh dcc7226685
Make lambda-assignment fix always-manual in class bodies (#6626)
## Summary

Related to https://github.com/astral-sh/ruff/issues/6620 (although that
will only be truly closed once we respect manual fixes on the CLI).
2023-08-16 21:24:48 -04:00
Charlie Marsh 036035bc50
Refactor literal-comparison and not-test rules (#6636)
## Summary

No behavior changes, but these need some refactoring to support
https://github.com/astral-sh/ruff/pull/6575 (namely, they need to take
the `ast::ExprCompare` or similar node instead of the attribute fields),
and I don't want to muddy that PR.

## Test Plan

`cargo test`
2023-08-17 01:02:30 +00:00
Charlie Marsh 97ae9e7433
Don't detect `pandas#values` for stores, deletes, or class accesses (#6631)
## Summary

Ensures we avoid cases like:

```python
x.values = 1
```

Since Pandas doesn't even expose a setter for that. We also avoid cases
like:

```python
print(self.values)
```

Since it's overwhelming likely to be a false positive.

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

## Test Plan

`cargo test`
2023-08-16 17:13:33 -04:00
Charlie Marsh 98b9f2e705
Respect .ipynb and .pyi sources when linting from stdin (#6628)
## Summary

When running Ruff from stdin, we were always falling back to the default
source type, even if the user specified a path (as is the case when
running from the LSP). This PR wires up the source type inference, which
means we now get the expected result when checking `.pyi` and `.ipynb`
files.

Closes #6627.

## Test Plan

Verified that `cat
crates/ruff/resources/test/fixtures/jupyter/valid.ipynb | cargo run -p
ruff_cli -- --force-exclude --no-cache --no-fix --isolated --select ALL
--stdin-filename foo.ipynb -` yielded the expected results (and differs
from the errors you get if you omit the filename).

Verified that `cat foo.pyi | cargo run -p ruff_cli -- --force-exclude
--no-cache --no-fix --format json --isolated --select TCH
--stdin-filename path/to/foo.pyi -` yielded no errors.
2023-08-16 20:33:59 +00:00
Zanie Blue 6253d8e2c8
Remove unused runtime string formatting logic (#6624)
In https://github.com/astral-sh/ruff/pull/6616 we are adding support for
nested replacements in format specifiers which makes actually formatting
strings infeasible without a great deal of complexity. Since we're not
using these functions (they just exist for runtime use in RustPython),
we can just remove them.
2023-08-16 17:38:33 +00:00
Micha Reiser fdbb2fbdba
Fix unreachable in playground (#6623) 2023-08-16 18:54:42 +02:00
Charlie Marsh d0b8e4f701
Update Black tests (#6618)
## Summary

Pulls in some tests that we previously couldn't support

## Test Plan

`cargo test`
2023-08-16 15:05:51 +00:00
Charlie Marsh 12f3c4c931
Fix comment formatting for yielded tuples (#6603)
## Summary
Closes https://github.com/astral-sh/ruff/issues/6384, although I think
the issue was fixed already on main, for the most part.

The linked issue is around formatting expressions like:

```python
def test():
    (
        yield 
        #comment 1
        * # comment 2
        # comment 3
        test # comment 4
    )

```

On main, prior to this PR, we now format like:

```python
def test():
    (
        yield (
            # comment 1
            # comment 2
            # comment 3
            *test
        )  # comment 4
    )
```

Which strikes me as reasonable. (We can't test this, since it's a syntax
error after for our parser, despite being a syntax error in both cases
from CPython's perspective.)

Meanwhile, Black does:

```python
def test():
    (
        yield
        # comment 1
        *  # comment 2
        # comment 3
        test  # comment 4
    )
```

So our formatting differs in that we move comments between the star and
the expression above the star.

As of this PR, we also support formatting this input, which is valid:

```python
def test():
    (
        yield 
        #comment 1
        * # comment 2
        # comment 3
        test, # comment 4
        1
    )
```

Like:

```python
def test():
    (
        yield (
            # comment 1
            (
                # comment 2
                # comment 3
                *test,  # comment 4
                1,
            )
        )
    )
```

There were two fixes here: (1) marking starred comments as dangling and
formatting them properly; and (2) supporting parenthesized comments for
tuples that don't contain their own parentheses, as is often the case
for yielded tuples (previously, we hit a debug assert).

Note that this diff

## Test Plan
cargo test
2023-08-16 13:41:07 +00:00
Micha Reiser 7ee2ae8395
Estimate expected `VecBuffer` size (#6612) 2023-08-16 15:31:31 +02:00
Charlie Marsh 95f78821ad
Fix parenthesized detection for tuples (#6599)
## Summary

This PR fixes our code for detecting whether a tuple has its own
parentheses, which is necessary when attempting to preserve parentheses.
As-is, we were getting some cases wrong, like `(a := 1), (b := 3))` --
the detection code inferred that this _was_ parenthesized, and so
wrapped the entire thing in an unnecessary set of parentheses.

## Test Plan

`cargo test`

Before:

| project      | similarity index |
|--------------|------------------|
| cpython      | 0.75472          |
| django       | 0.99804          |
| transformers | 0.99618          |
| twine        | 0.99876          |
| typeshed     | 0.74288          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |

After:
| project      | similarity index |
|--------------|------------------|
| cpython      | 0.75473          |
| django       | 0.99804 |
| transformers | 0.99618          |
| twine        | 0.99876          |
| typeshed     | 0.74288          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |
2023-08-16 13:20:48 +00:00
Micha Reiser daac31d2b9
Make `Buffer::write_element` non-failable (#6613) 2023-08-16 15:13:07 +02:00
Charlie Marsh 86ccdcc9d9
Add support for multi-character operator tokens to `SimpleTokenizer` (#6563)
## Summary

Allows for proper lexing of tokens like `->`.

The main challenge is to ensure that our forward and backwards
representations are the same for cases like `===`. Specifically, we want
that to lex as `==` followed by `=` regardless of whether it's a
forwards or backwards lex. To do so, we identify the range of the
sequential characters (the full span of `===`), lex it forwards, then
return the last token.

## Test Plan

`cargo test`
2023-08-16 09:09:19 -04:00
Micha Reiser e28858bb29
Fast path for ASCII only identifiers start (#6609) 2023-08-16 10:22:44 +02:00
Charlie Marsh 2d86e78bfc
Allow top-level `await` in Jupyter notebooks (#6607)
## Summary

Top-level `await` is allowed in Jupyter notebooks (see:
[autoawait](https://ipython.readthedocs.io/en/stable/interactive/autoawait.html)).

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

## Test Plan

Had to test this manually. Created a notebook, verified that the `yield`
was flagged but the `await` was not.

<img width="868" alt="Screen Shot 2023-08-15 at 11 40 19 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/b2853651-30a6-4dc6-851c-9fe7f694b8e8">
2023-08-15 23:59:05 -04:00
Harutaka Kawamura d9a81f4fbb
[`flake8-pytest-style`] Implement duplicate parameterized fixture detection (`PT014`) (#6598) 2023-08-16 03:35:46 +00:00
Micha Reiser 897cce83b3
Call pattern formatting (#6594) 2023-08-16 08:31:25 +05:30
Charlie Marsh 3f1658a25b
Remove pylint's duplicate_value.rs (#6604)
This was moved to bugbear, but we forgot to delete the file.
2023-08-16 00:10:24 +00:00
Zanie Blue 097db2fcce
Fix docs for `PLW1508` (#6602) 2023-08-15 15:29:29 -05:00
Charlie Marsh a3d4f08f29
Add general support for parenthesized comments on expressions (#6485)
## Summary

This PR adds support for parenthesized comments. A parenthesized comment
is a comment that appears within a parenthesis, but not within the range
of the expression enclosed by the parenthesis. For example, the comment
here is a parenthesized comment:

```python
if (
    # comment
    True
):
    ...
```

The parentheses enclose the `True`, but the range of `True` doesn’t
include the `# comment`.

There are at least two problems associated with parenthesized comments:
(1) associating the comment with the correct (i.e., enclosed) node; and
(2) formatting the comment correctly, once it has been associated with
the enclosed node.

The solution proposed here for (1) is to search for parentheses between
preceding and following node, and use open and close parentheses to
break ties, rather than always assigning to the preceding node.

For (2), we handle these special parenthesized comments in `FormatExpr`.
The biggest risk with this approach is that we forget some codepath that
force-disables parenthesization (by passing in `Parentheses::Never`).
I've audited all usages of that enum and added additional handling +
test coverage for such cases.

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

## Test Plan

`cargo test` with new cases.

Before:

| project      | similarity index |
|--------------|------------------|
| build        | 0.75623          |
| cpython      | 0.75472          |
| django       | 0.99804          |
| transformers | 0.99618          |
| typeshed     | 0.74233          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |

After:

| project      | similarity index |
|--------------|------------------|
| build        | 0.75623          |
| cpython      | 0.75472          |
| django       | 0.99804          |
| transformers | 0.99618          |
| typeshed     | 0.74237          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |
2023-08-15 18:59:18 +00:00
Micha Reiser 29c0b9f91c
Use single lookup for leading, dangling, and trailing comments (#6589) 2023-08-15 17:39:45 +02:00
Harutaka Kawamura 81b1176f99
Fix PT005 doc (#6596) 2023-08-15 12:48:44 +00:00
Charlie Marsh b1c4c7be69
Add trailing comma for single-element import-from groups (#6583)
## Summary

Unlike other statements, Black always adds a trailing comma if an
import-from statement breaks with a single import member. I believe this
is for compatibility with isort -- see
09f5ee3a19,
https://github.com/psf/black/issues/127, or
66648c528a/src/black/linegen.py (L1452)
for the current version.

## Test Plan

`cargo test`, notice that a big chunk of the compatibility suite is
removed.

Before:

| project      | similarity index |
|--------------|------------------|
| cpython      | 0.75472          |
| django       | 0.99804          |
| transformers | 0.99618          |
| twine        | 0.99876          |
| typeshed     | 0.74233          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |

After:

| project      | similarity index |
|--------------|------------------|
| cpython      | 0.75472          |
| django       | 0.99804          |
| transformers | 0.99618          |
| twine        | 0.99876          |
| typeshed     | 0.74260          |
| warehouse    | 0.99601          |
| zulip        | 0.99727          |
2023-08-15 07:15:33 -04:00
Tom Kuson 84d178a219
Use one line between top-level items if formatting a stub file (#6501)
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-08-15 09:33:57 +02:00
Micha Reiser 455db84a59
Replace `inline(always)` with `inline` (#6590) 2023-08-15 08:58:11 +02:00
Micha Reiser 232b44a8ca
Indent statements in suppressed ranges (#6507) 2023-08-15 08:00:35 +02:00
Harutaka Kawamura e1e213decf
Import `pytest` in `flake8-pytest-style` docs (#6580) 2023-08-14 23:08:15 -04:00
Charlie Marsh 17e7eae2f9
Avoid unused argument rules when functions call `locals()` (#6578)
Closes https://github.com/astral-sh/ruff/issues/6576.
2023-08-14 19:48:20 -04:00
Charlie Marsh 7f7df852e8
Remove some extraneous newlines in Cargo.toml (#6577) 2023-08-14 23:39:41 +00:00
Harutaka Kawamura ebda5fcd99
Add PT002 ~ PT005 docs (#6521) 2023-08-14 21:29:03 +00:00
Charlie Marsh b1870b2b16
Add deprecated unittest assertions to PT009 (#6572)
## Summary

This rule was missing `self.failIf` and friends.

## Test Plan

`cargo test`
2023-08-14 21:08:02 +00:00
Harutaka Kawamura a51d1ac980
Add `PT006` and `PT007` docs (#6531) 2023-08-14 17:03:42 -04:00
Evan Rittenhouse 1a52b548e7
Ignore PERF203 if `try` contains loop control flow statements (#6536) 2023-08-14 20:47:37 +00:00
Harutaka Kawamura 70696061cd
[`flake8-pytest-style`] Implement `pytest-unittest-raises-assertion` (`PT027`) (#6554) 2023-08-14 20:25:23 +00:00
Charlie Marsh cd634a9489
Expand documentation around flake8-type-checking rules for SQLAlchemy (#6570)
## Summary

Not addressing the root issue as much as improving the documentation.

Closes https://github.com/astral-sh/ruff/issues/6510.
2023-08-14 19:47:10 +00:00
Charlie Marsh 5ddf143cae
Clarify FBT documentation and refine rule names (#6567)
Closes https://github.com/astral-sh/ruff/issues/6530.
2023-08-14 15:24:16 -04:00
Charlie Marsh 46862473b9
Omit `NotImplementedError` from `TRY003` (#6568)
Closes https://github.com/astral-sh/ruff/issues/6528.
2023-08-14 18:24:44 +00:00
Charlie Marsh 96d310fbab
Remove `Stmt::TryStar` (#6566)
## Summary

Instead, we set an `is_star` flag on `Stmt::Try`. This is similar to the
pattern we've migrated towards for `Stmt::For` (removing
`Stmt::AsyncFor`) and friends. While these are significant differences
for an interpreter, we tend to handle these cases identically or nearly
identically.

## Test Plan

`cargo test`
2023-08-14 13:39:44 -04:00
Micha Reiser 09c8b17661
`fmt: off..on` suppression comments (#6477) 2023-08-14 15:57:36 +00:00
qdegraaf 278a4f6e14
Formatter: Fix posonlyargs for `expr_lambda` (#6562) 2023-08-14 17:38:56 +02:00
Charlie Marsh c3a9151eb5
Handle comments on open parentheses in with statements (#6515)
## Summary

This PR adds handling for comments on open parentheses in parenthesized
context managers. For example, given:

```python
with (  # comment
    CtxManager1() as example1,
    CtxManager2() as example2,
    CtxManager3() as example3,
):
    ...
```

We want to preserve that formatting. (Black does the same.) On `main`,
we format as:

```python
with (
    # comment
    CtxManager1() as example1,
    CtxManager2() as example2,
    CtxManager3() as example3,
):
    ...
```

It's very similar to how `StmtImportFrom` is handled.

Note that this case _isn't_ covered by the "parenthesized comment"
proposal, since this is a common on the statement that would typically
be attached to the first `WithItem`, and the `WithItem` _itself_ can
have parenthesized comments, like:

```python
with (  # comment
    (
        CtxManager1()  # comment
    ) as example1,
    CtxManager2() as example2,
    CtxManager3() as example3,
):
    ...
```

## Test Plan

`cargo test`

Confirmed no change in similarity score.
2023-08-14 15:11:03 +00:00