Commit Graph

1838 Commits

Author SHA1 Message Date
Micha Reiser 40ddc1604c
Introduce `parenthesized` helper (#5565) 2023-07-07 11:28:25 +02:00
Charlie Marsh bf4b96c5de
Differentiate between runtime and typing-time annotations (#5575)
## Summary

In Python, the annotations on `x` and `y` here have very different
treatment:

```python
def foo(x: int):
  y: int
```

The `int` in `x: int` is a runtime-required annotation, because `x` gets
added to the function's `__annotations__`. You'll notice, for example,
that this fails:

```python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
  from foo import Bar

def f(x: Bar):
  ...
```

Because `Bar` is required to be available at runtime, not just at typing
time. Meanwhile, this succeeds:

```python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
  from foo import Bar

def f():
  x: Bar = 1

f()
```

(Both cases are fine if you use `from __future__ import annotations`.)

Historically, we've tracked those annotations that are _not_
runtime-required via the semantic model's `ANNOTATION` flag. But
annotations that _are_ runtime-required have been treated as "type
definitions" that aren't annotations.

This causes problems for the flake8-future-annotations rules, which try
to detect whether adding `from __future__ import annotations` would
_allow_ you to rewrite a type annotation. We need to know whether we're
in _any_ type annotation, runtime-required or not, since adding `from
__future__ import annotations` will convert any runtime-required
annotation to a typing-only annotation.

This PR adds separate state to track these runtime-required annotations.
The changes in the test fixtures are correct -- these were false
negatives before.

Closes https://github.com/astral-sh/ruff/issues/5574.
2023-07-07 00:21:44 -04:00
Charlie Marsh b11492e940
Fix remaining Copyright rule references (#5577) 2023-07-07 02:49:19 +00:00
Tom Kuson 5908b39102
Support globbing in `isort` options (#5473)
## Summary

Support glob patterns in `isort` options.

Closes #5420.

## Test Plan

Added test.

`cargo test`
2023-07-06 20:37:41 -04:00
konsti 5e5a96ca28
Fix formatter `StmtTry` test (#5568)
For some reason this didn't turn up on CI before

CC @michareiser this is the fix for the error you had
2023-07-06 18:23:53 +00:00
Tom Kuson 3650aaa8b3
Add documentation to the `S1XX` rules (#5479)
## Summary

Add documentation to the `S1XX` rules (the `flake8-bandit` ['misc
tests'](https://bandit.readthedocs.io/en/latest/plugins/index.html#plugin-id-groupings)
rule group).

## Test Plan

`python scripts/check_docs_formatted.py && mkdocs serve`
2023-07-06 17:46:16 +00:00
Charlie Marsh cc822082a7
Refactor `noqa` directive parsing away from regex-based implementation (#5554)
## Summary

I'll write up a more detailed description tomorrow, but in short, this
PR removes our regex-based implementation in favor of "manual" parsing.

I tried a couple different implementations. In the benchmarks below:

- `Directive/Regex` is our implementation on `main`.
- `Directive/Find` just uses `text.find("noqa")`, which is insufficient,
since it doesn't cover case-insensitive variants like `NOQA`, and
doesn't handle multiple `noqa` matches in a single like, like ` # Here's
a noqa comment # noqa: F401`. But it's kind of a baseline.
- `Directive/Memchr` uses three `memchr` iterative finders (one for
`noqa`, `NOQA`, and `NoQA`).
- `Directive/AhoCorasick` is roughly the variant checked-in here.

The raw results:

```
Directive/Regex/# noqa: F401
                        time:   [273.69 ns 274.71 ns 276.03 ns]
                        change: [+1.4467% +1.8979% +2.4243%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 15 outliers among 100 measurements (15.00%)
  3 (3.00%) low mild
  8 (8.00%) high mild
  4 (4.00%) high severe
Directive/Find/# noqa: F401
                        time:   [66.972 ns 67.048 ns 67.132 ns]
                        change: [+2.8292% +2.9377% +3.0540%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 15 outliers among 100 measurements (15.00%)
  1 (1.00%) low severe
  3 (3.00%) low mild
  8 (8.00%) high mild
  3 (3.00%) high severe
Directive/AhoCorasick/# noqa: F401
                        time:   [76.922 ns 77.189 ns 77.536 ns]
                        change: [+0.4265% +0.6862% +0.9871%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 8 outliers among 100 measurements (8.00%)
  1 (1.00%) low mild
  3 (3.00%) high mild
  4 (4.00%) high severe
Directive/Memchr/# noqa: F401
                        time:   [62.627 ns 62.654 ns 62.679 ns]
                        change: [-0.1780% -0.0887% -0.0120%] (p = 0.03 < 0.05)
                        Change within noise threshold.
Found 11 outliers among 100 measurements (11.00%)
  1 (1.00%) low severe
  5 (5.00%) low mild
  3 (3.00%) high mild
  2 (2.00%) high severe
Directive/Regex/# noqa: F401, F841
                        time:   [321.83 ns 322.39 ns 322.93 ns]
                        change: [+8602.4% +8623.5% +8644.5%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  1 (1.00%) low severe
  2 (2.00%) low mild
  1 (1.00%) high mild
  1 (1.00%) high severe
Directive/Find/# noqa: F401, F841
                        time:   [78.618 ns 78.758 ns 78.896 ns]
                        change: [+1.6909% +1.8771% +2.0628%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 3 outliers among 100 measurements (3.00%)
  3 (3.00%) high mild
Directive/AhoCorasick/# noqa: F401, F841
                        time:   [87.739 ns 88.057 ns 88.468 ns]
                        change: [+0.1843% +0.4685% +0.7854%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 11 outliers among 100 measurements (11.00%)
  5 (5.00%) low mild
  3 (3.00%) high mild
  3 (3.00%) high severe
Directive/Memchr/# noqa: F401, F841
                        time:   [80.674 ns 80.774 ns 80.860 ns]
                        change: [-0.7343% -0.5633% -0.4031%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 14 outliers among 100 measurements (14.00%)
  4 (4.00%) low severe
  9 (9.00%) low mild
  1 (1.00%) high mild
Directive/Regex/# noqa  time:   [194.86 ns 195.93 ns 196.97 ns]
                        change: [+11973% +12039% +12103%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 6 outliers among 100 measurements (6.00%)
  5 (5.00%) low mild
  1 (1.00%) high mild
Directive/Find/# noqa   time:   [25.327 ns 25.354 ns 25.383 ns]
                        change: [+3.8524% +4.0267% +4.1845%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 9 outliers among 100 measurements (9.00%)
  6 (6.00%) high mild
  3 (3.00%) high severe
Directive/AhoCorasick/# noqa
                        time:   [34.267 ns 34.368 ns 34.481 ns]
                        change: [+0.5646% +0.8505% +1.1281%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 5 outliers among 100 measurements (5.00%)
  5 (5.00%) high mild
Directive/Memchr/# noqa time:   [21.770 ns 21.818 ns 21.874 ns]
                        change: [-0.0990% +0.1464% +0.4046%] (p = 0.26 > 0.05)
                        No change in performance detected.
Found 10 outliers among 100 measurements (10.00%)
  4 (4.00%) low mild
  4 (4.00%) high mild
  2 (2.00%) high severe
Directive/Regex/# type: ignore # noqa: E501
                        time:   [278.76 ns 279.69 ns 280.72 ns]
                        change: [+7449.4% +7469.8% +7490.5%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 3 outliers among 100 measurements (3.00%)
  1 (1.00%) low mild
  1 (1.00%) high mild
  1 (1.00%) high severe
Directive/Find/# type: ignore # noqa: E501
                        time:   [67.791 ns 67.976 ns 68.184 ns]
                        change: [+2.8321% +3.1735% +3.5418%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 6 outliers among 100 measurements (6.00%)
  5 (5.00%) high mild
  1 (1.00%) high severe
Directive/AhoCorasick/# type: ignore # noqa: E501
                        time:   [75.908 ns 76.055 ns 76.210 ns]
                        change: [+0.9269% +1.1427% +1.3955%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high severe
Directive/Memchr/# type: ignore # noqa: E501
                        time:   [72.549 ns 72.723 ns 72.957 ns]
                        change: [+1.5881% +1.9660% +2.3974%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 15 outliers among 100 measurements (15.00%)
  10 (10.00%) high mild
  5 (5.00%) high severe
Directive/Regex/# type: ignore # nosec
                        time:   [66.967 ns 67.075 ns 67.207 ns]
                        change: [+1713.0% +1715.8% +1718.9%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 10 outliers among 100 measurements (10.00%)
  1 (1.00%) low severe
  3 (3.00%) low mild
  2 (2.00%) high mild
  4 (4.00%) high severe
Directive/Find/# type: ignore # nosec
                        time:   [18.505 ns 18.548 ns 18.597 ns]
                        change: [+1.3520% +1.6976% +2.0333%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 4 outliers among 100 measurements (4.00%)
  4 (4.00%) high mild
Directive/AhoCorasick/# type: ignore # nosec
                        time:   [16.162 ns 16.206 ns 16.252 ns]
                        change: [+1.2919% +1.5587% +1.8430%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe
Directive/Memchr/# type: ignore # nosec
                        time:   [39.192 ns 39.233 ns 39.276 ns]
                        change: [+0.5164% +0.7456% +0.9790%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 13 outliers among 100 measurements (13.00%)
  2 (2.00%) low severe
  4 (4.00%) low mild
  3 (3.00%) high mild
  4 (4.00%) high severe
Directive/Regex/# some very long comment that # is interspersed with characters but # no directive
                        time:   [81.460 ns 81.578 ns 81.703 ns]
                        change: [+2093.3% +2098.8% +2104.2%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) low mild
  2 (2.00%) high mild
Directive/Find/# some very long comment that # is interspersed with characters but # no directive
                        time:   [26.284 ns 26.331 ns 26.387 ns]
                        change: [+0.7554% +1.1027% +1.3832%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 6 outliers among 100 measurements (6.00%)
  5 (5.00%) high mild
  1 (1.00%) high severe
Directive/AhoCorasick/# some very long comment that # is interspersed with characters but # no direc...
                        time:   [28.643 ns 28.714 ns 28.787 ns]
                        change: [+1.3774% +1.6780% +2.0028%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high mild
Directive/Memchr/# some very long comment that # is interspersed with characters but # no directive
                        time:   [55.766 ns 55.831 ns 55.897 ns]
                        change: [+1.5802% +1.7476% +1.9021%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) low mild
```

While memchr is faster than aho-corasick in some of the common cases
(like `# noqa: F401`), the latter is way, way faster when there _isn't_
a match (like 2x faster -- see the last two cases). Since most comments
_aren't_ `noqa` comments, this felt like the right tradeoff. Note that
all implementations are significantly faster than the regex version.

(I know I originally reported a 10x speedup, but I ended up improving
the regex version a bit in some prior PRs, so it got unintentionally
faster via some refactors.)

There's also one behavior change in here, which is that we now allow
variable spaces, e.g., `#noqa` or `# noqa`. Previously, we required
exactly one space. This thus closes #5177.
2023-07-06 16:03:10 +00:00
Charlie Marsh 9713ee4b80
Remove `ParsedFileExemption::None` (#5555)
## Summary

This is more aligned with the other enums in this module. Should've been
changed in a previous refactor, just an oversight.
2023-07-06 11:15:46 -04:00
konsti 8184235f93
Try statements have a body: Fix formatter instability (#5558)
## Summary

The following code was previously leading to unstable formatting:
```python
try:
    try:
        pass
    finally:
        print(1)  # issue7208
except A:
    pass
```
The comment would be formatted as a trailing comment of `try` which is
unstable as an end-of-line comment gets two extra whitespaces.

This was originally found in
99b00efd5e/Lib/getpass.py (L68-L91)

## Test Plan

I added a regression test
2023-07-06 16:07:47 +02:00
Charlie Marsh bf02c77fd7
Replace stat mapping with match statement (#5548) 2023-07-05 23:42:21 +00:00
Charlie Marsh ba7041b6bf
Remove Directive's dependency on Locator (#5547)
## Summary

It's a bit simpler to let the API just take the text itself, plus an
offset (to make the returned `TextRange` absolute, rather than
relative).
2023-07-05 23:33:57 +00:00
Charlie Marsh 5dff3195d4
Refactor tokens-based rules to take an `&mut Vec<Diagnostic>` (#5525) 2023-07-05 19:21:42 -04:00
Charlie Marsh 23363cafd1
Move `Directive` fields behind accessor methods (#5546) 2023-07-05 23:13:41 +00:00
Charlie Marsh e4596ebc35
Remove leading and trailing space length from `Directive` (#5545)
## Summary

We only need this in one place (when removing the directive), and it
simplifies a lot of details to just compute it there.
2023-07-05 23:03:06 +00:00
Charlie Marsh c9e02c52a8
Add separate configuration for MkDocs Insiders plugins (#5544)
## Summary

This PR adds a separate configuration file to enable us to turn on
[Insiders-only
plugins](https://squidfunk.github.io/mkdocs-material/insiders/getting-started/#built-in-plugins).

I've turned on the `typeset` plugin which ensures that the settings on
the left-hand navigation pane render as code:

<img width="1792" alt="Screen Shot 2023-07-05 at 6 27 20 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/c93676dd-bb48-417a-9d3b-528bf001e9b7">
2023-07-05 18:40:21 -04:00
Charlie Marsh d097b49371
Remove `Directive::None` variant (#5543)
## Summary

This is creating some weird, impossible states. Make impossible states
unrepresentable!
2023-07-05 22:22:21 +00:00
Charlie Marsh cdb9fda3b8
Add debug-based snapshot tests for noqa directive parsing (#5535)
## Summary

Better tests, helpful for future refactors.
2023-07-05 21:49:07 +00:00
Charlie Marsh a0c0b74b6d
Use structs for noqa `Directive` variants (#5533)
## Summary

No behavioral changes, just clearer (IMO) and with better documentation.
2023-07-05 21:37:32 +00:00
qdegraaf 6f548d9872
`[isort]` Add `--case-sensitive` flag (#5539)
## Summary

Adds a `--case-sensitive` setting/flag to isort (default: `false`)
which, when set to `true` sorts imports case sensitively instead of case
insensitively.

Tests and Docs can be improved, can do that if the general idea of the
implementation is in order.

First `isort` edit so any and all feedback is welcomed even more than
usual.

## Test Plan

Added a fixture with an assortment of imports in various cases.

## Issue links

Closes: https://github.com/astral-sh/ruff/issues/5514
2023-07-05 16:10:53 -04:00
Charlie Marsh 5a74a8e5a1
Avoid syntax errors when rewriting str(dict) in f-strings (#5538)
Closes https://github.com/astral-sh/ruff/issues/5530.
2023-07-05 19:22:22 +00:00
Charlie Marsh c5bfd1e877
Allow descriptor instantiations in dataclass fields (#5537)
## Summary

Per the Python documentation, dataclasses are allowed to instantiate
descriptors, like so:

```python
class IntConversionDescriptor:
  def __init__(self, *, default):
    self._default = default

  def __set_name__(self, owner, name):
    self._name = "_" + name

  def __get__(self, obj, type):
    if obj is None:
      return self._default

    return getattr(obj, self._name, self._default)

  def __set__(self, obj, value):
    setattr(obj, self._name, int(value))

@dataclass
class InventoryItem:
  quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)
```

Closes https://github.com/astral-sh/ruff/issues/4451.
2023-07-05 15:19:24 -04:00
Charlie Marsh 9e1039f823
Enable attribute lookups via semantic model (#5536)
## Summary

This PR enables us to resolve attribute accesses within files, at least
for static and class methods. For example, we can now detect that this
is a function access (and avoid a false-positive):

```python
class Class:
    @staticmethod
    def error():
        return ValueError("Something")


# OK
raise Class.error()
```

Closes #5487.

Closes #5416.
2023-07-05 15:19:14 -04:00
Tom Kuson 9478454b96
[`pylint`] Implement Pylint `typevar-double-variance` (`C0131`) (#5517)
## Summary

Implement Pylint `typevar-double-variance` (`C0131`) as
`type-bivariance` (`PLC0131`). Includes documentation. Related to #970.
Renamed the rule to be more clear (it's not immediately obvious what
'double' means, IMO).

The Pylint implementation checks only `TypeVar`, but this PR checks
`ParamSpec` as well.

## Test Plan

Added tests.

`cargo test`
2023-07-05 14:53:41 -04:00
Charlie Marsh 9a8e5f7877
Run `cargo update` (#5534)
```console
❯ cargo update
    Updating crates.io index
    Updating git repository `https://github.com/charliermarsh/LibCST`
    Updating git repository `https://github.com/astral-sh/RustPython-Parser.git`
    Updating git repository `https://github.com/youknowone/unicode_names2.git`
    Updating bitflags v2.3.2 -> v2.3.3
    Updating bstr v1.5.0 -> v1.6.0
    Updating clap v4.3.8 -> v4.3.11
    Updating clap_builder v4.3.8 -> v4.3.11
    Updating clap_complete v4.3.1 -> v4.3.2
    Updating colored v2.0.0 -> v2.0.4
    Removing hermit-abi v0.2.6
    Removing hermit-abi v0.3.1
      Adding hermit-abi v0.3.2
    Updating is-terminal v0.4.7 -> v0.4.8
    Updating itoa v1.0.6 -> v1.0.8
      Adding linux-raw-sys v0.4.3
    Updating num_cpus v1.15.0 -> v1.16.0
    Updating paste v1.0.12 -> v1.0.13
    Updating pin-project-lite v0.2.9 -> v0.2.10
    Updating quote v1.0.28 -> v1.0.29
    Updating regex v1.8.4 -> v1.9.0
    Updating regex-automata v0.1.10 -> v0.3.0
    Updating regex-syntax v0.7.2 -> v0.7.3
    Removing rustix v0.37.20
      Adding rustix v0.37.23
      Adding rustix v0.38.3
    Updating rustversion v1.0.12 -> v1.0.13
    Updating ryu v1.0.13 -> v1.0.14
    Updating serde v1.0.164 -> v1.0.166
    Updating serde_derive v1.0.164 -> v1.0.166
    Updating serde_json v1.0.99 -> v1.0.100
    Updating syn v2.0.22 -> v2.0.23
    Updating thiserror v1.0.40 -> v1.0.41
    Updating thiserror-impl v1.0.40 -> v1.0.41
    Updating unicode-ident v1.0.9 -> v1.0.10
    Updating uuid v1.3.4 -> v1.4.0
    Updating windows-targets v0.48.0 -> v0.48.1
```
2023-07-05 12:34:15 -04:00
Dhruv Manilawala 6fd71e6f53
Avoid triggering DTZ001-006 when using `.astimezone()` (#5524)
## Summary

Avoid triggering DTZ001-006 when using `.astimezone()`

## Test Plan

Added test cases to call `.astimezone()` on DTZ001-006

fixes: #5516
2023-07-05 00:18:59 -04:00
Charlie Marsh dd60a3865c
Avoid triggering `unnecessary-map` (`C417`) for late-bound lambdas (#5520)
Closes https://github.com/astral-sh/ruff/issues/5502.
2023-07-04 22:11:29 -04:00
Charlie Marsh 26a268a3ec
Refactor the `unnecessary-map` (`C417`) implementation (#5518)
## Summary

No behavioral changes. Just refactors + adding a test for a false
positive, which I'll fix in a downstream PR.
2023-07-04 20:25:54 -04:00
Charlie Marsh 324455f580
Bump version to 0.0.277 (#5515) 2023-07-04 17:31:32 -04:00
Charlie Marsh da1c320bfa
Add .ipynb_checkpoints, .pyenv, .pytest_cache, and .vscode to default excludes (#5513)
## Summary

VS Code extensions are
[recommended](https://code.visualstudio.com/docs/python/settings-reference#_linting-settings)
to exclude `.vscode` and `site-packages`. Black also now omits
`.vscode`, `.pytest_cache`, and `.ipynb_checkpoints` by default.
Omitting `.pyenv` is similar to omitting virtual environments, but
really only matters in the context of VS Code (see:
https://github.com/astral-sh/ruff/discussions/5509).

Closes: #5510.
2023-07-04 20:25:16 +00:00
Charlie Marsh 485d997d35
Tweak prefix match to use .all_rules() (#5512)
## Summary

No behavior change, but I think this is a little cleaner.
2023-07-04 20:02:57 +00:00
Aarni Koskela d7214e77e6
Add `ruff rule --all` subcommand (with JSON output) (#5059)
## Summary

This adds a `ruff rule --all` switch that prints out a human-readable
Markdown or a machine-readable JSON document of the lint rules known to
Ruff.

I needed a machine-readable document of the rules [for a
project](https://github.com/astral-sh/ruff/discussions/5078), and
figured it could be useful for other people – or tooling! – to be able
to interrogate Ruff about its arcane knowledge.

The JSON output is an array of the same objects printed by `ruff rule
--format=json`.

## Test Plan

I ran `ruff rule --all --format=json`. I think more might be needed, but
maybe a snapshot test is overkill?
2023-07-04 19:45:38 +00:00
Charlie Marsh 952c623102
Avoid returning first-match for rule prefixes (#5511)
Closes #5495, but there's a TODO here to improve this further. The
current `from_code` implementation feels really indirect.
2023-07-04 19:23:05 +00:00
Tom Kuson 0e67757edb
[`pylint`] Implement Pylint `typevar-name-mismatch` (`C0132`) (#5501)
## Summary

Implement Pylint `typevar-name-mismatch` (`C0132`) as
`type-param-name-mismatch` (`PLC0132`). Includes documentation. Related
to #970.

The Pylint implementation checks only `TypeVar`, but this PR checks
`TypeVarTuple`, `ParamSpec`, and `NewType` as well. This seems to better
represent the Pylint rule's [intended
behaviour](https://github.com/pylint-dev/pylint/issues/5224).

Full disclosure: I am not a fan of the translated name and think it
should probably be different.

## Test Plan

`cargo test`
2023-07-04 18:49:43 +00:00
Charlie Marsh c395e44bd7
Avoid PERF rules for iteration-dependent assignments (#5508)
## Summary

We need to avoid raising "rewrite as a comprehension" violations in
cases like:

```python
d = defaultdict(list)

for i in [1, 2, 3]:
    d[i].append(i**2)
```

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

Closes https://github.com/astral-sh/ruff/issues/5500.
2023-07-04 18:21:05 +00:00
Charlie Marsh 75da72bd7f
Update documentation to list double-quote preference first (#5507)
Closes https://github.com/astral-sh/ruff/issues/5496.
2023-07-04 18:06:01 +00:00
Charlie Marsh 521e6de2c8
Fix eval detection for suspicious-eval-usage (#5506)
Closes https://github.com/astral-sh/ruff/issues/5505.
2023-07-04 18:01:29 +00:00
Thomas de Zeeuw 0b963ddcfa
Add unreachable code rule (#5384)
Co-authored-by: Thomas de Zeeuw <thomas@astral.sh>
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-07-04 14:27:23 +00:00
konsti 937de121f3
check-formatter-stability: Remove newlines and add `--error-file` (#5491)
## Summary

This makes the output of `check-formatter-stability` more concise by
removing extraneous newlines. It also adds a `--error-file` option to
that script that allows creating a file with just the errors (without
the status messages) to share with others.

## Test Plan

I ran it over CPython and looked at the output. I then added the
`--error-file` option and looked at the contents of the file
2023-07-04 07:54:35 +00:00
konsti 787e2fd49d
Format import statements (#5493)
## Summary

Format import statements in all their variants. Specifically, this
implemented formatting `StmtImport`, `StmtImportFrom` and `Alias`.

## Test Plan

I added some custom snapshots, even though this has been covered well by
black's tests.
2023-07-04 07:07:20 +00:00
Aarni Koskela 6acc316d19
Turn Linters', etc. implicit `into_iter()`s into explicit `rules()` (#5436)
## Summary

As discussed on ~IRC~ Discord, this will make it easier for e.g. the
docs generation stuff to get all rules for a linter (using
`all_rules()`) instead of just non-nursery ones, and it also makes it
more Explicit Is Better Than Implicit to iterate over linter rules.

Grepping for `Item = Rule` reveals some remaining implicit
`IntoIterator`s that I didn't feel were necessarily in scope for this
(and honestly, iterating over a `RuleSet` makes sense).
2023-07-03 19:35:16 -04:00
konsti a647f31600
Don't add a magic trailing comma for a single entry (#5463)
## Summary

If a comma separated list has only one entry, black will respect the
magic trailing comma, but it will not add a new one.

The following code will remain as is:

```python
b1 = [
    aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa
]
b2 = [
    aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa,
]
b3 = [
    aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa,
    aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa
]
```

## Test Plan

This was first discovered in
7eeadc82c2/django/contrib/admin/checks.py (L674-L681),
which i've minimized into a call test.

I've added tests for the three cases (one entry + no comma, one entry +
comma, more than one entry) to the list tests.

The diffs from the black tests get smaller.
2023-07-03 21:48:44 +02:00
Charlie Marsh 3992c47c00
Bump version to 0.0.276 (#5488) 2023-07-03 18:02:49 +00:00
Charlie Marsh 8de5a3d29d
Allow `Final` assignments in stubs (#5490)
## Summary

This fixes one incompatibility with `flake8-pyi`, and gives us a clean
pass on `typeshed`.
2023-07-03 17:57:49 +00:00
Charlie Marsh ed1dd09d02
Refine some `perflint` rules (#5484)
## Summary

Removing some false positives based on running over `zulip`.

`PERF401` now also detects cases like:

```py
original = list(range(10000))
filtered = []
for i in original:
    filtered.append(i * i)
```

Previously, these were caught by the list-copy rule, but these too need
comprehensions.
2023-07-03 13:53:17 -04:00
Charlie Marsh ca497fabbd
Remove some `diagnostics.extend` calls (#5483)
## Summary

It's more efficient (and more idiomatic for us) to pass in the `Checker`
directly.
2023-07-03 16:47:23 +00:00
Charlie Marsh 00fbbe4223
Remove some additional manual iterator matches (#5482)
## Summary

I've done a few of these PRs, I thought I'd caught them all, but missed
this pattern.
2023-07-03 16:29:59 +00:00
Charlie Marsh dadad0e9ed
Remove some allocations in argument detection (#5481)
## Summary

Drive-by PR to remove some allocations around argument name matching.
2023-07-03 12:21:26 -04:00
Charlie Marsh d2450c25ab
Audit `remove_argument` usages to use end-of-function (#5480)
## Summary

This PR applies the fix in #5478 to a variety of other call-sites, and
fixes some other range hygienic stuff in the rules that were modified.
2023-07-03 12:21:01 -04:00
Harutaka Kawamura 1e4b88969c
Fix `unnecessary-encode-utf8` to fix `encode` on parenthesized strings correctly (#5478)
## Summary

Fixes #5477

## Test Plan

New test cases.
2023-07-03 10:11:09 -04:00
Louis Dispa dc072537e5
Fix python_formatter generate.py with rust path (#5475)
## Summary

This PR fix an issue with the `generate.py` file of the python
formatter.
Since https://github.com/astral-sh/ruff/pull/5369 the [node.rs
file](f51dc20497/crates/ruff_python_ast/src/node.rs)
used to generate the types now has `ast::` in the enum.

```rust
pub enum AnyNode {
   ModModule(ModModule),
   ModInteractive(ModInteractive),
   ModExpression(ModExpression),
   ModFunctionType(ModFunctionType),
   ...
```

And now:

```rust
pub enum AnyNode {
   ModModule(ast::ModModule),
   ModInteractive(ast::ModInteractive),
   ModExpression(ast::ModExpression),
   ModFunctionType(ast::ModFunctionType),
   ...
```

The python script was not parsing rust paths. This PR adds the
possibility to have it.

## Test Plan

This was tested locally.

### Script output

Before

```
['ast::ModModule),', 'ast::ModInteractive),', 'ast::ModExpression),', 'ast::ModFunctionType),', 'ast::StmtFunctionDef),', 'ast::StmtAsyncFunctionDef),', 'ast::StmtClassDef),', 'ast::StmtReturn),', 'ast::StmtDelete),', 'ast::StmtAssign),', 'ast::StmtAugAssign),', 'ast::StmtAnnAssign),', 'ast::StmtFor),', 'ast::StmtAsyncFor),', 'ast::StmtWhile),', 'ast::StmtIf),', 'ast::StmtWith),', 'ast::StmtAsyncWith),', 'ast::StmtMatch),', 'ast::StmtRaise),', 'ast::StmtTry),', 'ast::StmtTryStar),', 'ast::StmtAssert),', 'ast::StmtImport),', 'ast::StmtImportFrom),', 'ast::StmtGlobal),', 'ast::StmtNonlocal),', 'ast::StmtExpr),', 'ast::StmtPass),', 'ast::StmtBreak),', 'ast::StmtContinue),', 'ast::ExprBoolOp),', 'ast::ExprNamedExpr),', 'ast::ExprBinOp),', 'ast::ExprUnaryOp),', 'ast::ExprLambda),', 'ast::ExprIfExp),', 'ast::ExprDict),', 'ast::ExprSet),', 'ast::ExprListComp),', 'ast::ExprSetComp),', 'ast::ExprDictComp),', 'ast::ExprGeneratorExp),', 'ast::ExprAwait),', 'ast::ExprYield),', 'ast::ExprYieldFrom),', 'ast::ExprCompare),', 'ast::ExprCall),', 'ast::ExprFormattedValue),', 'ast::ExprJoinedStr),', 'ast::ExprConstant),', 'ast::ExprAttribute),', 'ast::ExprSubscript),', 'ast::ExprStarred),', 'ast::ExprName),', 'ast::ExprList),', 'ast::ExprTuple),', 'ast::ExprSlice),', 'ast::ExceptHandlerExceptHandler),', 'ast::PatternMatchValue),', 'ast::PatternMatchSingleton),', 'ast::PatternMatchSequence),', 'ast::PatternMatchMapping),', 'ast::PatternMatchClass),', 'ast::PatternMatchStar),', 'ast::PatternMatchAs),', 'ast::PatternMatchOr),', 'ast::TypeIgnoreTypeIgnore),', 'Comprehension),', 'Arguments),', 'Arg),', 'ArgWithDefault),', 'Keyword),', 'Alias),', 'WithItem),', 'MatchCase),', 'Decorator),']

error: unexpected closing delimiter: `)`
 --> <stdin>:3:55
  |
2 |             use ruff_formatter::{write, Buffer, FormatResult};
  |                                 - this opening brace...     - ...matches this closing brace
3 |             use rustpython_parser::ast::ast::ModModule),;
  |                                                       ^ unexpected closing delimiter

Traceback (most recent call last):
  File "/Users/ldispa/Documents/perso/ruff/crates/ruff_python_formatter/generate.py", line 100, in <module>
    node_path.write_text(rustfmt(code))
                         ^^^^^^^^^^^^^
  File "/Users/ldispa/Documents/perso/ruff/crates/ruff_python_formatter/generate.py", line 12, in rustfmt
    return check_output(["rustfmt", "--emit=stdout"], input=code, text=True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/subprocess.py", line 466, in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/subprocess.py", line 571, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['rustfmt', '--emit=stdout']' returned non-zero exit status 1.
```

After:
```
['ModModule', 'ModInteractive', 'ModExpression', 'ModFunctionType', 'StmtFunctionDef', 'StmtAsyncFunctionDef', 'StmtClassDef', 'StmtReturn', 'StmtDelete', 'StmtAssign', 'StmtAugAssign', 'StmtAnnAssign', 'StmtFor', 'StmtAsyncFor', 'StmtWhile', 'StmtIf', 'StmtWith', 'StmtAsyncWith', 'StmtMatch', 'StmtRaise', 'StmtTry', 'StmtTryStar', 'StmtAssert', 'StmtImport', 'StmtImportFrom', 'StmtGlobal', 'StmtNonlocal', 'StmtExpr', 'StmtPass', 'StmtBreak', 'StmtContinue', 'ExprBoolOp', 'ExprNamedExpr', 'ExprBinOp', 'ExprUnaryOp', 'ExprLambda', 'ExprIfExp', 'ExprDict', 'ExprSet', 'ExprListComp', 'ExprSetComp', 'ExprDictComp', 'ExprGeneratorExp', 'ExprAwait', 'ExprYield', 'ExprYieldFrom', 'ExprCompare', 'ExprCall', 'ExprFormattedValue', 'ExprJoinedStr', 'ExprConstant', 'ExprAttribute', 'ExprSubscript', 'ExprStarred', 'ExprName', 'ExprList', 'ExprTuple', 'ExprSlice', 'ExceptHandlerExceptHandler', 'PatternMatchValue', 'PatternMatchSingleton', 'PatternMatchSequence', 'PatternMatchMapping', 'PatternMatchClass', 'PatternMatchStar', 'PatternMatchAs', 'PatternMatchOr', 'TypeIgnoreTypeIgnore', 'Comprehension', 'Arguments', 'Arg', 'ArgWithDefault', 'Keyword', 'Alias', 'WithItem', 'MatchCase', 'Decorator']
```
2023-07-03 16:07:57 +02:00
konsti 7ac9e0252e
Document Checking formatter stability and panics (#5415)
This adds the documentation, but ideally we should add the CI first
2023-07-03 11:22:19 +02:00
konsti ca6ff72404
Change generator formatting dummy to include NOT_YET_IMPLEMENTED (#5464)
## Summary

Change generator formatting dummy to include `NOT_YET_IMPLEMENTED`. This
makes it easier to correctly identify them as dummies

## Test Plan

This is a dummy change
2023-07-03 09:11:14 +02:00
Charlie Marsh 94ac2c4e1b
Reorganize some `flake8-pyi` rules (#5472) 2023-07-03 04:39:22 +00:00
qdegraaf 93b2bd7184
[`perflint`] Add `PERF401` and `PERF402` rules (#5298)
## Summary

Adds `PERF401` and `PERF402` mirroring `W8401` and `W8402` from
https://github.com/tonybaloney/perflint

Implementation is not super smart but should be at parity with upstream
implementation judging by:
c07391c176/perflint/comprehension_checker.py (L42-L73)

It essentially checks:

- If the body of a for-loop is just one statement
- If that statement is an `if` and the if-statement contains a call to
`append()` we flag `PERF401` and suggest a list comprehension
- If that statement is a plain call to `append()` or `insert()` we flag
`PERF402` and suggest `list()` or `list.copy()`

I've set the violation to only flag the first append call in a long
`if-else` statement for `PERF401`. Happy to change this to some other
location or make it multiple violations if that makes more sense.

## Test Plan

Fixtures were added with the relevant scenarios for both rules

## Issue Links

Refers: https://github.com/astral-sh/ruff/issues/4789
2023-07-03 04:03:09 +00:00
Justin Prieto 0bff4ed4d3
[`flake8-pyi`] Implement PYI002, PYI003, PYI004, PYI005 (#5457)
## Summary

Implements flake8-pyi checks 002, 003, 004, 005. The logic is a bit
complex, as you can see in the [original
code](57921813c1/pyi.py (L1403C18-L1403C18)).

ref: #848 

## Test Plan

Updated snapshot tests. Ran flake8 to double check lints, and ran ruff
with all PYI lints enabled to check for incorrect overlapping lint
errors.
2023-07-02 23:52:16 -04:00
Anders Kaseorg df13e69c3c
Format let-else with rustfmt nightly (#5461)
Support for `let…else` formatting was just merged to nightly
(rust-lang/rust#113225). Rerun `cargo fmt` with Rust nightly 2023-07-02
to pick this up. Followup to #939.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2023-07-03 02:13:35 +00:00
Charlie Marsh c8b9a46e2b
[`pyupgrade`] Restore the `keep-runtime-typing` setting (#5470)
## Summary

This PR reverts #4427. See the included documentation for a detailed
explanation.

Closes #5434.
2023-07-03 02:11:31 +00:00
Charlie Marsh 6cc04d64e4
[`flake8-django`] Skip duplicate violations in `DJ012` (#5469)
## Summary

This PR reduces the noise from `DJ012` by emitting a single violation
when you have multiple consecutive violations of the same "type".

For example, given:

```py
class MultipleConsecutiveFields(models.Model):
    """Model that contains multiple out-of-order field definitions in a row."""


    class Meta:
        verbose_name = "test"

    first_name = models.CharField(max_length=32)
    last_name = models.CharField(max_length=32)
```

It's convenient to only error on `first_name`, and not `last_name`,
since we're really flagging that the _section_ is out-of-order.

Closes #5465.
2023-07-02 21:09:49 -04:00
Charlie Marsh d0b2fffb87
[`numpy`] Add `numpy-deprecated-function` (NPY003) (#5468)
## Summary

Closes #5456.
2023-07-02 20:50:14 -04:00
Charlie Marsh b32d1e8d78
Detect consecutive, non-newline-delimited NumPy sections (#5467)
## Summary

Given a docstring like:

```py
def f(a: int, b: int) -> int:
    """Showcase function.
    Parameters
    ----------
    a : int
        _description_
    b : int
        _description_
    Returns
    -------
    int
        _description
    """
```

We were failing to identify `Returns` as a section, because the previous
line was neither empty nor ended with punctuation. This was causing a
false negative, where by we weren't flagging a missing line before
`Returns`. So, the very reason for the rule (no blank line) was causing
us to fail to catch it.

Note that, we did have a test case for this, which was working properly:

```py
def f() -> int:
    """Showcase function.
    Parameters
    ----------
    Returns
    -------
    """
```

...because the line before `Returns` "ends in a punctuation mark" (`-`).

Closes #5442.
2023-07-02 20:29:45 -04:00
Charlie Marsh af7051b976
Include BaseException in B017 rule (#5466)
Closes #5462.
2023-07-02 20:18:33 -04:00
Micha Reiser f0ec9ecd67
Show `BestFitting` mode if it isn't `FirstLine` (#5452) 2023-06-30 09:49:00 +00:00
Micha Reiser f9129e435a
Normalize '\r' in string literals to '\n'
<!--
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

This PR normalizes line endings inside of strings to `\n` as required by the printer.

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

## Test Plan

I added a new test using `\r\n` and ran the ecosystem check. There are no remaining end of line panics. 


https://gist.github.com/MichaReiser/8f36b1391ca7b48475b3a4f592d74ff4

<!-- How was it tested? -->
2023-06-30 10:13:23 +02:00
Micha Reiser dc65007fe9
Use rayon to parallelize the stability check
<!--
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

This PR uses rayon to parallelize the stability check by scheduling each project as its own task.

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

## Test Plan

I ran the ecosystem check. It now makes use of all cores (except at the end, there are some large projects). 

## Performance

The check now completes in minutes where it took about 30 minutes before.

<!-- How was it tested? -->
2023-06-30 10:05:25 +02:00
Micha Reiser 9c2a75284b
Preserve parentheses around left side of binary expression
<!--
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

This PR fixes an issue where the binary expression formatting removed parentheses around the left hand side of an expression.

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

## Test Plan

I added a new regression test and re-ran the ecosystem check. It brings down the `check-formatter-stability` output from a 3.4MB file down to 900KB. 

<!-- How was it tested? -->
2023-06-30 09:52:14 +02:00
Micha Reiser ae25638b0b
Update Black tests (#5438) 2023-06-30 06:32:50 +00:00
Micha Reiser 955e9ef821
Fix invalid syntax for binary expression in unary op (#5370) 2023-06-29 08:09:26 +02:00
Micha Reiser 38189ed913
Fix invalid printer IR error (#5422) 2023-06-29 08:09:13 +02:00
David Szotten ca5e10b5ea
format StmtTryStar (#5418) 2023-06-29 08:07:33 +02:00
Charlie Marsh a973019358
Rewrite a variety of `.contains()` calls as `matches!` statements (#5432)
## Summary

These have the potential to be much more efficient, as we've seen in the
past.
2023-06-28 22:42:27 -04:00
Charlie Marsh aa887d5a1d
Use "manual" fixability for E731 in shadowed context (#5430)
## Summary

This PR makes E731 a "manual" fix in one other context: when the lambda
is shadowing another variable in the scope. Function declarations (with
shadowing) cause issues for type checkers, and so rewriting an
annotation, e.g., in branches of an `if` statement can lead to failures.

Closes https://github.com/astral-sh/ruff/issues/5421.
2023-06-28 22:00:06 -04:00
Charlie Marsh 72f7f11bac
Use `matches!` for reserved attribute lookup (#5431) 2023-06-29 01:52:11 +00:00
Tom Kuson 5aa2a90e17
Add documentation to `flake8-logging-format` rules (#5417)
## Summary

Completes the documentation for the `flake8-logging-format` rules.
Related to #2646.

I included both the `flake8-logging-format` recommendation to use the
`extra` keyword and the Pylint recommendation to pass format values as
parameters so that formatting is done lazily, as #970 suggests the
Pylint logging rules are covered by this ruleset. Using lazy formatting
via parameters is probably more common than avoiding formatting entirely
in favour of the `extra` argument, regardless.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-29 01:30:11 +00:00
Charlie Marsh 0e89c94947
Run shadowed-variable analyses in deferred handlers (#5181)
## Summary

This PR extracts a bunch of complex logic from `add_binding`, instead
running the the shadowing rules in the deferred handler, thereby
decoupling the binding phase (during which we build up the semantic
model) from the analysis phase, and generally making `add_binding` much
more focused.

This was made possible by improving the semantic model to better handle
deletions -- previously, we'd "lose track" of bindings if they were
deleted, which made this kind of refactor impossible.

## Test Plan

We have good automated coverage for this, but I want to benchmark it
separately.
2023-06-29 00:08:18 +00:00
Charlie Marsh c5e20505f8
Remove an unsafe access in the resolver (#5428) 2023-06-28 19:08:10 +00:00
Charlie Marsh 69c4b7fa11
Add dedicated `struct` for implicit imports (#5427)
## Summary

This was some feedback on a prior PR that I decided to act on
separately.
2023-06-28 18:55:43 +00:00
Charlie Marsh 0e12eb3071
Add a snapshot test for native module resolution (#5423) 2023-06-28 18:16:39 +00:00
Charlie Marsh 864f50a3a4
Remove all `unwrap` calls from the resolver (#5426) 2023-06-28 18:06:17 +00:00
Charlie Marsh 4d90a5a9bc
Move resolver tests out to top-level (#5424)
## Summary

These are really tests for the entire crate.
2023-06-28 13:25:37 -04:00
Charlie Marsh 1d2d015bc5
Make standard input detection robust to invalid arguments (#5393)
## Summary

This PR fixes a silent failure that manifested itself in
https://github.com/astral-sh/ruff-vscode/issues/238. In short, if the
user provided invalid arguments to Ruff in the VS Code extension (like
`"ruff.args": ["a"]`), then we generated something like the following
command:

```console
/path/to/ruff --force-exclude --no-cache --no-fix --format json - --fix a --stdin-filename /path/to/file.py
```

Since this contains both `-` and `a` as the "input files", Ruff would
treat this as if we're linting the files names `-` and `a`, rather than
linting standard input.

This PR modifies out standard input detection to force standard input
when `--stdin-filename` is present, or at least one file is `-`. (We
then warn and ignore the others.)
2023-06-28 14:52:23 +00:00
Charlie Marsh ea7bb199bc
Fill-in missing implementation for `is_native_module_file_name` (#5410)
## Summary

This was just an oversight -- the last remaining `todo!()` that I never
filled in. We clearly don't have any test coverage for it yet, but this
mimics the Pyright implementation.
2023-06-28 14:50:54 +00:00
Charlie Marsh 979049b2a6
Make lib iteration platform-specific (#5406) 2023-06-28 13:52:20 +00:00
Charlie Marsh 6587fb844a
Add snapshot tests for resolver (#5404)
## Summary

This PR adds some snapshot tests for the resolver based on executing
resolutions within a "mock" of the Airflow repo (that is: a folder that
contains a subset of the repo's files, but all empty, and with an
only-partially-complete virtual environment). It's intended to act as a
lightweight integration test, to enable us to test resolutions on a
"real" project without adding a dependency on Airflow itself.
2023-06-28 13:38:51 +00:00
Dhruv Manilawala a68a86e18b
fixup! Consider Jupyter index for code frames (`--show-source`) (#5402) (#5414) 2023-06-28 10:25:05 +00:00
Christian Clauss b42d76494c
types.rs: fnmatch url should point to current Python docs (#5413)
Like #5412
2023-06-28 15:54:13 +05:30
David Szotten c7adb9117f
format StmtAsyncWith (#5376)
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-06-28 10:21:44 +00:00
David Szotten 1979103ec0
Format `StmtTry` (#5222)
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-06-28 10:02:15 +00:00
Christian Clauss 9e2fd0c620
ruff rule SLOT uses URL to current Python docs (#5412)
<!--
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? -->
Currently the URL at the bottom of the `ruff rule SLOT00x` output points
to Python 3.7 docs.
Given that Python 3.7 is now end-of-life (as of yesterday), let's
instead point users to the current Python docs.

## Test Plan

<!-- How was it tested? -->
2023-06-28 09:48:52 +00:00
Charlie Marsh 366edc5a3f
Fix string annotation in docs (#5411) 2023-06-28 03:29:56 +00:00
Dhruv Manilawala 2aecaf5060
Consider Jupyter index for code frames (`--show-source`) (#5402)
## Summary

Consider Jupyter index for code frames (`--show-source`).

This solves two problems as mentioned in the linked issue:

> Omit any contents from adjoining cells

If the Jupyter index is present, we'll use that to check if the
surrounding
lines belong to the same cell as the content line. If not, we'll skip
that line
until we either reach the one which does or we reach the content line.

> code frame line number

If the Jupyter index is present, we'll use that to get the actual start
line in
corresponding to the computed start index.

## Test Plan

`cargo run --bin ruff -- check --no-cache --isolated --select=ALL --show-source /path/to/notebook.ipynb`

fixes: #5395
2023-06-28 08:54:51 +05:30
Marti Raudsepp 2c99b268c6
Exclude docstrings from PYI053 (#5405)
## Summary

The `Y053` rule of `flake8-pyi` ignores docstrings, it only triggers on
other string literals.

The separate `Y021/PYI021` rule exists to disallow docstrings.

## Test Plan

Added some `# OK` test cases to `PYI053.py(i)` files.
2023-06-28 00:19:20 +00:00
Charlie Marsh 56f73de0cb
Misc. clean-up for import resolver (#5401)
## Summary

Renaming functions, adding documentation, refactoring the test
infrastructure a bit.
2023-06-27 19:27:12 +00:00
Tom Kuson a0a93a636f
Implement Pylint `single-string-used-for-slots` (`C0205`) as `single-string-slots` (`PLC0205`) (#5399)
## Summary

Implement Pylint rule `single-string-used-for-slots` (`C0205`) as
`single-string-slots` (`PLC0205`). This rule checks for single strings
being assigned to `__slots__`. For example

```python
class Foo:
    __slots__: str = "bar"

    def __init__(self, bar: str) -> None:
        self.bar = bar
```

should be

```python
class Foo:
    __slots__: tuple[str, ...] = ("bar",)

    def __init__(self, bar: str) -> None:
        self.bar = bar
```

Related to #970. Includes documentation.

## Test Plan

`cargo test`
2023-06-27 18:33:58 +00:00
Tom Kuson 035f8993f4
Complete documentation for `pydocstyle` rules (#5387)
## Summary

Completes the documentation for the `pydocstyle` ruleset. Related to
#2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-27 18:12:21 +00:00
Charlie Marsh 032b967b05
Enable --watch for Jupyter notebooks (#5394)
## Summary

The list of extensions that support watching is hard-coded
(unfortunately); this PR adds `.ipynb` to the list.
2023-06-27 12:53:47 -04:00
Dhruv Manilawala 962479d943
Replace same length equal line with dash line in D407 (#5383)
## Summary

Replace same length equal line with dash line in D407

Do we want to update the message and autofix title to reflect this
change?

## Test Plan

Added test cases for:
- Equal line length == dash line length
- Equal line length != dash line length

fixes: #5378
2023-06-27 16:50:20 +00:00
Evan Rittenhouse ff0d0ab7a0
Add applicability to pydocstyle (#5390) 2023-06-27 12:40:19 -04:00
Evan Rittenhouse 0585e14d3b
Add applicability to flake8_pytest_style (#5389) 2023-06-27 12:39:56 -04:00
Charlie Marsh 1ed227a1e0
Port Pyright's import resolver to Rust (#5381)
## Summary

This PR contains the first step towards enabling robust first-party,
third-party, and standard library import resolution in Ruff (including
support for `typeshed`, stub files, native modules, etc.) by porting
Pyright's import resolver to Rust.

The strategy taken here was to start with a more-or-less direct port of
the Pyright's TypeScript resolver. The code is intentionally similar,
and the test suite is effectively a superset of Pyright's test suite for
its own resolver. Due to the nature of the port, the code is very, very
non-idiomatic for Rust. The code is also entirely unused outside of the
test suite, and no effort has been made to integrate it with the rest of
the codebase.

Future work will include:

- Refactoring the code (now that it works) to match Rust and Ruff
idioms.
- Further testing, in practice, to ensure that the resolver can resolve
imports in a complex project, when provided with a virtual environment
path.
- Caching, to minimize filesystem lookups and redundant resolutions.
- Integration into Ruff itself (use Ruff's existing settings, find rules
that can make use of robust resolution, etc.)
2023-06-27 16:15:07 +00:00
Charlie Marsh 502e15585d
Ignore unpacking in `iteration-over-set` (#5392)
Closes #5386.
2023-06-27 15:33:42 +00:00
konstin 520f4f33c3
Fix ruff_dev repeat by removing short argument (#5388)
ruff_dev repeat recently broke (i think with the cargo update?):

> thread 'main' panicked at 'Command repeat: Short option names must be
unique for each argument, but '-n' is in use by both 'no_cache' and
'repeat''

This fixes this by removing the short argument.
2023-06-27 13:29:20 +00:00
konstin 7f6cb9dfb5
Format call expressions (without call chaining) (#5341)
## Summary

This formats call expressions with magic trailing comma and parentheses
behaviour but without call chaining

## Test Plan

Lots of new test fixtures, including some that don't work yet
2023-06-27 09:29:40 +00:00
David Szotten 50a7769d69
magic trailing comma for ExprList (#5365) 2023-06-26 21:59:01 +02:00
Evan Rittenhouse 190bed124f
[`perflint`] Implement `try-except-in-loop` (`PERF203`) (#5166)
## Summary

Implements PERF203 from #4789, which throws if a `try/except` block is
inside of a loop. Not sure if we want to extend the diagnostic to the
`except` as well, but I thought that that may get a little messy. We may
also want to just throw on the word `try` - open to suggestions though.

## Test Plan
`cargo test`
2023-06-26 17:34:37 +00:00
Charlie Marsh d53b986fd4
Fix autofix capabilities in playground (#5375)
## Summary

These had just bitrotted over time -- we were no longer passing along
the row-and-column indices, etc.

## Test Plan

![Screen Shot 2023-06-26 at 12 03 41
PM](https://github.com/astral-sh/ruff/assets/1309177/6791330d-010b-45d3-91ef-531d4745193f)
2023-06-26 16:40:28 +00:00
Charlie Marsh 8a1bb7a5af
Fix version number in playground (#5372)
## Summary

`v0.0.275` in the top-right was showing `v0.0.0` at all times.

## Test Plan

![Screen Shot 2023-06-26 at 11 31 16
AM](https://github.com/astral-sh/ruff/assets/1309177/e6cd0e19-6a5f-4b46-a060-54f492524737)
2023-06-26 15:56:12 +00:00
Dhruv Manilawala 2fc38d81e6
Experimental release for Jupyter notebook integration (#5363)
## Summary

Experimental release for Jupyter Notebook integration.

Currently, this requires a user to explicitly opt-in using the
[include](https://beta.ruff.rs/docs/settings/#include) configuration:

```toml
[tool.ruff]
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
```

Or, a user can pass in the file directly:

```sh
ruff check path/to/notebook.ipynb
```

For known limitations, please refer #5188 

## Test Plan

Following command should work without the `--all-features` flag:

```sh
cargo dev round-trip /path/to/notebook.ipynb
```

Following command should work with the above config file along with
`select = ["ALL"]`:

```sh
cargo run --bin ruff -- check --no-cache --config=../test-repos/openai-cookbook/pyproject.toml --fix ../test-repos/openai-cookbook/
```

Passing the Jupyter notebook directly:

```sh
cargo run --bin ruff -- check --no-cache --isolated --select=ALL --fix ../test-repos/openai-cookbook/examples/Classification_using_embeddings.ipynb
```
2023-06-26 21:22:42 +05:30
Charlie Marsh fa1b85b3da
Remove prelude from `ruff_python_ast` (#5369)
## Summary

Per @MichaReiser, this is causing more confusion than it is helpful.
2023-06-26 11:43:49 -04:00
Tom Kuson baa7264ca4
Add documentation for `flake8-2020` (#5366)
## Summary

Completes the documentation for the `flake8-2020` ruleset. Related to
#2646 .

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-26 15:24:42 +00:00
Tom Kuson fde3f09370
Add documentation missing docstring rules (`D1XX`) (#5330)
## Summary

Add documentation to the `D1XX` rules that flag missing docstrings. 

The examples are quite long and docstrings practices vary a lot between
projects, so I thought it would be best that the documentation for these
rules be their own PR separate to the other `pydocstyle` rules.

Related to #2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-26 14:44:46 +00:00
David Szotten d00559e42a
format StmtWith (#5350) 2023-06-26 15:09:06 +01:00
Micha Reiser 49cabca3e7
Format implicit string continuation (#5328) 2023-06-26 12:41:47 +00:00
Micha Reiser 313711aaf9
Prefer the configured quote style
<!--
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

This PR extends the string formatting to respect the configured quote style.

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

## Test Plan

Extended the string test with new cases and set it up to run twice: Once with the `quote_style: Doube`, and once with `quote_style: Single` single and double quotes. 

<!-- How was it tested? -->
2023-06-26 14:24:25 +02:00
Micha Reiser f18a1f70de
Add tests for skip magic trailing comma
<!--
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

This PR adds tests that verify that the magic trailing comma is not respected if disabled in the formatter options. 

Our test setup now allows to create a `<fixture-name>.options.json` file that contains an array of configurations that should be tested. 

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

## Test Plan

It's all about tests :) 

<!-- How was it tested? -->
2023-06-26 14:15:55 +02:00
Micha Reiser dd0d1afb66
Create `PyFormatOptions`
<!--
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

This PR adds a new `PyFormatOptions` struct that stores the python formatter options. 
The new options aren't used yet, with the exception of magical trailing commas and the options passed to the printer. 
I'll follow up with more PRs that use the new options (e.g. `QuoteStyle`).

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

## Test Plan

`cargo test` I'll follow up with a new PR that adds support for overriding the options in our fixture tests.
2023-06-26 14:02:17 +02:00
konstin a52cd47c7f
Fix attribute chain own line comments (#5340)
## Motation

Previously,
```python
x = (
    a1
    .a2
    # a
    .  # b
    # c
    a3
)
```
got formatted as
```python
x = a1.a2
# a
.  # b
# c
a3
```
which is invalid syntax. This fixes that.

## Summary

This implements a basic form of attribute chaining
(<https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#call-chains>)
by checking if any inner attribute access contains an own line comment,
and if this is the case, adds parentheses around the outermost attribute
access while disabling parentheses for all inner attribute expressions.
We want to replace this with an implementation that uses recursion or a
stack while formatting instead of in `needs_parentheses` and also
includes calls rather sooner than later, but i'm fixing this now because
i'm uncomfortable with having known invalid syntax generation in the
formatter.

## Test Plan

I added new fixtures.
2023-06-26 09:13:07 +00:00
Micha Reiser 8879927b9a
Use `insta::glob` instead of `fixture` macro (#5364) 2023-06-26 08:46:18 +00:00
Charlie Marsh dce6a046b0
Add tests for escape-sequence-in-docstring (#5362)
## Summary

Looks like I added a regression in #5360. This PR fixes it and adds
dedicated tests to avoid it in the future.
2023-06-25 22:42:12 -04:00
Charlie Marsh 18c73c1f9b
Improve backslash-detection rule for docstrings (#5360) 2023-06-26 01:58:20 +00:00
Charlie Marsh 19c221a2d2
Use matches for `os-error-alias` (#5361) 2023-06-26 01:57:52 +00:00
Tom Kuson fd0c3faa70
Add documentation to rules that check docstring quotes (`D3XX`) (#5351)
## Summary

Add documentation to the `D3XX` rules that check for issues with
docstring quotes. Related to #2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-25 22:34:03 +00:00
Charlie Marsh 1fe4073b56
Update the `invalid-escape-sequence` rule (#5359)
Just a couple small tweaks based on reading the rule with fresh eyes and
new best-practices.
2023-06-25 22:20:31 +00:00
Charlie Marsh 1ef4eee089
Add space when migrating to raw string (#5358)
## Summary

We had to do this for f-strings too -- if we add a prefix to `"foo"` in
`return"foo"`, we also need to add a leading space.
2023-06-25 18:10:08 -04:00
Shantanu 0ce38b650e
Change W605 autofix to use raw strings if possible (#5352)
Fixes #5061.
2023-06-25 17:35:07 -04:00
Evan Rittenhouse e0a507e48e
Add Applicability to flake8_simplify (#5348) 2023-06-23 22:54:43 +00:00
Dhruv Manilawala adf5cb5ff7
Ignore type aliases for RUF013 (#5344)
## Summary

Ignore type aliases for RUF013 to avoid flagging false positives:

```python
from typing import Optional

MaybeInt = Optional[int]


def f(arg: MaybeInt = None):
    pass
```

But, at the expense of having false negatives:

```python
Text = str | bytes


def f(arg: Text = None):
    pass
```

## Test Plan

`cargo test`

fixes: #5295
2023-06-23 22:51:09 +00:00
Micha Reiser d3d69a031e
Add `JoinCommaSeparatedBuilder` (#5342) 2023-06-23 22:03:05 +01:00
Micha Reiser 6ba9d5d5a4
Upgrade RustPython (#5334) 2023-06-23 20:39:47 +00:00
Charlie Marsh f45d1c2b84
Remove HashMap and HashSet for known-standard-library detection (#5345)
## Summary

This is a lot more concise and probably much more performant (with fewer
instructions).
2023-06-23 19:59:03 +00:00
konstin 4b65446de6
Refactor magic trailing comma (#5339)
## Summary

This is small refactoring to reuse the code that detects the magic
trailing comma across functions. I make this change now to avoid copying
code in a later PR. @MichaReiser is planning on making a larger
refactoring later that integrates with the join nodes builder

## Test Plan

No functional changes. The magic trailing comma behaviour is checked by
the fixtures.
2023-06-23 18:53:55 +02:00
James Berry f85eb709e2
Visit AugAssign target after value (#5325)
## Summary

When visiting AugAssign in evaluation order, the AugAssign `target`
should be visited after it's `value`. Based on my testing, the pseudo
code for `a += b` is effectively:
```python
tmp = a
a = tmp.__iadd__(b)
```

That is, an ideal traversal order would look something like this:
1. load a
2. b
3. op
4. store a

But, there is only a single AST node which captures `a` in the statement
`a += b`, so it cannot be traversed both before and after the traversal
of `b` and the `op`.

Nonetheless, I think traversing `a` after `b` and the `op` makes the
most sense for a number of reasons:
1. All the other assignment expressions traverse their `value`s before
their `target`s. Having `AugAssign` traverse in the same order would be
more consistent.
2. Within the AST, the `ctx` of the `target` for an `AugAssign` is
`Store` (though technically this is a `Load` and `Store` operation, the
AST only indicates it as a `Store`). Since the the store portion of the
`AugAssign` occurs last, I think it makes sense to traverse the `target`
last as well.

The effect of this is marginal, but it may have an impact on the
behavior of #5271.
2023-06-23 09:54:54 -04:00
Thomas de Zeeuw 1c638264b2
Keep track of when files are last seen in the cache (#5214)
## Summary

And remove cached files that we haven't seen for a certain period of
time, currently 30 days.

For the last seen timestamp we actually use an `u64`, it's smaller on
disk than `SystemTime` (which size is OS dependent) and fits in an
`AtomicU64` which we can use to update it without locks.

## Test Plan

Added a new unit test, run by `cargo test`.
2023-06-23 15:40:35 +02:00
Micha Reiser 2dfa6ff58d
Fix unstable set comprehension formatting (#5327) 2023-06-23 11:50:24 +02:00
konstin 930f03de98
Don't mistake a following if for an elif (#5296)
In the following code, the comment used to get wrongly associated with
the `if False` since it looked like an elif. This fixes it by checking
the indentation and adding a regression test
```python
if True:
    pass
else:  # Comment
    if False:
        pass
    pass
```
    
Originally found in
1570b94a02/gradio/external.py (L478)
2023-06-23 10:07:28 +02:00
Micha Reiser c52aa8f065
Basic string formatting
<!--
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

This PR implements formatting for non-f-string Strings that do not use implicit concatenation. 

Docstring formatting is out of the scope of this PR.

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

## Test Plan

I added a few tests for simple string literals. 

## Performance

Ouch. This is hitting performance somewhat hard. This is probably because we now iterate each string a couple of times:

1. To detect if it is an implicit string continuation
2. To detect if the string contains any new lines
3. To detect the preferred quote
4. To normalize the string

Edit: I integrated the detection of newlines into the preferred quote detection so that we only iterate the string three time.
We can probably do better by merging the implicit string continuation with the quote detection and new line detection by iterating till the end of the string part and returning the offset. We then use our simple tokenizer to skip over any comments or whitespace until we find the first non trivia token. From there we keep continue doing this in a loop until we reach the end o the string. I'll leave this improvement for later.
2023-06-23 09:46:05 +02:00
Micha Reiser 3e12bdff45
Format Compare Op
<!--
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

This PR adds basic formatting for compare operations.

The implementation currently breaks diffeently when nesting binary like expressions. I haven't yet figured out what Black's logic is in that case but I think that this by itself is already an improvement worth merging.

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

## Test Plan

I added a few new tests 

<!-- How was it tested? -->
2023-06-23 09:35:29 +02:00
James Berry 2142bf6141
Fix annotation and format spec visitors (#5324)
## Summary

The `Visitor` and `preorder::Visitor` traits provide some convenience
functions, `visit_annotation` and `visit_format_spec`, for handling
annotation and format spec expressions respectively. Both of these
functions accept an `&Expr` and have a default implementation which
delegates to `walk_expr`. The problem with this approach is that any
custom handling done in `visit_expr` will be skipped for annotations and
format specs. Instead, to capture any custom logic implemented in
`visit_expr`, both of these function's default implementations should
delegate to `visit_expr` instead of `walk_expr`.

## Example

Consider the below `Visitor` implementation:
```rust
impl<'a> Visitor<'a> for Example<'a> {
    fn visit_expr(&mut self, expr: &'a Expr) {
        match expr {
            Expr::Name(ExprName { id, .. }) => println!("Visiting {:?}", id),
            _ => walk_expr(self, expr),
        }
    }
}
```

Run on the following Python snippet:
```python
a: b
```

I would expect such a visitor to print the following:
```
Visiting b
Visiting a
```

But it instead prints the following:
```
Visiting a
```

Our custom `visit_expr` handler is not invoked for the annotation.

## Test Plan

Tests added in #5271 caught this behavior.
2023-06-23 03:55:42 +00:00
Tom Kuson 1cf307c34c
Fix `collection-literal-concatenation` documentation (#5320)
## Summary

Move `collection-literal-concatenation` markdown documentation to the
correct place.

Fixes error in #5262.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-22 18:37:54 -04:00
Charlie Marsh 7819b95d7f
Avoid syntax errors when removing f-string prefixes (#5319)
Closes https://github.com/astral-sh/ruff/issues/5281.

Closes https://github.com/astral-sh/ruff/issues/4827.
2023-06-22 17:21:09 -04:00
Lukas Mayrhofer 4a81cfc51a
Allow `@Author` format for "Missing Author" rule in `flake8-todos` (#4903)
## Summary

The TD-002 rule "Missing Author" was updated to allow another format
using "@". This reflects the current 0.3.0 version of flake8-todos.
2023-06-22 20:53:58 +00:00
qdegraaf 38e618cd18
[`perflint`] Add `PERF101` with autofix (#5121)
## Summary

Adds PERF101 which checks for unnecessary casts to `list` in for loops. 

NOTE: Is not fully equal to its upstream implementation as this
implementation does not flag based on type annotations
(i.e.):
```python
def foo(x: List[str]):
    for y in list(x):
        ...
```

With the current set-up it's quite hard to get the annotation from a
function arg from its binding. Problem is best considered broader than
this implementation.

## Test Plan

Added fixture. 

## Issue links

Refers: https://github.com/astral-sh/ruff/issues/4789

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-06-22 20:44:26 +00:00
Charlie Marsh 8bc7378002
Add `PythonVersion::Py312` (#5316)
Closes #5310.
2023-06-22 20:01:07 +00:00
Charlie Marsh cdbd0bd5cd
Respect `abc` decorators when classifying function types (#5315)
Closes #5307.
2023-06-22 19:52:36 +00:00
Charlie Marsh 5f88ff8a96
Allow `__slots__` assignments in `mutable-class-default` (#5314)
Closes #5309.
2023-06-22 19:40:54 +00:00
Charlie Marsh 1c2be54b4a
Support `pydantic.BaseSettings` in `mutable-class-default` (#5312)
Closes #5308.
2023-06-22 19:27:05 +00:00
konstin 03694ef649
More stability checker options (#5299)
## Summary

This contains three changes:
* repos in `check_ecosystem.py` are stored as `org:name` instead of
`org/name` to create a flat directory layout
* `check_ecosystem.py` performs a maximum of 50 parallel jobs at the
same time to avoid consuming to much RAM
* `check-formatter-stability` gets a new option `--multi-project` so
it's possible to do `cargo run --bin ruff_dev --
check-formatter-stability --multi-project target/checkouts`
With these three changes it becomes easy to check the formatter
stability over a larger number of repositories. This is part of the
integration of integrating formatter regressions checks into the
ecosystem checks.

## Test Plan

```shell
python scripts/check_ecosystem.py --checkouts target/checkouts --projects github_search.jsonl -v $(which true) $(which true)
cargo run --bin ruff_dev -- check-formatter-stability --multi-project target/checkouts
```
2023-06-22 15:48:11 +00:00
Tom Kuson eaa10ad2d9
Fix `deprecated-import` false positives (#5291)
## Summary

Remove recommendations to replace
`typing_extensions.dataclass_transform` and
`typing_extensions.SupportsIndex` with their `typing` library
counterparts.

Closes #5112.

## Test Plan

Added extra checks to the test fixture.

`cargo test`
2023-06-22 15:34:44 +00:00
Evan Rittenhouse 84259f5440
Add Applicability to pycodestyle (#5282) 2023-06-22 11:25:20 -04:00
konstin d407165aa7
Fix formatter panic with comment after parenthesized dict value (#5293)
## Summary

This snippet used to panic because it expected to see a comma or
something similar after the `2` but met the closing parentheses that is
not part of the range and panicked
```python
a = {
    1: (2),
    # comment
    3: True,
}
```

Originally found in
636a717ef0/testing/marionette/client/marionette_driver/geckoinstance.py (L109)

This snippet is also the test plan.
2023-06-22 16:52:48 +02:00
Micha Reiser f7e1cf4b51
Format `class` definitions (#5289) 2023-06-22 09:09:43 +00:00
konstin 7d4f8e59da
Improve FormatExprCall dummy (#5290)
This solves an instability when formatting cpython. It also introduces
another one, but i think it's still a worthwhile change for now.

There's no proper testing since this is just a dummy.
2023-06-22 10:59:30 +02:00
Charlie Marsh 1c0a3a467f
Bump version to 0.0.275 (#5276) 2023-06-21 21:53:37 -04:00
Charlie Marsh 6b8b318d6b
Use `mod tests` consistently (#5278)
As per the Rust documentation.
2023-06-22 01:50:28 +00:00
Charlie Marsh c0c59b82ec
Use 'Checks for uses' consistently (#5279) 2023-06-22 01:44:52 +00:00
Charlie Marsh ac146e11f0
Allow `typing.Final` for `mutable-class-default annotations` (`RUF012`) (#5274)
## Summary

See: https://github.com/astral-sh/ruff/issues/5243.
2023-06-22 00:24:53 +00:00
Charlie Marsh 1229600e1d
Ignore Pydantic classes when evaluating `mutable-class-default` (`RUF012`) (#5273)
Closes https://github.com/astral-sh/ruff/issues/5272.
2023-06-21 23:59:44 +00:00
Micha Reiser ccf34aae8c
Format Attribute Expression (#5259) 2023-06-21 21:33:53 +00:00
Tom Kuson 341b12d918
Complete documentation for Ruff-specific rules (#5262)
## Summary

Completes the documentation for the Ruff-specific ruleset. Related to
#2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-21 21:30:44 +00:00
Micha Reiser 3d7411bfaf
Use trait for labels instead of `TypeId` (#5270) 2023-06-21 22:26:09 +01:00
David Szotten 1eccbbb60e
Format StmtFor (#5163)
<!--
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

format StmtFor

still trying to learn how to help out with the formatter. trying
something slightly more advanced than [break](#5158)

mostly copied form StmtWhile

## Test Plan

snapshots
2023-06-21 23:00:31 +02:00
Charlie Marsh e71f044f0d
Avoid including nursery rules in linter-level selectors (#5268)
## Summary

Ensures that `--select PL` and `--select PLC` don't include `PLC1901`.
Previously, `--select PL` _did_, because it's a "linter-level selector"
(`--select PLC` is viewed as selecting the `C` prefix from `PL`), and we
were missing this filtering path.
2023-06-21 20:11:40 +00:00
James Berry f194572be8
Remove visit_arg_with_default (#5265)
## Summary

This is a follow up to #5221. Turns out it was easy to restructure the
visitor to get the right order, I'm just dumb 🤷‍♂️ I've
removed `visit_arg_with_default` entirely from the `Visitor`, although
it still exists as part of `preorder::Visitor`.
2023-06-21 16:00:24 -04:00
Charlie Marsh 62e2c46f98
Move `compare-to-empty-string` to nursery (#5264)
## Summary

This rule has too many false positives. It has parity with the Pylint
version, but the Pylint version is part of an
[extension](https://pylint.readthedocs.io/en/stable/user_guide/messages/convention/compare-to-empty-string.html),
and so requires explicit opt-in.

I'm moving this rule to the nursery to require explicit opt-in, as with
Pylint.

Closes #4282.
2023-06-21 19:47:02 +00:00
konstin 9419d3f9c8
Special `ExprTuple` formatting option for `for`-loops (#5175)
## Motivation

While black keeps parentheses nearly everywhere, the notable exception
is in the body of for loops:
```python
for (a, b) in x:
    pass
```
becomes
```python
for a, b in x:
    pass
```

This currently blocks #5163, which this PR should unblock.

## Solution

This changes the `ExprTuple` formatting option to include one additional
option that removes the parentheses when not using magic trailing comma
and not breaking. It is supposed to be used through
```rust
#[derive(Debug)]
struct ExprTupleWithoutParentheses<'a>(&'a Expr);

impl Format<PyFormatContext<'_>> for ExprTupleWithoutParentheses<'_> {
    fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
        match self.0 {
            Expr::Tuple(expr_tuple) => expr_tuple
                .format()
                .with_options(TupleParentheses::StripInsideForLoop)
                .fmt(f),
            other => other.format().with_options(Parenthesize::IfBreaks).fmt(f),
        }
    }
}
```


## Testing

The for loop formatting isn't merged due to missing this (and i didn't
want to create more git weirdness across two people), but I've confirmed
that when applying this to while loops instead of for loops, then
```rust
        write!(
            f,
            [
                text("while"),
                space(),
                ExprTupleWithoutParentheses(test.as_ref()),
                text(":"),
                trailing_comments(trailing_condition_comments),
                block_indent(&body.format())
            ]
        )?;
```
makes
```python
while (a, b):
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (a,b,):
    pass
```
formatted as
```python
while a, b:
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (
    a,
    b,
):
    pass
```
2023-06-21 21:17:47 +02:00
James Berry 9b5fb8f38f
Fix AST visitor traversal order (#5221)
## Summary

According to the AST visitor documentation, the AST visitor "visits all
nodes in the AST recursively in evaluation-order". However, the current
traversal fails to meet this specification in a few places.

### Function traversal

```python
order = []
@(order.append("decorator") or (lambda x: x))
def f(
    posonly: order.append("posonly annotation") = order.append("posonly default"),
    /,
    arg: order.append("arg annotation") = order.append("arg default"),
    *args: order.append("vararg annotation"),
    kwarg: order.append("kwarg annotation") = order.append("kwarg default"),
    **kwargs: order.append("kwarg annotation")
) -> order.append("return annotation"):
    pass
print(order)
```

Executing the above snippet using CPython 3.10.6 prints the following
result (formatted for readability):
```python
[
    'decorator',
    'posonly default',
    'arg default',
    'kwarg default',
    'arg annotation',
    'posonly annotation',
    'vararg annotation',
    'kwarg annotation',
    'kwarg annotation',
    'return annotation',
]
```

Here we can see that decorators are evaluated first, followed by
argument defaults, and annotations are last. The current traversal of a
function's AST does not align with this order.

### Annotated assignment traversal
```python
order = []
x: order.append("annotation") = order.append("expression")
print(order)
```

Executing the above snippet using CPython 3.10.6 prints the following
result:
```python
['expression', 'annotation']
```

Here we can see that an annotated assignments annotation gets evaluated
after the assignment's expression. The current traversal of an annotated
assignment's AST does not align with this order.

## Why?

I'm slowly working on #3946 and porting over some of the logic and tests
from ssort. ssort is very sensitive to AST traversal order, so ensuring
the utmost correctness here is important.

## Test Plan

There doesn't seem to be existing tests for the AST visitor, so I didn't
bother adding tests for these very subtle changes. However, this
behavior will be captured in the tests for the PR which addresses #3946.
2023-06-21 14:40:58 -04:00
konstin d7c7484618
Format function argument separator comments (#5211)
## Summary

This is a complete rewrite of the handling of `/` and `*` comment
handling in function signatures. The key problem is that slash and star
don't have a note. We now parse out the positions of slash and star and
their respective preceding and following note. I've left code comments
for each possible case of function signature structure and comment
placement

## Test Plan

I extended the function statement fixtures with cases that i found. If
you have more weird edge cases your input would be appreciated.
2023-06-21 17:56:47 +00:00
konstin bc63cc9b3c
Fix remaining CPython formatter errors except for function argument separator comments (#5210)
## Summary

This fixes two problems discovered when trying to format the cpython
repo with `cargo run --bin ruff_dev -- check-formatter-stability
projects/cpython`:

The first is to ignore try/except trailing comments for now since they
lead to unstable formatting on the dummy.

The second is to avoid dropping trailing if comments through placement:
This changes the placement to keep a comment trailing an if-elif or
if-elif-else to keep the comment a trailing comment on the entire if.
Previously the last comment would have been lost.
```python
if "first if":
    pass
elif "first elif":
    pass
```

The last remaining problem in cpython so far is function signature
argument separator comment placement which is its own PR on top of this.

## Test Plan

I added test fixtures of minimized examples with links back to the
original cpython location
2023-06-21 19:45:53 +02:00
Charlie Marsh bf1a94ee54
Initialize caches for packages and standalone files (#5237)
## Summary

While fixing https://github.com/astral-sh/ruff/pull/5233, I noticed that
in FastAPI, 343 out of 823 files weren't hitting the cache. It turns out
these are standalone files in the documentation that lack a "package
root". Later, when looking up the cache entries, we fallback to the
package directory.

This PR ensures that we initialize the cache for both kinds of files:
those that are in a package, and those that aren't.

The total size of the FastAPI cache for me is now 388K. I also suspect
that this approach is much faster than as initially written, since
before, we were probably initializing one cache per _directory_.

## Test Plan

Ran `cargo run -p ruff_cli -- check ../fastapi --verbose`; verified
that, on second execution, there were no "Checking" entries in the logs.
2023-06-21 17:29:09 +00:00
Dhruv Manilawala c792c10eaa
Add support for nested quoted annotations in RUF013 (#5254)
## Summary

This is a follow up on #5235 to add support for nested quoted
annotations for RUF013.

## Test Plan

`cargo test`
2023-06-21 17:25:27 +00:00
Evan Rittenhouse f9ffb3d50d
Add Applicability to pylint (#5251) 2023-06-21 17:22:01 +00:00
Evan Rittenhouse 2b76d88bd3
Add Applicability to pandas_vet (#5252) 2023-06-21 17:12:47 +00:00
Evan Rittenhouse 41ef17b007
Add Applicability to pyflakes (#5253) 2023-06-21 17:04:55 +00:00
Charlie Marsh 0aa21277c6
Improve documentation for overlong-line rules (#5260)
Closes https://github.com/astral-sh/ruff/issues/5248.
2023-06-21 17:02:20 +00:00
Charlie Marsh ecf61d49fa
Restore existing bindings when unbinding caught exceptions (#5256)
## Summary

In the latest release, we made some improvements to the semantic model,
but our modifications to exception-unbinding are causing some
false-positives. For example:

```py
try:
    v = 3
except ImportError as v:
    print(v)
else:
    print(v)
```

In the latest release, we started unbinding `v` after the `except`
handler. (We used to restore the existing binding, the `v = 3`, but this
was quite complicated.) Because we don't have full branch analysis, we
can't then know that `v` is still bound in the `else` branch.

The solution here modifies `resolve_read` to skip-lookup when hitting
unbound exceptions. So when store the "unbind" for `except ImportError
as v`, we save the binding that it shadowed `v = 3`, and skip to that.

Closes #5249.

Closes #5250.
2023-06-21 12:53:58 -04:00
Micha Reiser e47aa468d5
Format Identifier (#5255) 2023-06-21 17:35:37 +02:00
konstin 6155fd647d
Format Slice Expressions (#5047)
This formats slice expressions and subscript expressions.

Spaces around the colons follows the same rules as black
(https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices):
```python
e00 = "e"[:]
e01 = "e"[:1]
e02 = "e"[: a()]
e10 = "e"[1:]
e11 = "e"[1:1]
e12 = "e"[1 : a()]
e20 = "e"[a() :]
e21 = "e"[a() : 1]
e22 = "e"[a() : a()]
e200 = "e"[a() : :]
e201 = "e"[a() :: 1]
e202 = "e"[a() :: a()]
e210 = "e"[a() : 1 :]
```

Comment placement is different due to our very different infrastructure.
If we have explicit bounds (e.g. `x[1:2]`) all comments get assigned as
leading or trailing to the bound expression. If a bound is missing
`[:]`, comments get marked as dangling and placed in the same section as
they were originally in:
```python
x = "x"[ # a
      # b
    :  # c
      # d
]
```
to
```python
x = "x"[
    # a
    # b
    :
    # c
    # d
]
```
Except for the potential trailing end-of-line comments, all comments get
formatted on their own line. This can be improved by keeping end-of-line
comments after the opening bracket or after a colon as such but the
changes were already complex enough.

I added tests for comment placement and spaces.
2023-06-21 15:09:39 +00:00
Charlie Marsh 10885d09a1
Add support for top-level quoted annotations in RUF013 (#5235)
## Summary

This PR adds support for autofixing annotations like:

```python
def f(x: "int" = None):
    ...
```

However, we don't yet support nested quotes, like:

```python
def f(x: Union["int", "str"] = None):
    ...
```

Closes #5231.
2023-06-21 10:23:37 -04:00
konstin 44156f6962
Improve debuggability of `place_comment` (#5209)
## Summary

I found it hard to figure out which function decides placement for a
specific comment. An explicit loop makes this easier to debug

## Test Plan

There should be no functional changes, no changes to the formatting of
the fixtures.
2023-06-21 09:52:13 +00:00
konstin f551c9aad2
Unify benchmarking and profiling docs (#5145)
This moves all docs about benchmarking and profiling into
CONTRIBUTING.md by moving the readme of `ruff_benchmark` and adding more
information on profiling.

We need to somehow consolidate that documentation, but i'm not convinced
that this is the best way (i tried subpages in mkdocs, but that didn't
seem good either), so i'm happy to take suggestions.
2023-06-21 09:39:56 +00:00
Micha Reiser 653dbb6d17
Format BoolOp (#4986) 2023-06-21 09:27:57 +00:00
konstin db301c14bd
Consistently name comment own line/end-of-line `line_position()` (#5215)
## Summary

Previously, `DecoratedComment` used `text_position()` and
`SourceComment` used `position()`. This PR unifies this to
`line_position` everywhere.

## Test Plan

This is a rename refactoring.
2023-06-21 11:04:56 +02:00
Micha Reiser 1336ca601b
Format `UnaryExpr`
<!--
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

This PR adds basic formatting for unary expressions.

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

## Test Plan

I added a new `unary.py` with custom test cases
2023-06-21 10:09:47 +02:00
Micha Reiser 3973836420
Correctly handle left/right breaking of binary expression
<!--
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
Black supports for layouts when it comes to breaking binary expressions:

```rust
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum BinaryLayout {
    /// Put each operand on their own line if either side expands
    Default,

    /// Try to expand the left to make it fit. Add parentheses if the left or right don't fit.
    ///
    ///```python
    /// [
    ///     a,
    ///     b
    /// ] & c
    ///```
    ExpandLeft,

    /// Try to expand the right to make it fix. Add parentheses if the left or right don't fit.
    ///
    /// ```python
    /// a & [
    ///     b,
    ///     c
    /// ]
    /// ```
    ExpandRight,

    /// Both the left and right side can be expanded. Try in the following order:
    /// * expand the right side
    /// * expand the left side
    /// * expand both sides
    ///
    /// to make the expression fit
    ///
    /// ```python
    /// [
    ///     a,
    ///     b
    /// ] & [
    ///     c,
    ///     d
    /// ]
    /// ```
    ExpandRightThenLeft,
}
```

Our current implementation only handles `ExpandRight` and `Default` correctly. This PR adds support for `ExpandRightThenLeft` and `ExpandLeft`. 

## Test Plan

I added tests that play through all 4 binary expression layouts.
2023-06-21 09:40:05 +02:00
Charlie Marsh e0339b538b
Bump version to 0.0.274 (#5230) 2023-06-20 22:12:32 -04:00
Charlie Marsh 07b6b7401f
Move `copyright` rules to `flake8_copyright` module (#5236)
## Summary

I initially wanted this category to be more general and decoupled from
the plugin, but I got some feedback that the titling felt inconsistent
with others.
2023-06-21 01:56:40 +00:00
Charlie Marsh 1db7d9e759
Avoid erroneous RUF013 violations for quoted annotations (#5234)
## Summary

Temporary fix for #5231: if we can't flag and fix these properly, just
disabling them for now.

\cc @dhruvmanila 

## Test Plan

`cargo test`
2023-06-21 01:29:12 +00:00
Charlie Marsh 621e9ace88
Use package roots rather than package members for cache initialization (#5233)
## Summary

This is a proper fix for the issue patched-over in
https://github.com/astral-sh/ruff/pull/5229, thanks to an extremely
helpful repro from @tlambert03 in that thread. It looks like we were
using the keys of `package_roots` rather than the values to initialize
the cache -- but it's a map from package to package root.

## Test Plan

Reverted #5229, then ran through the plan that @tlambert03 included in
https://github.com/astral-sh/ruff/pull/5229#issuecomment-1599723226.
Verified the panic before but not after this change.
2023-06-20 21:21:45 -04:00
Charlie Marsh f9f77cf617
Revert change to `RUF010` to remove unnecessary `str` calls (#5232)
## Summary

This PR reverts #4971 (aba073a791). It
turns out that `f"{str(x)}"` and `f"{x}"` are often but not exactly
equivalent, and performing that conversion automatically can lead to
subtle bugs, See the discussion in
https://github.com/astral-sh/ruff/issues/4958.
2023-06-20 21:15:17 -04:00
Charlie Marsh 1a2bd984f2
Avoid `.unwrap()` on cache access (#5229)
## Summary

I haven't been able to determine why / when this is happening, but in
some cases, users are reporting that this `unwrap()` is causing a panic.
It's fine to just return `None` here and fallback to "No cache",
certainly better than panicking (while we figure out the edge case).

Closes #5225.

Closes #5228.
2023-06-20 19:01:21 -04:00
Tom Kuson 4717d0779f
Complete `flake8-debugger` documentation (#5223)
## Summary

Completes the documentation for the `flake8-debugger` ruleset. Related
to #2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-20 21:04:32 +00:00
Florian Stasse 07409ce201
Fixed typo in numpy deprecated type alias rule documentation (#5224)
## Summary

It is a very simple typo fix in the "numy deprecated type alias"
documentation.
2023-06-20 16:51:51 -04:00
Micha Reiser e520a3a721
Fix ArgWithDefault comments handling (#5204) 2023-06-20 20:48:07 +00:00
Charlie Marsh fde5dbc9aa
Bump version to 0.0.273 (#5218) 2023-06-20 14:37:28 -04:00
Charlie Marsh 30734f06fd
Support parenthesized expressions when splitting compound assertions (#5219)
## Summary

I'm looking into the Black stability tests, and here's one failing case.

We split `assert a and (b and c)` into:

```python
assert a
assert (b and c)
```

We fail to split `assert (b and c)` due to the parentheses. But Black
then removes then, and when running Ruff again, we get:

```python
assert a
assert b
assert c
```

This PR just enables us to fix to this in one pass.
2023-06-20 13:47:01 -04:00
Charlie Marsh 4547002eb7
Remove defaults from fixtures/pyproject.toml (#5217)
## Summary

These should be encoded in the tests themselves, rather than here. In
fact, I think they're all unused?
2023-06-20 13:16:00 -04:00
Charlie Marsh 310abc769d
Move `StarImport` to its own module (#5186) 2023-06-20 13:12:46 -04:00
Micha Reiser b369288833
Accept any `Into<AnyNodeRef>` as `Comments` arguments (#5205) 2023-06-20 16:49:21 +00:00
Dhruv Manilawala 6f7d3cc798
Add option (`-o`/`--output-file`) to write output to a file (#4950)
## Summary

A new CLI option (`-o`/`--output-file`) to write output to a file
instead of stdout.

Major change is to remove the lock acquired on stdout. The argument is
that the output is buffered and thus the lock is acquired only when
writing a block (8kb). As per the benchmark below there is a slight
performance penalty.

Reference:
https://rustmagazine.org/issue-3/javascript-compiler/#printing-is-slow

## Benchmarks

_Output is truncated to only contain useful information:_

Command: `check --isolated --no-cache --select=ALL --show-source
./test-repos/cpython"`

Latest HEAD (361d45f2b2) with and without
the manual lock on stdout:

```console
Benchmark 1: With lock
  Time (mean ± σ):      5.687 s ±  0.075 s    [User: 17.110 s, System: 0.486 s]
  Range (min … max):    5.615 s …  5.860 s    10 runs

Benchmark 2: Without lock
  Time (mean ± σ):      5.719 s ±  0.064 s    [User: 17.095 s, System: 0.491 s]
  Range (min … max):    5.640 s …  5.865 s    10 runs

Summary
  (1) ran 1.01 ± 0.02 times faster than (2)
```

This PR:

```console
Benchmark 1: This PR
  Time (mean ± σ):      5.855 s ±  0.058 s    [User: 17.197 s, System: 0.491 s]
  Range (min … max):    5.786 s …  5.987 s    10 runs
 
Benchmark 2: Latest HEAD with lock
  Time (mean ± σ):      5.645 s ±  0.033 s    [User: 16.922 s, System: 0.495 s]
  Range (min … max):    5.600 s …  5.712 s    10 runs
 
Summary
  (2) ran 1.04 ± 0.01 times faster than (1)
```

## Test Plan

Run all of the commands which gives output with and without the
`--output-file=ruff.out` option:
* `--show-settings`
* `--show-files`
* `--show-fixes`
* `--diff`
* `--select=ALL`
* `--select=All --show-source`
* `--watch` (only stdout allowed)

resolves: #4754
2023-06-20 22:16:49 +05:30
Micha Reiser d9e59b21cd
Add BestFittingMode (#5184)
## Summary
Black supports for layouts when it comes to breaking binary expressions:

```rust
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum BinaryLayout {
    /// Put each operand on their own line if either side expands
    Default,

    /// Try to expand the left to make it fit. Add parentheses if the left or right don't fit.
    ///
    ///```python
    /// [
    ///     a,
    ///     b
    /// ] & c
    ///```
    ExpandLeft,

    /// Try to expand the right to make it fix. Add parentheses if the left or right don't fit.
    ///
    /// ```python
    /// a & [
    ///     b,
    ///     c
    /// ]
    /// ```
    ExpandRight,

    /// Both the left and right side can be expanded. Try in the following order:
    /// * expand the right side
    /// * expand the left side
    /// * expand both sides
    ///
    /// to make the expression fit
    ///
    /// ```python
    /// [
    ///     a,
    ///     b
    /// ] & [
    ///     c,
    ///     d
    /// ]
    /// ```
    ExpandRightThenLeft,
}
```

Our current implementation only handles `ExpandRight` and `Default` correctly. `ExpandLeft` turns out to be surprisingly hard. This PR adds a new `BestFittingMode` parameter to `BestFitting` to support `ExpandLeft`.

There are 3 variants that `ExpandLeft` must support:

**Variant 1**: Everything fits on the line (easy)

```python
[a, b] + c
```

**Variant 2**: Left breaks, but right fits on the line. Doesn't need parentheses

```python
[
	a,
	b
] + c
```

**Variant 3**: The left breaks, but there's still not enough space for the right hand side. Parenthesize the whole expression:

```python
(
	[
		a, 
		b
	]
	+ ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
)
```

Solving Variant 1 and 2 on their own is straightforward The printer gives us this behavior by nesting right inside of the group of left:

```
group(&format_args![
	if_group_breaks(&text("(")),
	soft_block_indent(&group(&format_args![
		left, 
		soft_line_break_or_space(), 
		op, 
		space(), 
		group(&right)
	])),
	if_group_breaks(&text(")"))
])
```

The fundamental problem is that the outer group, which adds the parentheses, always breaks if the left side breaks. That means, we end up with

```python
(
	[
		a,
		b
	] + c
)
```

which is not what we want (we only want parentheses if the right side doesn't fit). 

Okay, so nesting groups don't work because of the outer parentheses. Sequencing groups doesn't work because it results in a right-to-left breaking which is the opposite of what we want. 

Could we use best fitting? Almost! 

```
best_fitting![
	// All flat
	format_args![left, space(), op, space(), right],
	// Break left
	format_args!(group(&left).should_expand(true), space(), op, space(), right],
	// Break all
	format_args![
		text("("), 
		block_indent!(&format_args![
			left, 
			hard_line_break(), 
			op,
			space()
			right
		])
	]
]
```

I hope I managed to write this up correctly. The problem is that the printer never reaches the 3rd variant because the second variant always fits:

* The `group(&left).should_expand(true)` changes the group so that all `soft_line_breaks` are turned into hard line breaks. This is necessary because we want to test if the content fits if we break after the `[`. 
* Now, the whole idea of `best_fitting` is that you can pretend that some content fits on the line when it actually does not. The way this works is that the printer **only** tests if all the content of the variant **up to** the first line break fits on the line (we insert that line break by using `should_expand(true))`. The printer doesn't care whether the rest `a\n, b\n ] + c` all fits on (multiple?) lines. 

Why does breaking right work but not breaking the left? The difference is that we can make the decision whether to parenthesis the expression based on the left expression. We can't do this for breaking left because the decision whether to insert parentheses or not would depend on a lookahead: will the right side break. We simply don't know this yet when printing the parentheses (it would work for the right parentheses but not for the left and indent).

What we kind of want here is to tell the printer: Look, what comes here may or may not fit on a single line but we don't care. Simply test that what comes **after** fits on a line. 

This PR adds a new `BestFittingMode` that has a new `AllLines` option that gives us the desired behavior of testing all content and not just up to the first line break. 

## Test Plan

I added a new example to  `BestFitting::with_mode`
2023-06-20 18:16:01 +02:00
Tom Kuson 6929fcc55f
Complete `flake8-bugbear` documentation (#5178)
## Summary

Completes the documentation for the `flake8-bugbear` ruleset. Related to
#2646.

## Test Plan

`python scripts/check_docs_formatted.py`

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-06-20 12:10:58 -04:00
Charlie Marsh 7bc33a8d5f
Remove identifier lexing in favor of parser ranges (#5195)
## Summary

Now that all identifiers include ranges (#5194), we can remove a ton of
this "custom lexing" code that we have to sketchily extract identifier
ranges from source.

## Test Plan

`cargo test`
2023-06-20 12:07:29 -04:00
Charlie Marsh 6331598511
Upgrade `RustPython` to access ranged names (#5194)
## Summary

In https://github.com/astral-sh/RustPython-Parser/pull/8, we modified
RustPython to include ranges for any identifiers that aren't
`Expr::Name` (which already has an identifier).

For example, the `e` in `except ValueError as e` was previously
un-ranged. To extract its range, we had to do some lexing of our own.
This change should improve performance and let us remove a bunch of
code.

## Test Plan

`cargo test`
2023-06-20 15:43:38 +00:00
Thomas de Zeeuw 17f1ecd56e
Open cache files in parallel (#5120)
## Summary

Open cache files in parallel (again), brings the performance back to be roughly equal to the old implementation.

## Test Plan

Existing tests should keep working.
2023-06-20 17:43:09 +02:00
Dhruv Manilawala 062b6e5c2b
Handle trailing newline in Jupyter notebook JSON string (#5202)
## Summary

Handle trailing newline in Jupyter Notebook JSON string similar to how
`black`
does it.

## Test Plan

Add test cases when the JSON string for notebook ends with and without a
newline.

resolves: #5190
2023-06-20 10:19:11 +00:00
David Szotten 773e79b481
basic formatting for ExprDict (#5167) 2023-06-20 09:25:08 +00:00
Charlie Marsh 4cc3cdba16
Use some more wildcard imports in rules (#5201) 2023-06-20 03:21:08 +00:00
Charlie Marsh a797e05602
Use a consistent argument ordering for `Indexer` (#5200) 2023-06-20 02:59:51 +00:00
Evan Rittenhouse 62aa77df31
Fix corner case involving terminal backslash after fixing `W293` (#5172)
## Summary

Fixes #4404. 

Consider this file:
```python
if True:
    x = 1; \
<space><space><space>
```

The current implementation of W293 removes the 3 spaces on line 2. This
fix changes the file to:
```python
if True:
    x = 1; \
```
A file can't end in a `\`, according to Python's [lexical
analysis](https://docs.python.org/3/reference/lexical_analysis.html), so
subsequent iterations of the autofixer fail (the AST-based ones
specifically, since they depend on a valid syntax tree and get
re-parsed).

This patch examines the line before the line checked in `W293`. If its
first non-whitespace character is a `\`, the patch will extend the
diagnostic's fix range to all whitespace up until the previous line's
*second* non-whitespace character; that is, it deletes all spaces and
potential `\`s up until the next non-whitespace character on the
previous line.

## Test Plan
Ran `cargo run -p ruff_cli -- ~/Downloads/aa.py --fix --select W293,D100
--no-cache` against the above file. This resulted in:
```
/Users/evan/Downloads/aa.py:1:1: D100 Missing docstring in public module
Found 2 errors (1 fixed, 1 remaining).
```
The file's contents, after the fix:
```python
if True:
    x = 1;<space>
```
The `\` was removed, leaving the terminal space. The space should be
handled by `Rule::TrailingWhitespace`, not `BlankLineWithWhitespace`.
2023-06-20 02:57:24 +00:00
Charlie Marsh 64bd955c58
Remove continuations before trailing semicolons (#5199)
## Summary

Closes #4828.
2023-06-20 02:22:32 +00:00
Charlie Marsh 8e06140d1d
Remove continuations when deleting statements (#5198)
## Summary

This PR modifies our statement deletion logic to delete any preceding
continuation lines.

For example, given:

```py
x = 1; \
  import os
```

We'll now rewrite to:

```py
x = 1;
```

In addition, the logic can now handle multiple preceding continuations
(which is unlikely, but valid).
2023-06-19 22:04:28 -04:00
Charlie Marsh 015895bcae
Move copyright rule to nursery (#5197)
## Summary

I want this to be explicitly opted-into.
2023-06-19 21:41:47 -04:00
Charlie Marsh 36e01ad6eb
Upgrade RustPython (#5192)
## Summary

This PR upgrade RustPython to pull in the changes to `Arguments` (zip
defaults with their identifiers) and all the renames to `CmpOp` and
friends.
2023-06-19 21:09:53 +00:00
Charlie Marsh ddfdc3bb01
Add rule documentation URL to JSON output (#5187)
## Summary

I want to include URLs to the rule documentation in the LSP (the LSP has
a native `code_description` field for this, which, if specified, causes
the source to be rendered as a link to the docs). This PR exposes the
URL to the documentation in the Ruff JSON output.
2023-06-19 21:09:15 +00:00
Dhruv Manilawala 48f4f2d63d
Maintain consistency when deserializing to JSON (#5114)
## Summary

Maintain consistency while deserializing Jupyter notebook to JSON. The
following changes were made:

1. Use string array to store the source value as that's the default
(5781720423/nbformat/v4/nbjson.py (L56-L57))
2. Remove unused structs and enums
3. Reorder the keys in alphabetical order as that's the default.
(5781720423/nbformat/v4/nbjson.py (L51))

### Side effect

Removing the `preserve_order` feature means that the order of keys in
JSON output (`--format json`) will be in alphabetical order. This is
because the value is represented using `serde_json::Value` which
internally is a `BTreeMap`, thus sorting it as per the string key. For
posterity if this turns out to be not ideal, then we could define a
struct representing the JSON object and the order of struct fields will
determine the order in the JSON string.

## Test Plan

Add a test case to assert the raw JSON string.
2023-06-19 23:47:56 +05:30
Charlie Marsh 94abf7f088
Rename `*Importation` structs to `*Import` (#5185)
## Summary

I find "Importation" a bit awkward, it may not even be grammatically
correct here.
2023-06-19 12:09:10 -04:00
Thomas de Zeeuw e3c12764f8
Only use a single cache file per Python package (#5117)
## Summary

This changes the caching design from one cache file per source file, to
one cache file per package. This greatly reduces the amount of cache
files that are opened and written, while maintaining roughly the same
(combined) size as bincode is very compact.

Below are some very much not scientific performance tests. It uses
projects/sources to check:

* small.py: single, 31 bytes Python file with 2 errors.
* test.py: single, 43k Python file with 8 errors.
* fastapi: FastAPI repo, 1134 files checked, 0 errors.

Source   | Before # files | After # files | Before size | After size
-------|-------|-------|-------|-------
small.py | 1              | 1             | 20 K        | 20 K
test.py  | 1              | 1             | 60 K        | 60 K
fastapi  | 1134           | 518           | 4.5 M       | 2.3 M

One question that might come up is why fastapi still has 518 cache files
and not 1? That is because this is using the existing package
resolution, which sees examples, docs, etc. as separate from the "main"
source code (in the fastapi directory in the repo). In this future it
might be worth consider switching to a one cache file per repo strategy.

This new design is not perfect and does have a number of known issues.
First, like the old design it doesn't remove the cache for a source file
that has been (re)moved until `ruff clean` is called.

Second, this currently uses a large mutex around the mutation of the
package cache (e.g. inserting result). This could be (or become) a
bottleneck. It's future work to test and improve this (if needed).

Third, currently the packages and opened and stored in a sequential
loop, this could be done parallel. This is also future work.


## Test Plan

Run `ruff check` (with caching enabled) twice on any Python source code
and it should produce the same results.
2023-06-19 17:46:13 +02:00
konstin b8d378b0a3
Add a script that tests formatter stability on repositories (#5055)
## Summary

We want to ensure that once formatted content stays the same when
formatted again, which is known as formatter stability or formatter
idempotency, and that the formatter prints syntactically valid code. As
our test cases cover only a limited amount of code, this allows checking
entire repositories.

This adds a new subcommand to `ruff_dev` which can be invoked as `cargo
run --bin ruff_dev -- check-formatter-stability <repo>`. While initially
only intended to check stability, it has also found cases where the
formatter printed invalid syntax or panicked.

 ## Test Plan

Running this on cpython is already identifying bugs
(https://github.com/astral-sh/ruff/pull/5089)
2023-06-19 14:13:38 +00:00
konstin 0e028142f4
Explain dangling comments in the formatter (#5170)
This documentation change improves the section on dangling comments in
the formatter.

---------

Co-authored-by: David Szotten <davidszotten@gmail.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-06-19 14:24:45 +02:00
konstin 361d45f2b2
Add `cargo dev repeat` for profiling (#5144)
## Summary

This adds a new subcommand that can be used as

```shell
cargo build --bin ruff_dev --profile=release-debug
perf record -g -F 999 target/release-debug/ruff_dev repeat --repeat 30 --exit-zero --no-cache path/to/cpython > /dev/null
flamegraph --perfdata perf.data
```

## Test Plan

This is a ruff internal script. I successfully used it to profile
cpython with the instructions above
2023-06-19 11:40:09 +02:00
Charlie Marsh be11cae619
Fix allowed-ellipsis detection (#5174)
## Summary

We weren't resetting the `allow_ellipsis` flag properly, which
ultimately caused us to treat the semicolon as "unnecessary" rather than
"creating a multi-statement line".

Closes #5154.
2023-06-19 04:19:41 +00:00
Charlie Marsh 2b82caa163
Detect continuations at start-of-file (#5173)
## Summary

Given:

```python
\
import os
```

Deleting `import os` leaves a syntax error: a file can't end in a
continuation. We have code to handle this case, but it failed to pick up
continuations at the _very start_ of a file.

Closes #5156.
2023-06-19 00:09:02 -04:00
Charlie Marsh a6cf31cc89
Move `dead_scopes` to `deferred.scopes` (#5171)
## Summary

This is more consistent with the rest of the `deferred` patterns.
2023-06-18 15:57:38 +00:00
Charlie Marsh 524a2045ba
Enable autofix for unconventional imports rule (#5152)
## Summary

We can now automatically rewrite `import pandas` to `import pandas as
pd`, with minimal changes needed.
2023-06-18 15:56:42 +00:00
Charlie Marsh a0b750f74b
Move unconventional import rule to post-binding phase (#5151)
## Summary

This PR moves the "unconventional import alias" rule (which enforces,
e.g., that `pandas` is imported as `pd`) to the "dead scopes" phase,
after the main linter pass. This (1) avoids an allocation since we no
longer need to create the qualified name in the linter pass; and (2)
will allow us to autofix it, since we'll have access to all references.

## Test Plan

`cargo test` -- all changes are to ranges (which are improvements IMO).
2023-06-18 15:23:40 +00:00
Chris Pryer 195b36c429
Format `continue` statement (#5165)
<!--
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

Format `continue` statement.

## Test Plan

`continue` is used already in some tests, but if a new test is needed I
could add it.

---------

Co-authored-by: konstin <konstin@mailbox.org>
2023-06-18 11:25:59 +00:00
konstin 5c416e4d9b
Pre commit without cargo and other pre-PR improvements (#5146)
This tackles three problems:
* pre-commit was slow because it ran cargo commands
* Improve the clarity on what you need to run to get your PR pass on CI
(and make those fast)
* You had to compile and run `cargo dev generate-all` separately, which
was slow

The first change is to remove all cargo commands except running ruff
itself from pre-commit. With `cargo run --bin ruff` already compiled it
takes about 7s on my machine. It would make sense to also use the ruff
pre-commit action here even if we're then lagging a release behind for
checking ruff on ruff.

The contributing guide is now clear about what you need to run:

```shell
cargo clippy --workspace --all-targets --all-features -- -D warnings  # Linting...
RUFF_UPDATE_SCHEMA=1 cargo test  # Testing and updating ruff.schema.json
pre-commit run --all-files  # rust and python formatting, markdown and python linting, etc.
```

Example timings from my machine:

`cargo clippy --workspace --all-targets --all-features -- -D warnings`:
23s
`RUFF_UPDATE_SCHEMA=1 cargo test`: 2min (recompiling), 1min (no code
changes, this is mainly doc tests)
`pre-commit run --all-files`: 7s

The exact numbers don't matter so much as the approximate experience (6s
is easier to just wait than 1min, esp if you need to fix and rerun). The
biggest remaining block seems to be doc tests, i'm surprised i didn't
find any solution to speeding them up (nextest simply doesn't run them
at all). Also note that the formatter has it's own tests which are much
faster since they avoid linking ruff (`cargo test
ruff_python_formatter`).

The third change is to enable `cargo test` to update the schema. Similar
to `INSTA_UPDATE=always`, i've added `RUFF_UPDATE_SCHEMA=1` (name open
to bikeshedding), so `RUFF_UPDATE_SCHEMA=1 cargo test` updates the
schema, while `cargo test` still fails as expected if the repo isn't
up-to-date.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2023-06-18 11:00:42 +00:00
konstin 763d38cafb
Refactor top llvm-lines entry (#5147)
## Summary

This refactors the top entry in terms of llvm lines,
`RuleCodePrefix::iter()`. It's only used for generating the schema and
the clap completion so no effect on performance.

I've confirmed with
```
CARGO_TARGET_DIR=target-llvm-lines RUSTFLAGS="-Csymbol-mangling-version=v0" cargo llvm-lines -p ruff --lib | head -n 20
```
that this indeed remove the method from the list of heaviest symbols in
terms of llvm-lines

Before:
```
  Lines                  Copies               Function name
  -----                  ------               -------------
  1768469                40538                (TOTAL)
    10391 (0.6%,  0.6%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::RuleCodePrefix>::iter
     8250 (0.5%,  1.1%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::Rule>::noqa_code
     7427 (0.4%,  1.5%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::checkers::ast::Checker as ruff_python_ast[c4c9eadfa5741dd4]::visitor::Visitor>::visit_stmt
     6536 (0.4%,  1.8%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_map::<toml_edit[de4ca26332d39787]:🇩🇪:spanned::SpannedDeserializer<toml_edit[de4ca26332d39787]:🇩🇪:value::ValueDeserializer>>
     6536 (0.4%,  2.2%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_map::<toml_edit[de4ca26332d39787]:🇩🇪:table::TableMapAccess>
     6533 (0.4%,  2.6%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_map::<toml_edit[de4ca26332d39787]:🇩🇪:datetime::DatetimeDeserializer>
     5727 (0.3%,  2.9%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::checkers::ast::Checker as ruff_python_ast[c4c9eadfa5741dd4]::visitor::Visitor>::visit_expr
     4453 (0.3%,  3.2%)      1 (0.0%,  0.0%)  ruff[fa0f2e8ef07114da]::flake8_to_ruff::converter::convert
     3790 (0.2%,  3.4%)      1 (0.0%,  0.0%)  <&ruff[fa0f2e8ef07114da]::registry::Linter as core[da82827a87f140f9]::iter::traits::collect::IntoIterator>::into_iter
     3416 (0.2%,  3.6%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::registry::Linter>::code_for_rule
     3187 (0.2%,  3.7%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::Rule as core[da82827a87f140f9]::fmt::Debug>::fmt
     3185 (0.2%,  3.9%)      1 (0.0%,  0.0%)  <&str as core[da82827a87f140f9]::convert::From<&ruff[fa0f2e8ef07114da]::codes::Rule>>::from
     3185 (0.2%,  4.1%)      1 (0.0%,  0.0%)  <&str as core[da82827a87f140f9]::convert::From<ruff[fa0f2e8ef07114da]::codes::Rule>>::from
     3185 (0.2%,  4.3%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::Rule as core[da82827a87f140f9]::convert::AsRef<str>>::as_ref
     3183 (0.2%,  4.5%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::RuleIter>::get
     2718 (0.2%,  4.6%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_seq::<toml_edit[de4ca26332d39787]:🇩🇪:array::ArraySeqAccess>
     2706 (0.2%,  4.8%)      1 (0.0%,  0.0%)  <&ruff[fa0f2e8ef07114da]::codes::Pylint as core[da82827a87f140f9]::iter::traits::collect::IntoIterator>::into_iter
```
After:
```
  Lines                  Copies               Function name
  -----                  ------               -------------
  1763380                40806                (TOTAL)
     8250 (0.5%,  0.5%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::Rule>::noqa_code
     7427 (0.4%,  0.9%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::checkers::ast::Checker as ruff_python_ast[c4c9eadfa5741dd4]::visitor::Visitor>::visit_stmt
     6536 (0.4%,  1.3%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_map::<toml_edit[de4ca26332d39787]:🇩🇪:spanned::SpannedDeserializer<toml_edit[de4ca26332d39787]:🇩🇪:value::ValueDeserializer>>
     6536 (0.4%,  1.6%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_map::<toml_edit[de4ca26332d39787]:🇩🇪:table::TableMapAccess>
     6533 (0.4%,  2.0%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_map::<toml_edit[de4ca26332d39787]:🇩🇪:datetime::DatetimeDeserializer>
     5727 (0.3%,  2.3%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::checkers::ast::Checker as ruff_python_ast[c4c9eadfa5741dd4]::visitor::Visitor>::visit_expr
     4453 (0.3%,  2.6%)      1 (0.0%,  0.0%)  ruff[fa0f2e8ef07114da]::flake8_to_ruff::converter::convert
     3790 (0.2%,  2.8%)      1 (0.0%,  0.0%)  <&ruff[fa0f2e8ef07114da]::registry::Linter as core[da82827a87f140f9]::iter::traits::collect::IntoIterator>::into_iter
     3416 (0.2%,  3.0%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::registry::Linter>::code_for_rule
     3187 (0.2%,  3.2%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::Rule as core[da82827a87f140f9]::fmt::Debug>::fmt
     3185 (0.2%,  3.3%)      1 (0.0%,  0.0%)  <&str as core[da82827a87f140f9]::convert::From<&ruff[fa0f2e8ef07114da]::codes::Rule>>::from
     3185 (0.2%,  3.5%)      1 (0.0%,  0.0%)  <&str as core[da82827a87f140f9]::convert::From<ruff[fa0f2e8ef07114da]::codes::Rule>>::from
     3185 (0.2%,  3.7%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::Rule as core[da82827a87f140f9]::convert::AsRef<str>>::as_ref
     3183 (0.2%,  3.9%)      1 (0.0%,  0.0%)  <ruff[fa0f2e8ef07114da]::codes::RuleIter>::get
     2718 (0.2%,  4.0%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::settings::options::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_seq::<toml_edit[de4ca26332d39787]:🇩🇪:array::ArraySeqAccess>
     2706 (0.2%,  4.2%)      1 (0.0%,  0.0%)  <&ruff[fa0f2e8ef07114da]::codes::Pylint as core[da82827a87f140f9]::iter::traits::collect::IntoIterator>::into_iter
     2573 (0.1%,  4.3%)      1 (0.0%,  0.0%)  <<ruff[fa0f2e8ef07114da]::rules::isort::settings::Options as serde[1a28808d63625aed]:🇩🇪:Deserialize>::deserialize::__Visitor as serde[1a28808d63625aed]:🇩🇪:Visitor>::visit_map::<toml_edit[de4ca26332d39787]:🇩🇪:spanned::SpannedDeserializer<toml_edit[de4ca26332d39787]:🇩🇪:value::ValueDeserializer>>
```
I didn't measure the effect on binary size this time.

## Testing

`cargo test` which uses this to generate the schema didn't change
2023-06-18 12:39:06 +02:00
Evan Rittenhouse 653a0ebf2d
Add Applicability to pyupgrade (#5162)
## Summary

Fixes some of #4184.
2023-06-17 19:33:11 +00:00
Evan Rittenhouse 95448ba669
Add Applicability to isort (#5161)
## Summary

Fixes some of #4184.
2023-06-17 19:08:11 +00:00
Charlie Marsh f18e10183f
Add some minor tweaks to latest docs (#5164) 2023-06-17 17:04:50 +00:00
Tom Kuson 98920909c6
Complete documentation for `flake8-blind-except` and `flake8-raise` rules (#5143)
## Summary

Completes the documentation for the `flake8-blind-except` and
`flake8-raise` rules.

Related to #2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-17 12:56:27 -04:00
Evan Rittenhouse e1e1d2d341
Add Applicability to flynt (#5160)
## Summary

Fixes some of #4184.
2023-06-17 12:05:43 -04:00
David Szotten 4b9b6829dc
format StmtBreak (#5158)
## Summary

format `StmtBreak`

trying to learn how to help out with the formatter. starting simple

## Test Plan

new snapshot test
2023-06-17 10:31:29 +02:00
Charlie Marsh d0ad1ed0af
Replace static `CallPath` vectors with `matches!` macros (#5148)
## Summary

After #5140, I audited the codebase for similar patterns (defining a
list of `CallPath` entities in a static vector, then looping over them
to pattern-match). This PR migrates all other such cases to use `match`
and `matches!` where possible.

There are a few benefits to this:

1. It more clearly denotes the intended semantics (branches are
exclusive).
2. The compiler can help deduplicate the patterns and detect unreachable
branches.
3. Performance: in the benchmark below, the all-rules performance is
increased by nearly 10%...

## Benchmarks

I decided to benchmark against a large file in the Airflow repository
with a lot of type annotations
([`views.py`](https://raw.githubusercontent.com/apache/airflow/f03f73100e8a7d6019249889de567cb00e71e457/airflow/www/views.py)):

```
linter/default-rules/airflow/views.py
                        time:   [10.871 ms 10.882 ms 10.894 ms]
                        thrpt:  [19.739 MiB/s 19.761 MiB/s 19.781 MiB/s]
                 change:
                        time:   [-2.7182% -2.5687% -2.4204%] (p = 0.00 < 0.05)
                        thrpt:  [+2.4805% +2.6364% +2.7942%]
                        Performance has improved.

linter/all-rules/airflow/views.py
                        time:   [24.021 ms 24.038 ms 24.062 ms]
                        thrpt:  [8.9373 MiB/s 8.9461 MiB/s 8.9527 MiB/s]
                 change:
                        time:   [-8.9537% -8.8516% -8.7527%] (p = 0.00 < 0.05)
                        thrpt:  [+9.5923% +9.7112% +9.8342%]
                        Performance has improved.
Found 12 outliers among 100 measurements (12.00%)
  5 (5.00%) high mild
  7 (7.00%) high severe
```

The impact is dramatic -- nearly a 10% improvement for `all-rules`.
2023-06-16 17:34:42 +00:00
Charlie Marsh b3240dbfa2
Avoid propagating `BindingKind::Global` and `BindingKind::Nonlocal` (#5136)
## Summary

This PR fixes a small quirk in the semantic model. Typically, when we
see an import, like `import foo`, we create a `BindingKind::Importation`
for it. However, if `foo` has been declared as a `global`, then we
propagate the kind forward. So given:

```python
global foo

import foo
```

We'd create two bindings for `foo`, both with type `global`.

This was originally borrowed from Pyflakes, and it exists to help avoid
false-positives like:

```python
def f():
    global foo

    # Don't mark `foo` as "assigned but unused"! It's a global!
    foo = 1
```

This PR removes that behavior, and instead tracks "Does this binding
refer to a global?" as a flag. This is much cleaner, since it means we
don't "lose" the identity of various bindings.

As a very strange example of why this matters, consider:

```python
def foo():
    global Member

    from module import Member

    x: Member = 1
```

`Member` is only used in a typing context, so we should flag it and say
"move it to a `TYPE_CHECKING` block". However, when we go to analyze
`from module import Member`, it has `BindingKind::Global`. So we don't
even know that it's an import!
2023-06-16 11:06:59 -04:00
Charlie Marsh fd1dfc3bfa
Add support for global and nonlocal symbol renames (#5134)
## Summary

In #5074, we introduced an abstraction to support local symbol renames
("local" here refers to "within a module"). However, that abstraction
didn't support `global` and `nonlocal` symbols. This PR extends it to
those cases.

Broadly, there are considerations.

First, if we're renaming a symbol in a scope in which it is declared
`global` or `nonlocal`. For example, given:

```python
x = 1

def foo():
    global x
```

Then when renaming `x` in `foo`, we need to detect that it's `global`
and instead perform the rename starting from the module scope.

Second, when renaming a symbol, we need to determine the scopes in which
it is declared `global` or `nonlocal`. This is effectively the inverse
of the above: when renaming `x` in the module scope, we need to detect
that we should _also_ rename `x` in `foo`.

To support these cases, the renaming algorithm was adjusted as follows:

- When we start a rename in a scope, determine whether the symbol is
declared `global` or `nonlocal` by looking for a `global` or `nonlocal`
binding. If it is, start the rename in the defining scope. (This
requires storing the defining scope on the `nonlocal` binding, which is
new.)
- We then perform the rename in the defining scope.
- We then check whether the symbol was declared as `global` or
`nonlocal` in any scopes, and perform the rename in those scopes too.
(Thankfully, this doesn't need to be done recursively.)

Closes #5092.

## Test Plan

Added some additional snapshot tests.
2023-06-16 14:35:10 +00:00
Charlie Marsh b9754bd5c5
Add autofix for `Set`-to-`AbstractSet` rewrite using reference tracking (#5074)
## Summary

This PR enables autofix behavior for the `flake8-pyi` rule that asks you
to alias `Set` to `AbstractSet` when importing `collections.abc.Set`.
It's not the most important rule, but it's a good isolated test-case for
local symbol renaming.

The renaming algorithm is outlined in-detail in the `renamer.rs` module.
But to demonstrate the behavior, here's the diff when running this fix
over a complex file that exercises a few edge cases:

```diff
--- a/foo.pyi
+++ b/foo.pyi
@@ -1,16 +1,16 @@
 if True:
-    from collections.abc import Set
+    from collections.abc import Set as AbstractSet
 else:
-    Set = 1
+    AbstractSet = 1

-x: Set = set()
+x: AbstractSet = set()

-x: Set
+x: AbstractSet

-del Set
+del AbstractSet

 def f():
-    print(Set)
+    print(AbstractSet)

     def Set():
         pass
```

Making this work required resolving a bunch of edge cases in the
semantic model that were causing us to "lose track" of references. For
example, the above wasn't possible with our previous approach to
handling deletions (#5071). Similarly, the `x: Set` "delayed annotation"
tracking was enabled via #5070. And many of these edits would've failed
if we hadn't changed `BindingKind` to always match the identifier range
(#5090). So it's really the culmination of a bunch of changes over the
course of the week.

The main outstanding TODO is that this doesn't support `global` or
`nonlocal` usages. I'm going to take a look at that tonight, but I'm
comfortable merging this as-is.

Closes #1106.

Closes #5091.
2023-06-16 14:12:33 +00:00
Charlie Marsh 307f7a735c
Avoid allocations in lowercase comparisons (#5137)
## Summary

I noticed that we have a few hot comparisons that involve called
`s.to_lowercase()`. We can avoid an allocation by comparing characters
directly.
2023-06-16 08:57:43 -04:00
Charlie Marsh 3af9dfeb0a
Rewrite `suspicious_function_call` as a match statement (#5140)
## Summary

@konstin mentioned that in profiling, this function accounted for a
non-trivial amount of time (0.33% of total execution, the most of any
rule). This PR attempts to rewrite it as a match statement for better
performance over a looping comparison.

## Test Plan

`cargo test`
2023-06-16 08:57:20 -04:00
Charlie Marsh 5526699535
Use const-singleton helpers in more rules (#5142) 2023-06-16 04:28:35 +00:00
Charlie Marsh fab2a4adf7
Use `matches!` for insecure hash rule (#5141) 2023-06-16 04:18:32 +00:00
Charlie Marsh 13813dc1b1
Skip `DJ008` enforcement in stub files (#5139)
Closes #5138.
2023-06-16 03:49:40 +00:00
Charlie Marsh 70c01257ca
Minor formatting changes to `Checker` (#5135) 2023-06-15 22:42:21 -04:00
Evan Rittenhouse 26d19655db
Add Applicability to flake8_tidy_imports (#5131)
## Summary
Fixes some of https://github.com/astral-sh/ruff/issues/4184
2023-06-15 18:09:00 -04:00
Charlie Marsh 1f856aa576
Don't treat straight imports of __future__ as `__future__` imports (#5128)
## Summary

If you `import __future__`, it's not subject to the same rules as `from
__future__ import feature` -- i.e., this is fine:

```python
x = 1

import __future__
```

It doesn't really make sense to treat these as `__future__` imports
(though I can't imagine anyone ever does this anyway).
2023-06-15 20:53:02 +00:00
Evan Rittenhouse 1e383483f7
Add Applicability to flake8_quotes fixes (#5130)
## Summary

Fixes some of #4184
2023-06-15 16:50:54 -04:00
Evan Rittenhouse 89b328c6be
Add Applicability to flake8_logging_format fixes (#5129)
## Summary

Fixes some of #4184
2023-06-15 16:50:19 -04:00
Evan Rittenhouse 6143065fc2
Add Applicability to flake8_comma fixes (#5127)
## Summary

Fixes some of #4184
2023-06-15 16:49:54 -04:00
Charlie Marsh 107a295af4
Allow `async with` in `redefined-loop-name` (#5125)
## Summary

Closes #5124.
2023-06-15 15:00:19 -04:00
Charlie Marsh 5ea3e42513
Always use identifier ranges to store bindings (#5110)
## Summary

At present, when we store a binding, we include a `TextRange` alongside
it. The `TextRange` _sometimes_ matches the exact range of the
identifier to which the `Binding` is linked, but... not always.

For example, given:

```python
x = 1
```

The binding we create _will_ use the range of `x`, because the left-hand
side is an `Expr::Name`, which has a valid range on it.

However, given:

```python
try:
  pass
except ValueError as e:
  pass
```

When we create a binding for `e`, we don't have a `TextRange`... The AST
doesn't give us one. So we end up extracting it via lexing.

This PR extends that pattern to the rest of the binding kinds, to ensure
that whenever we create a binding, we always use the range of the bound
name. This leads to better diagnostics in cases like pattern matching,
whereby the diagnostic for "unused variable `x`" here used to include
`*x`, instead of just `x`:

```python
def f(provided: int) -> int:
    match provided:
        case [_, *x]:
            pass
```

This is _also_ required for symbol renames, since we track writes as
bindings -- so we need to know the ranges of the bound symbols.

By storing these bindings precisely, we can also remove the
`binding.trimmed_range` abstraction -- since bindings already use the
"trimmed range".

To implement this behavior, I took some of our existing utilities (like
the code we had for `except ValueError as e` above), migrated them from
a full lexer to a zero-allocation lexer that _only_ identifies
"identifiers", and moved the behavior into a trait, so we can now do
`stmt.identifier(locator)` to get the range for the identifier.

Honestly, we might end up discarding much of this if we decide to put
ranges on all identifiers
(https://github.com/astral-sh/RustPython-Parser/pull/8). But even if we
do, this will _still_ be a good change, because the lexer introduced
here is useful beyond names (e.g., we use it find the `except` keyword
in an exception handler, to find the `else` after a `for` loop, and so
on). So, I'm fine committing this even if we end up changing our minds
about the right approach.

Closes #5090.

## Benchmarks

No significant change, with one statistically significant improvement
(-2.1654% on `linter/all-rules/large/dataset.py`):

```
linter/default-rules/numpy/globals.py
                        time:   [73.922 µs 73.955 µs 73.986 µs]
                        thrpt:  [39.882 MiB/s 39.898 MiB/s 39.916 MiB/s]
                 change:
                        time:   [-0.5579% -0.4732% -0.3980%] (p = 0.00 < 0.05)
                        thrpt:  [+0.3996% +0.4755% +0.5611%]
                        Change within noise threshold.
Found 6 outliers among 100 measurements (6.00%)
  4 (4.00%) low severe
  1 (1.00%) low mild
  1 (1.00%) high mild
linter/default-rules/pydantic/types.py
                        time:   [1.4909 ms 1.4917 ms 1.4926 ms]
                        thrpt:  [17.087 MiB/s 17.096 MiB/s 17.106 MiB/s]
                 change:
                        time:   [+0.2140% +0.2741% +0.3392%] (p = 0.00 < 0.05)
                        thrpt:  [-0.3380% -0.2734% -0.2136%]
                        Change within noise threshold.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe
linter/default-rules/numpy/ctypeslib.py
                        time:   [688.97 µs 691.34 µs 694.15 µs]
                        thrpt:  [23.988 MiB/s 24.085 MiB/s 24.168 MiB/s]
                 change:
                        time:   [-1.3282% -0.7298% -0.1466%] (p = 0.02 < 0.05)
                        thrpt:  [+0.1468% +0.7351% +1.3461%]
                        Change within noise threshold.
Found 15 outliers among 100 measurements (15.00%)
  1 (1.00%) low mild
  2 (2.00%) high mild
  12 (12.00%) high severe
linter/default-rules/large/dataset.py
                        time:   [3.3872 ms 3.4032 ms 3.4191 ms]
                        thrpt:  [11.899 MiB/s 11.954 MiB/s 12.011 MiB/s]
                 change:
                        time:   [-0.6427% -0.2635% +0.0906%] (p = 0.17 > 0.05)
                        thrpt:  [-0.0905% +0.2642% +0.6469%]
                        No change in performance detected.
Found 20 outliers among 100 measurements (20.00%)
  1 (1.00%) low severe
  2 (2.00%) low mild
  4 (4.00%) high mild
  13 (13.00%) high severe

linter/all-rules/numpy/globals.py
                        time:   [148.99 µs 149.21 µs 149.42 µs]
                        thrpt:  [19.748 MiB/s 19.776 MiB/s 19.805 MiB/s]
                 change:
                        time:   [-0.7340% -0.5068% -0.2778%] (p = 0.00 < 0.05)
                        thrpt:  [+0.2785% +0.5094% +0.7395%]
                        Change within noise threshold.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) low mild
  1 (1.00%) high severe
linter/all-rules/pydantic/types.py
                        time:   [3.0362 ms 3.0396 ms 3.0441 ms]
                        thrpt:  [8.3779 MiB/s 8.3903 MiB/s 8.3997 MiB/s]
                 change:
                        time:   [-0.0957% +0.0618% +0.2125%] (p = 0.45 > 0.05)
                        thrpt:  [-0.2121% -0.0618% +0.0958%]
                        No change in performance detected.
Found 11 outliers among 100 measurements (11.00%)
  1 (1.00%) low severe
  3 (3.00%) low mild
  5 (5.00%) high mild
  2 (2.00%) high severe
linter/all-rules/numpy/ctypeslib.py
                        time:   [1.6879 ms 1.6894 ms 1.6909 ms]
                        thrpt:  [9.8478 MiB/s 9.8562 MiB/s 9.8652 MiB/s]
                 change:
                        time:   [-0.2279% -0.0888% +0.0436%] (p = 0.18 > 0.05)
                        thrpt:  [-0.0435% +0.0889% +0.2284%]
                        No change in performance detected.
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) low mild
  1 (1.00%) high severe
linter/all-rules/large/dataset.py
                        time:   [7.1520 ms 7.1586 ms 7.1654 ms]
                        thrpt:  [5.6777 MiB/s 5.6831 MiB/s 5.6883 MiB/s]
                 change:
                        time:   [-2.5626% -2.1654% -1.7780%] (p = 0.00 < 0.05)
                        thrpt:  [+1.8102% +2.2133% +2.6300%]
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) low mild
  1 (1.00%) high mild
```
2023-06-15 18:43:19 +00:00
konstin 66089e1a2e
Fix a number of formatter errors from the cpython repository (#5089)
## Summary

This fixes a number of problems in the formatter that showed up with
various files in the [cpython](https://github.com/python/cpython)
repository. These problems surfaced as unstable formatting and invalid
code. This is not the entirety of problems discovered through cpython,
but a big enough chunk to separate it. Individual fixes are generally
individual commits. They were discovered with #5055, which i update as i
work through the output

## Test Plan

I added regression tests with links to cpython for each entry, except
for the two stubs that also got comment stubs since they'll be
implemented properly later.
2023-06-15 11:24:14 +00:00
Dhruv Manilawala 097823b56d
Ability to perform integration test on Jupyter notebooks (#5076)
## Summary

Ability to perform integration test on Jupyter notebooks

Part of #1218

## Test Plan

`cargo test`
2023-06-15 08:04:27 +05:30
Charlie Marsh ed8113267c
Add autofix specification levels for a variety of rules (#5109) 2023-06-14 22:03:37 -04:00
Charlie Marsh c654280d84
Use direct links for all PEP 8 references (#5108) 2023-06-14 21:12:23 -04:00
Charlie Marsh 99486b38f4
Disambiguate all Python documentation references (#5107) 2023-06-15 00:47:00 +00:00
Charlie Marsh 716cab2f19
Run `rustfmt` on nightly to clean up erroneous comments (#5106)
## Summary

This PR runs `rustfmt` with a few nightly options as a one-time fix to
catch some malformatted comments. I ended up just running with:

```toml
condense_wildcard_suffixes = true
edition = "2021"
max_width = 100
normalize_comments = true
normalize_doc_attributes = true
reorder_impl_items = true
unstable_features = true
use_field_init_shorthand = true
```

Since these all seem like reasonable things to fix, so may as well while
I'm here.
2023-06-15 00:19:05 +00:00
Charlie Marsh 9ab16fb417
Add `target-version` link to relevant rules (#5105) 2023-06-15 00:12:32 +00:00
Charlie Marsh 458beccf14
Uniformly put `## Options` at the end of documentation (#5104) 2023-06-15 00:04:51 +00:00
Tom Kuson ccbc863960
Complete `pyupgrade` documentation (#5096)
## Summary

Completes the documentation for the `pyupgrade` rules.

Related to #2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-14 23:43:12 +00:00
Charlie Marsh 71b3130ff1
Remove manual `await` detection (#5103)
We can just use `any_over_expr` instead.
2023-06-14 22:17:14 +00:00
Tom Kuson 08cd140ea6
Ignore `reimplemented-builtin` if in `async` context (#5101)
## Summary

Checks if `checker` is in an `async` context. If yes, return early.

Fixes #5098.

## Test Plan

`cargo test`
2023-06-14 18:00:30 -04:00
Charlie Marsh 848f184b8c
Enable UTC-import for `datetime-utc-alias` fix (#5100)
## Summary

Small update to leverage `get_or_import_symbol` to fix `UP017` in more
cases (e.g., when we need to import `UTC`, or access it from an alias or
something).

## Test Plan

Check out the updated snapshot.
2023-06-14 21:13:36 +00:00
Charlie Marsh 56476dfd61
Use `matches!` for `CallPath` comparisons (#5099)
## Summary

This PR consistently uses `matches! for static `CallPath` comparisons.
In some cases, we can significantly reduce the number of cases or
checks.

## Test Plan

`cargo test `
2023-06-14 17:06:34 -04:00
Charlie Marsh bae183b823
Rename `semantic_model` and `model` usages to `semantic` (#5097)
## Summary

As discussed in Discord, and similar to oxc, we're going to refer to
this as `.semantic()` everywhere.

While I was auditing usages of `model: &SemanticModel`, I also changed
as many function signatures as I could find to consistently take the
model as the _last_ argument, rather than the first.
2023-06-14 15:01:51 -04:00
Charlie Marsh 65dbfd2556
Improve names and documentation on scope API (#5095)
## Summary

Just minor improvements to improve consistency of method names and
availability.
2023-06-14 18:28:55 +00:00
Charlie Marsh 86ff1febea
Re-export `ruff_python_semantic` members (#5094)
## Summary

This PR adds a more unified public API to `ruff_python_semantic`, so
that we don't need to do deeply nested imports all over the place.
2023-06-14 18:23:38 +00:00
Charlie Marsh a33bbe6335
Track "delayed" annotations in the semantic model (#5070)
## Summary

This PR tackles a corner case that we'll need to support local symbol
renaming. It relates to a nuance in how we want handle annotations
(i.e., `AnnAssign` statements with no value, like `x: int` in a function
body).

When we see a statement like:

```python
x: int
```

We create a `BindingKind::Annotation` for `x`. This is a special
`BindingKind` that the resolver isn't allowed to return. For example,
given:

```python
x: int
print(x)
```

The second line will yield an `undefined-name` error.

So why does this `BindingKind` exist at all? In Pyflakes, to support the
`unused-annotation` lint:

```python
def f():
    x: int  # unused-annotation
```

If we don't track `BindingKind::Annotation`, we can't lint for unused
variables that are only "defined" via annotations.

There are a few other wrinkles to `BindingKind::Annotation`. One is
that, if a binding already exists in the scope, we actually just discard
the `BindingKind`. So in this case:

```python
x = 1
x: int
```

When we go to create the `BindingKind::Annotation` for the second
statement, we notice that (1) we're creating an annotation but (2) the
scope already has binding for the name -- so we just drop the binding on
the floor. This has the nice property that annotations aren't considered
to "shadow" another binding, which is important in a bunch of places
(e.g., if we have `import os; os: int`, we still consider `os` to be an
import, as we should). But it also means that these "delayed"
annotations are one of the few remaining references that we don't track
anywhere in the semantic model.

This PR adds explicit support for these via a new `delayed_annotations`
attribute on the semantic model. These should be extremely rare, but we
do need to track them if we want to support local symbol renaming.

### This isn't the right way to model this

This isn't the right way to model this.

Here's an alternative:

- Remove `BindingKind::Annotation`, and treat annotations as their own,
separate concept.
- Instead of storing a map from name to `BindingId` on each `Scope`,
store a map from name to... `SymbolId`.
- Introduce a `Symbol` abstraction, where a symbol can point to a
current binding, and a list of annotations, like:

```rust
pub struct Symbol {
  binding: Option<BindingId>,
  annotations: Vec<AnnotationId>
}
```

If we did this, we could appropriately model the semantics described
above. When we go to resolve a binding, we ignore annotations (always).
When we try to find unused variables, we look through the list of
symbols, and have sufficient information to discriminate between
annotations and bound variables. Etc.

The main downside of this `Symbol`-based approach is that it's going to
take a lot more work to implement, and it'll be less performant (we'll
be storing more data per symbol, and our binding lookups will have an
added layer of indirection).
2023-06-14 17:54:35 +00:00
Charlie Marsh c992cfa76e
Make some of `ruff_python_semantic` `pub(crate)` (#5093) 2023-06-14 17:49:37 +00:00
konstin 916f0889f8
Add pyproject.toml to include option doc (#5080)
Fixes an oversight where i didn't update this initially
2023-06-14 15:55:12 +00:00
Charlie Marsh 732b0405d7
Remove `FixMode::None` (#5087)
## Summary

We now _always_ generate fixes, so `FixMode::None` and
`FixMode::Generate` are redundant. We can also remove the TODO around
`--fix-dry-run`, since that's our default behavior.

Closes #5081.
2023-06-14 11:17:09 -04:00
Thomas de Zeeuw e7316c1cc6
Consider ignore-names in all pep8 naming rules (#5079)
## Summary

This changes all remaining pep8 naming rules to consider the
`ingore-names` argument.

Closes #5050

## Test Plan

Added new tests.
2023-06-14 16:57:09 +02:00
Charlie Marsh 6f10aeebaa
Remove unused `Scope#delete` method (#5085)
## Summary

This is now intentionally unused and is now made impossible (via this
PR).
2023-06-14 14:15:14 +00:00
Charlie Marsh c74ef77e85
Move binding accesses into `SemanticModel` method (#5084) 2023-06-14 14:07:46 +00:00
Charlie Marsh 1e497162d1
Add a dedicated read result for unbound locals (#5083)
## Summary

Small follow-up to #4888 to add a dedicated `ResolvedRead` case for
unbound locals, mostly for clarity and documentation purposes (no
behavior changes).

## Test Plan

`cargo test`
2023-06-14 09:58:48 -04:00
Charlie Marsh aa41ffcfde
Add `BindingKind` variants to represent deleted bindings (#5071)
## Summary

Our current mechanism for handling deletions (e.g., `del x`) is to
remove the symbol from the scope's `bindings` table. This "does the
right thing", in that if we then reference a deleted symbol, we're able
to determine that it's unbound -- but it causes a variety of problems,
mostly in that it makes certain bindings and references unreachable
after-the-fact.

Consider:

```python
x = 1
print(x)
del x
```

If we analyze this code _after_ running the semantic model over the AST,
we'll have no way of knowing that `x` was ever introduced in the scope,
much less that it was bound to a value, read, and then deleted --
because we effectively erased `x` from the model entirely when we hit
the deletion.

In practice, this will make it impossible for us to support local symbol
renames. It also means that certain rules that we want to move out of
the model-building phase and into the "check dead scopes" phase wouldn't
work today, since we'll have lost important information about the source
code.

This PR introduces two new `BindingKind` variants to model deletions:

- `BindingKind::Deletion`, which represents `x = 1; del x`.
- `BindingKind::UnboundException`, which represents:

```python
try:
  1 / 0
except Exception as e:
  pass
```

In the latter case, `e` gets unbound after the exception handler
(assuming it's triggered), so we want to handle it similarly to a
deletion.

The main challenge here is auditing all of our existing `Binding` and
`Scope` usages to understand whether they need to accommodate deletions
or otherwise behave differently. If you look one commit back on this
branch, you'll see that the code is littered with `NOTE(charlie)`
comments that describe the reasoning behind changing (or not) each of
those call sites. I've also augmented our test suite in preparation for
this change over a few prior PRs.

### Alternatives

As an alternative, I considered introducing a flag to `BindingFlags`,
like `BindingFlags::UNBOUND`, and setting that at the appropriate time.

This turned out to be a much more difficult change, because we tend to
match on `BindingKind` all over the place (e.g., we have a bunch of code
blocks that only run when a `BindingKind` is
`BindingKind::Importation`). As a result, introducing these new
`BindingKind` variants requires only a few changes at the client sites.
Adding a flag would've required a much wider-reaching change.
2023-06-14 09:27:24 -04:00
Charlie Marsh fc6580592d
Use Expr::is_* methods at more call sites (#5075) 2023-06-14 04:02:39 +00:00
Tom Kuson 4d9b0b925d
Add documentation to `flake8-executable` rules (#5063)
## Summary

Completes the documentation for the `flake8-executable` rules.

Related to #2646.

## Test Plan

`python scripts/check_docs_formatted.py`
2023-06-14 01:31:06 +00:00
Charlie Marsh 0daeea1f42
Tweak exception-handler handling in AST visitor (#5069) 2023-06-14 01:00:42 +00:00
Charlie Marsh 3f6584b74f
Fix erroneous kwarg reference (#5068) 2023-06-14 00:01:52 +00:00
Charlie Marsh c2fa568b46
Use dedicated structs for excepthandler variants (#5065)
## Summary

Oversight from #5042.
2023-06-13 22:37:06 +00:00
Charlie Marsh 1895011ac2
Document some attributes on the semantic model (#5064) 2023-06-13 20:45:24 +00:00
Charlie Marsh 364bd82aee
Don't treat annotations as resolved in forward references (#5060)
## Summary

This behavior dates back to a Pyflakes commit (5fc37cbd), which was used
to allow this test to pass:

```py
from __future__ import annotations
T: object
def f(t: T): pass
def g(t: 'T'): pass
```

But, I think this is an error. Mypy and Pyright don't accept it -- you
can only use variables as type annotations if they're type aliases
(i.e., annotated with `TypeAlias`), in which case, there has to be an
assignment on the right-hand side (see: [PEP
613](https://peps.python.org/pep-0613/)).
2023-06-13 14:47:29 -04:00
Charlie Marsh f9f08d6b03
Add a few more tests for deletion behaviors (#5058) 2023-06-13 17:54:04 +00:00
Charlie Marsh b0984a2868
Treat exception binding as explicit deletion (#5057)
## Summary

This PR corrects a misunderstanding I had related to Python's handling
of bound exceptions.

Previously, I thought this code ran without error:

```py
def f():
    x = 1

    try:
        1 / 0
    except Exception as x:
        pass

    print(x)
```

My understanding was that `except Exception as x` bound `x` within the
`except` block, but then restored the `x = 1` binding after exiting the
block.

In practice, however, this throws a `UnboundLocalError` error, because
`x` becomes "unbound" after exiting the exception handler. It's similar
to a `del` statement in this way.

This PR removes our behavior to "restore" the previous binding. This
could lead to faulty analysis in conditional blocks due to our lack of
control flow analysis, but those same problems already exist for `del`
statements.
2023-06-13 13:45:51 -04:00
Charlie Marsh a431dd0368
Respect all `__all__` definitions for docstring visibility (#5052)
## Summary

We changed the semantics around `__all__` in #4885, but didn't update
the docstring visibility code to match those changes.
2023-06-13 12:22:20 -04:00
Charlie Marsh 099a9152d1
Use `.is_unbound()` in flake8-errmsg fix (#5053)
## Summary

Trying to bring some more consistent to these APIs as I look to change
them to accommodate deletions.
2023-06-13 12:22:05 -04:00
Charlie Marsh 19f972a305
Use `Scope#has` in lieu of `Scope#get` (#5051)
## Summary

These usages don't actually need the `BindingId`.
2023-06-13 15:59:53 +00:00
Thomas de Zeeuw b0f89fa814
Support glob patterns in pep8_naming ignore-names (#5024)
## Summary

 Support glob patterns in pep8_naming ignore-names.

Closes #2787

## Test Plan

Added new tests.
2023-06-13 17:37:13 +02:00
Charlie Marsh 65312bad01
Remove unannotated attributes from RUF008 (#5049)
## Summary

In a dataclass:

```py
from dataclasses import dataclass

@dataclass
class X:
    class_var = {}
    x: int
```

`class_var` isn't actually a dataclass attribute, since it's
unannotated. This PR removes such attributes from RUF008
(`mutable-dataclass-default`), but it does enforce them in RUF012
(`mutable-class-default`), since those should be annotated with
`ClassVar` like any other mutable class attribute.

Closes #5043.
2023-06-13 10:21:14 -04:00
Aarni Koskela 7b4dde0c6c
Add JSON Lines (NDJSON) message serialization (#5048)
## Summary

This adds `json-lines` (https://jsonlines.org/ or http://ndjson.org/) as
an output format.

I'm sure you already know, but

* JSONL is more greppable (each record is a single line) than the pretty
JSON
* JSONL is faster to ingest piecewise (and/or in parallel) than JSON

## Test Plan

Snapshot test in the new module :)
2023-06-13 14:15:55 +00:00
Thomas de Zeeuw e1fd3965a2
Start with Upper case in error messages (#5045)
## Summary

To be consistent with the format used by other errors.

## Test Plan

N/A.
2023-06-13 13:14:45 +02:00
konstin 95ee6dcb3b
Add contributor docs to formatter (#5023)
I've written done my condensed learnings from working on the formatter
so that others can have an easier start working on it.

This is a pure docs change
2023-06-13 07:22:17 +00:00
Charlie Marsh cc44349401
Use dedicated structs in `comparable.rs` (#5042)
## Summary

Updating to match the updated AST structure, for consistency.
2023-06-13 03:57:34 +00:00
qdegraaf a477720f4e
[`perflint`] Add `perflint` plugin, add first rule `PERF102` (#4821)
## Summary

Adds boilerplate for implementing the
[perflint](https://github.com/tonybaloney/perflint/) plugin, plus a
first rule.

## Test Plan

Fixture added for PER8102

## Issue link

Refers: https://github.com/charliermarsh/ruff/issues/4789
2023-06-13 01:54:44 +00:00
Charlie Marsh be2fa6d217
Increase density of `Checker` arms (#5041) 2023-06-13 01:08:23 +00:00
Charlie Marsh cbd4c10fdd
Support 'reason' argument to `pytest.fail` (#5040)
## Summary

Per the [API
reference](https://docs.pytest.org/en/7.1.x/reference/reference.html#pytest.fail),
`reason` was added in version 7, and is equivalent to `msg` (but
preferred going forward).

I also grepped for `msg` usages in `flake8_pytest_style`, but found no
others (apart from those that reference `unittest` APIs.)

Closes #3387.
2023-06-12 20:54:07 -04:00
Timofei Kukushkin e2130707f5
Autofixer for ISC001 (#4853)
## Summary

This PR adds autofixer for rule ISC001 in cases where both string
literals are of the same kind and with same quotes (double / single).

Fixes #4829

## Test Plan

I added testcases with different combinations of string literals.
2023-06-12 23:28:57 +00:00
Charlie Marsh 780336db0a
Include f-string prefixes in quote-stripping utilities (#5039)
Mentioned here:
https://github.com/astral-sh/ruff/pull/4853#discussion_r1217560348.

Generated with this hacky script:
https://gist.github.com/charliermarsh/8ecc4e55bc87d51dc27340402f33b348.
2023-06-12 18:25:47 -04:00
Charlie Marsh 7e37d8916c
Remove lexer dependency from identifier_range (#5036)
## Summary

We run this quite a bit -- the new version is zero-allocation, though
it's not quite as nice as the lexer we have in the formatter.
2023-06-12 22:06:03 +00:00
Charlie Marsh ab11dd08df
Improve `TypedDict` conversion logic for shadowed builtins and dunder methods (#5038)
## Summary

This PR (1) avoids flagging `TypedDict` and `NamedTuple` conversions
when attributes are dunder methods, like `__dict__`, and (2) avoids
flagging the `A003` shadowed-attribute rule for `TypedDict` classes at
all, where it doesn't really apply (since those attributes are only
accessed via subscripting anyway).

Closes #5027.
2023-06-12 21:23:39 +00:00
Charlie Marsh 4080f36850
Handle decorators in class-parenthesis-modifying rules (#5034)
## Summary

A few of our rules look at the parentheses that follow a class
definition (e.g., `class Foo(object):`) and attempt to modify those
parentheses. Neither of those rules were behaving properly in the
presence of decorators, which were recently added to the statement
range.

## Test Plan

`cargo test` with a variety of new fixture tests.
2023-06-12 15:19:59 -04:00
Charlie Marsh 6d861743c8
Remove custom tests in `rules/ruff/mod.rs` (#5033) 2023-06-12 18:54:04 +00:00
Charlie Marsh 54e103fc99
Add a rule to remove unnecessary parentheses in class definitions (#5032)
Closes #2409.
2023-06-12 18:43:06 +00:00
Dhruv Manilawala 3470dee7d4
Add rule to disallow implicit optional with autofix (#4831)
## Summary

Add rule to disallow implicit optional with autofix.

Currently, I've added it under `RUF` category.

### Limitation

Type aliases could result in false positive:

```python
from typing import Optional

StrOptional = Optional[str]


def foo(arg: StrOptional = None):
	pass
```

## Test Plan

`cargo test`

resolves: #1983

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-06-12 18:12:10 +00:00
Dhruv Manilawala cb4f086cbf
Add roundtrip support for Jupyter notebook (#5028)
## Summary

Add roundtrip support for Jupyter notebook.

1. Read the notebook
2. Extract out the source code content
3. Use it to update the notebook itself (should be exactly the same [^1])
4. Serialize into JSON and print it to stdout

## Test Plan

`cargo run --all-features --bin ruff_dev --package ruff_dev --
round-trip <path/to/notebook.ipynb>`

<details><summary>Example output:</summary>
<p>

```
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f3c286e9-fa52-4440-816f-4449232f199a",
   "metadata": {},
   "source": [
    "# Ruff Test"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2b7bc6c-778a-4b07-86ae-dde5a2d9511e",
   "metadata": {},
   "source": [
    "Markdown block before the first import"
   ]
  },
  {
   "cell_type": "code",
   "id": "5e3ef98e-224c-450a-80e6-be442ad50907",
   "metadata": {
    "tags": []
   },
   "source": "",
   "execution_count": 1,
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "6bced3f8-e0a4-450c-ae7c-f60ad5671ee9",
   "metadata": {},
   "source": "import contextlib\n\nwith contextlib.suppress(ValueError):\n    print()\n",
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "d7102cfd-5bb5-4f5b-a3b8-07a7b8cca34c",
   "metadata": {},
   "source": "import random\n\nrandom.randint(10, 20)",
   "outputs": []
  },
  {
   "cell_type": "code",
   "id": "88471d1c-7429-4967-898f-b0088fcb4c53",
   "metadata": {},
   "source": "foo = 1\nif foo < 2:\n    msg = f\"Invalid foo: {foo}\"\n    raise ValueError(msg)",
   "outputs": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (ruff-playground)",
   "name": "ruff-playground",
   "language": "python"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python",
   "version": "3.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
```

</p>
</details> 

[^1]: The type in JSON might be different (https://github.com/astral-sh/ruff/pull/4665#discussion_r1212663495)

Part of #1218
2023-06-12 23:27:45 +05:30
Charlie Marsh a77d2df934
Split mutable-class-defaults rules into separate modules (#5031) 2023-06-12 17:21:28 +00:00
Adam Pauls 638c18f007
Expand RUF008 to all classes, but to a new code (RUF012) (#4390)
AFAIK, there is no reason to limit RUF008 to just dataclasses -- mutable
defaults have the same problems for regular classes.

Partially addresses https://github.com/charliermarsh/ruff/issues/4053
and broken out from https://github.com/charliermarsh/ruff/pull/4096.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-06-12 16:54:27 +00:00
Addison Crump 70e6c212d9
Improve ruff_parse_simple to find UTF-8 violations (#5008)
Improves the `ruff_parse_simple` fuzz harness by adding checks for
parsed locations to ensure they all lie on UTF-8 character boundaries.
This will allow for faster identification of issues like #5004.

This also adds additional details for Apple M1 users and clarifies the
importance of using `init-fuzzer.sh` (thanks for the feedback,
@jasikpark 🙂).
2023-06-12 12:10:23 -04:00
Charlie Marsh 9db622afe1
Allow `Options`-to-`Settings` conversion to use `TryFrom` (#5025)
## Summary

This avoids a bad `expect()` call in the `copyright` conversion.

## Test Plan

`cargo test`
2023-06-12 15:31:50 +00:00
Thomas de Zeeuw d3aa81a474
Suggest combining async with statements (#5022)
## Summary

Previously the rule for SIM117 explicitly ignored `async with`
statements as it would incorrectly suggestion to merge `async with` and
regular `with` statements as reported in issue #1902.

This partially reverts the fix for that (commit
396be5edea) by enabling the rules for
`async with` statements again, but with a check ensuring that the
statements are both of the same kind, i.e. both `async with` or both
(just) `with` statements.

Closes #3025

## Test Plan

Updated and existing test and added a new test case from #3025.
2023-06-12 16:33:18 +02:00
Dhruv Manilawala d8f5d2d767
Add support for auto-fix in Jupyter notebooks (#4665)
## Summary

Add support for applying auto-fixes in Jupyter Notebook.

### Solution

Cell offsets are the boundaries for each cell in the concatenated source
code. They are represented using `TextSize`. It includes the start and
end offset as well, thus creating a range for each cell. These offsets
are updated using the `SourceMap` markers.

### SourceMap

`SourceMap` contains markers constructed from each edits which tracks
the original source code position to the transformed positions. The
following drawing might make it clear:

![SourceMap visualization](https://github.com/astral-sh/ruff/assets/67177269/3c94e591-70a7-4b57-bd32-0baa91cc7858)

The center column where the dotted lines are present are the markers
included in the `SourceMap`. The `Notebook` looks at these markers and
updates the cell offsets after each linter loop. If you notice closely,
the destination takes into account all of the markers before it.

The index is constructed only when required as it's only used to render
the diagnostics. So, a `OnceCell` is used for this purpose. The cell
offsets, cell content and the index will be updated after each iteration
of linting in the mentioned order. The order is important here as the
content is updated as per the new offsets and index is updated as per
the new content.

## Limitations

### 1

Styling rules such as the ones in `pycodestyle` will not be applicable
everywhere in Jupyter notebook, especially at the cell boundaries. Let's
take an example where a rule suggests to have 2 blank lines before a
function and the cells contains the following code:

```python
import something
# ---
def first():
	pass

def second():
	pass
```

(Again, the comment is only to visualize cell boundaries.)

In the concatenated source code, the 2 blank lines will be added but it
shouldn't actually be added when we look in terms of Jupyter notebook.
It's as if the function `first` is at the start of a file.

`nbqa` solves this by recording newlines before and after running
`autopep8`, then running the tool and restoring the newlines at the end
(refer https://github.com/nbQA-dev/nbQA/pull/807).

## Test Plan

Three commands were run in order with common flags (`--select=ALL
--no-cache --isolated`) to isolate which stage the problem is occurring:
1. Only diagnostics
2. Fix with diff (`--fix --diff`)
3. Fix (`--fix`)

### https://github.com/facebookresearch/segment-anything

```
-------------------------------------------------------------------------------
 Jupyter Notebooks       3            0            0            0            0
 |- Markdown             3           98            0           94            4
 |- Python               3          513          468            4           41
 (Total)                            611          468           98           45
-------------------------------------------------------------------------------
```

```console
$ cargo run --all-features --bin ruff -- check --no-cache --isolated --select=ALL /path/to/segment-anything/**/*.ipynb --fix
...
Found 180 errors (89 fixed, 91 remaining).
```

### https://github.com/openai/openai-cookbook

```
-------------------------------------------------------------------------------
 Jupyter Notebooks      65            0            0            0            0
 |- Markdown            64         3475           12         2507          956
 |- Python              65         9700         7362         1101         1237
 (Total)                          13175         7374         3608         2193
===============================================================================
```

```console
$ cargo run --all-features --bin ruff -- check --no-cache --isolated --select=ALL /path/to/openai-cookbook/**/*.ipynb --fix
error: Failed to parse /path/to/openai-cookbook/examples/vector_databases/Using_vector_databases_for_embeddings_search.ipynb:cell 4:29:18: unexpected token '-'
...
Found 4227 errors (2165 fixed, 2062 remaining).
```

### https://github.com/tensorflow/docs

```
-------------------------------------------------------------------------------
 Jupyter Notebooks     150            0            0            0            0
 |- Markdown             1           55            0           46            9
 |- Python               1          402          289           60           53
 (Total)                            457          289          106           62
-------------------------------------------------------------------------------
```

```console
$ cargo run --all-features --bin ruff -- check --no-cache --isolated --select=ALL /path/to/tensorflow-docs/**/*.ipynb --fix
error: Failed to parse /path/to/tensorflow-docs/site/en/guide/extension_type.ipynb:cell 80:1:1: unexpected token Indent
error: Failed to parse /path/to/tensorflow-docs/site/en/r1/tutorials/eager/custom_layers.ipynb:cell 20:1:1: unexpected token Indent
error: Failed to parse /path/to/tensorflow-docs/site/en/guide/data.ipynb:cell 175:5:14: unindent does not match any outer indentation level
error: Failed to parse /path/to/tensorflow-docs/site/en/r1/tutorials/representation/unicode.ipynb:cell 30:1:1: unexpected token Indent
...
Found 12726 errors (5140 fixed, 7586 remaining).
```

### https://github.com/tensorflow/models

```
-------------------------------------------------------------------------------
 Jupyter Notebooks      46            0            0            0            0
 |- Markdown             1           11            0            6            5
 |- Python               1          328          249           19           60
 (Total)                            339          249           25           65
-------------------------------------------------------------------------------
```

```console
$ cargo run --all-features --bin ruff -- check --no-cache --isolated --select=ALL /path/to/tensorflow-models/**/*.ipynb --fix
...
Found 4856 errors (2690 fixed, 2166 remaining).
```

resolves: #1218
fixes: #4556
2023-06-12 14:14:15 +00:00
konstin e586c27590
Format ExprTuple (#4963)
This implements formatting ExprTuple, including magic trailing comma. I
intentionally didn't change the settings mechanism but just added a
dummy global const flag.

Besides the snapshots, I added custom breaking/joining tests and a
deeply nested test case. The diffs look better than previously, proper
black compatibility depends on parentheses handling.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2023-06-12 12:55:47 +00:00
Thomas de Zeeuw 8161757229
[flake8-pyi] Implement PYI044 (#5021)
## Summary

This implements PYI044. This rule checks if `from __future__ import
annotations` is used in stub files as it has no effect in stub files, since type
checkers automatically treat stubs as having those semantics.

Updates https://github.com/astral-sh/ruff/issues/848

## Test Plan

Added a test case and snapshots.
2023-06-12 13:20:16 +02:00
Charlie Marsh 6a5f317362
Use `use::*` for rule re-exports (#5018) 2023-06-12 00:32:45 +00:00
Dhruv Manilawala c3d1fa851e
Ignore pyproject.toml for adding noqa directives (#5013)
## Summary

Ignore pyproject.toml file for adding noqa directives using `--add-noqa`

## Test Plan

`cargo run --bin ruff -- check --add-noqa .`

fixes: #5012
2023-06-11 20:21:24 -04:00
Charlie Marsh eac3a0cc3d
Update CONTRIBUTING.md guide (#5017) 2023-06-12 00:20:59 +00:00
Charlie Marsh 68b6d30c46
Use consistent `Cargo.toml` metadata in all crates (#5015) 2023-06-12 00:02:40 +00:00
Ryan Yang ab3c02342b
Implement copyright notice detection (#4701)
## Summary

Add copyright notice detection to enforce the presence of copyright
headers in Python files.

Configurable settings include: the relevant regular expression, the
author name, and the minimum file size, similar to
[flake8-copyright](https://github.com/savoirfairelinux/flake8-copyright).

Closes https://github.com/charliermarsh/ruff/issues/3579

---------

Signed-off-by: ryan <ryang@waabi.ai>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-06-11 02:17:58 +00:00
Trevor Gross 9f7cc86a22
Add more details to E722 (bare-except) docs (#5007)
## Summary

Note that catching a bare `Exception` is better than catching no
specific exception.

## Test Plan

Documentation only.
2023-06-10 18:42:43 -04:00
Charlie Marsh 445e1723ab
Use `Stmt::parse` in lieu of `Suite` unwraps (#5002) 2023-06-10 04:55:31 +00:00
Charlie Marsh 42c8054268
Implement autofix for revised `RET504` rule (#4999)
## Summary

This PR enables autofix for the revised `RET504` rule, by changing:

```py
def f():
    x = 1
    return x
```

...to:

```py
def f():
    return 1
```

Closes #2263.

Closes #2788.
2023-06-10 04:32:03 +00:00
Charlie Marsh 2d597bc1fb
Parenthesize expressions prior to lexing in F632 (#5001) 2023-06-10 04:23:43 +00:00
Charlie Marsh 7275c16d98
Extend revised `RET504` implementation to `with` statements (#4998)
## Summary

This PR extends the new `RET504` implementation to handle cases like:

```py
def foo():
    with open("foo.txt", "r") as f:
        x = f.read()
    return x
```

This was originally suggested in
https://github.com/astral-sh/ruff/issues/2950#issuecomment-1433441503.
2023-06-10 04:15:35 +00:00
Charlie Marsh 02b8ce82af
Refactor `RET504` to only enforce assignment-then-return pattern (#4997)
## Summary

The `RET504` rule, which looks for unnecessary assignments before return
statements, is a frequent source of issues (#4173, #4236, #4242, #1606,
#2950). Over time, we've tried to refine the logic to handle more cases.
For example, we now avoid analyzing any functions that contain any
function calls or attribute assignments, since those operations can
contain side effects (and so we mark them as a "read" on all variables
in the function -- we could do a better job with code graph analysis to
handle this limitation, but that'd be a more involved change.) We also
avoid flagging any variables that are the target of multiple
assignments. Ultimately, though, I'm not happy with the implementation
-- we just can't do sufficiently reliable analysis of arbitrary code
flow given the limited logic herein, and the existing logic is very hard
to reason about and maintain.

This PR refocuses the rule to only catch cases of the form:

```py
def f():
    x = 1
    return x
```

That is, we now only flag returns that are immediately preceded by an
assignment to the returned variable. While this is more limiting, in
some ways, it lets us flag more cases vis-a-vis the previous
implementation, since we no longer "fully eject" when functions contain
function calls and other effect-ful operations.

Closes #4173.

Closes #4236.

Closes #4242.
2023-06-10 00:05:01 -04:00
Charlie Marsh 5abb8ec0dc
Use Python whitespace utilities in `ruff_textwrap` (#4996)
## Summary

This change was intended to be included in #4994, but was somehow
dropped.
2023-06-10 02:32:42 +00:00
Charlie Marsh f401050878
Introduce `PythonWhitespace` to confine trim operations to Python whitespace (#4994)
## Summary

We use `.trim()` and friends in a bunch of places, to strip whitespace
from source code. However, not all Unicode whitespace characters are
considered "whitespace" in Python, which only supports the standard
space, tab, and form-feed characters.

This PR audits our usages of `.trim()`, `.trim_start()`, `.trim_end()`,
and `char::is_whitespace`, and replaces them as appropriate with a new
`.trim_whitespace()` analogues, powered by a `PythonWhitespace` trait.

In general, the only place that should continue to use `.trim()` is
content within docstrings, which don't need to adhere to Python's
semantic definitions of whitespace.

Closes #4991.
2023-06-09 21:44:50 -04:00
Charlie Marsh c1ac50093c
Use super visibility in helpers (#4995) 2023-06-10 01:23:13 +00:00
Charlie Marsh 1d756dc3a7
Move Python whitespace utilities into new `ruff_python_whitespace` crate (#4993)
## Summary

`ruff_newlines` becomes `ruff_python_whitespace`, and includes the
existing "universal newline" handlers alongside the Python
whitespace-specific utilities.
2023-06-10 00:59:57 +00:00
Charlie Marsh e86f12a1ec
Rename some methods on `SemanticModel` (#4990) 2023-06-09 19:36:59 +00:00
Charlie Marsh 5c502a3320
Add documentation for `BindingKind` variants (#4989) 2023-06-09 18:32:50 +00:00
Micha Reiser 901bcb6f21
Fix line numbers in source frames (#4984) 2023-06-09 17:21:18 +02:00
Micha Reiser 111e1f93ca
perf(formatter): Skip bodies without comments (#4978) 2023-06-09 11:33:57 +02:00
Micha Reiser 68d52da43b
Track formatted comments (#4979) 2023-06-09 09:09:45 +00:00
Micha Reiser 646ab64850
Fix binary expression formatting with leading comments (#4964) 2023-06-09 09:02:50 +00:00
Micha Reiser 1accbeffd6
Format `if` statements (#4961) 2023-06-09 10:55:14 +02:00
Charlie Marsh 16d1e63a5e
Respect 'is not' operators split across newlines (#4977) 2023-06-09 05:07:45 +00:00
Charlie Marsh d647105e97
Support concatenated string key removals (#4976) 2023-06-09 04:56:35 +00:00
Davide Canton 63fdcea29e
Handled dict and set inside f-string (#4249) (#4563) 2023-06-09 04:53:13 +00:00
qdegraaf 2bb32ee943
[`flake8-slots`] Add plugin, add `SLOT000`, `SLOT001` and `SLOT002` (#4909) 2023-06-09 04:14:16 +00:00
rodjunger ee1f094834
[`ruff`] Add a rule for static keys in dict comprehensions (#4929) 2023-06-09 02:06:34 +00:00
Tom Kuson efd8f3bdab
Complete `flake8-simplify` documentation (#4930) 2023-06-09 02:02:41 +00:00
Charlie Marsh 293889a352
Support concatenated literals in format-literals (#4974) 2023-06-09 01:29:19 +00:00
Tom Kuson 2c19000e4a
Add Pylint rule `comparison-with-itself` (`R0124`) (#4957) 2023-06-09 00:57:50 +00:00
Charlie Marsh aba073a791
Upgrade explicit-type-conversion rule (`RUF010`) to remove unnecessary `str` calls (#4971) 2023-06-08 20:02:57 +00:00
Charlie Marsh d042eddccc
Remove `unwrap` from none-comparison rule (#4969) 2023-06-08 18:21:56 +00:00
Charlie Marsh 775d247731
Allow private accesses within special dunder methods (#4968) 2023-06-08 17:36:49 +00:00
Charlie Marsh 58d08219e8
Allow re-assignments to `__all__` (#4967) 2023-06-08 17:19:56 +00:00
Charlie Marsh 902c4e7d77
Make SIM118 a suggested fix (#4966) 2023-06-08 17:02:42 +00:00
Micha Reiser 68969240c5
Format Function definitions (#4951) 2023-06-08 16:07:33 +00:00
Dhruv Manilawala 07cc4bcb0f
Update links to point to Astral org (#4949) 2023-06-08 11:43:40 -04:00