Compare commits
1 Commits
v0.0.286
...
charlie/de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
965243d98e |
14
.github/workflows/ci.yaml
vendored
14
.github/workflows/ci.yaml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
- "!crates/ruff_formatter/**"
|
||||
- "!crates/ruff_dev/**"
|
||||
- "!crates/ruff_shrinking/**"
|
||||
- scripts/*
|
||||
- scripts/check_ecosystem.py
|
||||
|
||||
formatter:
|
||||
- Cargo.toml
|
||||
@@ -56,7 +56,6 @@ jobs:
|
||||
- crates/ruff_text_size/**
|
||||
- crates/ruff_python_parser/**
|
||||
- crates/ruff_dev/**
|
||||
- scripts/*
|
||||
|
||||
cargo-fmt:
|
||||
name: "cargo fmt"
|
||||
@@ -328,7 +327,7 @@ jobs:
|
||||
name: "Formatter ecosystem and progress checks"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.formatter == 'true' || github.ref == 'refs/heads/main'
|
||||
if: needs.determine_changes.outputs.formatter == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -338,6 +337,9 @@ jobs:
|
||||
- name: "Formatter progress"
|
||||
run: scripts/formatter_ecosystem_checks.sh
|
||||
- name: "Github step summary"
|
||||
run: cat target/progress_projects_stats.txt > $GITHUB_STEP_SUMMARY
|
||||
- name: "Remove checkouts from cache"
|
||||
run: rm -r target/progress_projects
|
||||
run: grep "similarity index" target/progress_projects_log.txt | sort > $GITHUB_STEP_SUMMARY
|
||||
# CPython is not black formatted, so we run only the stability check
|
||||
- name: "Clone CPython 3.10"
|
||||
run: git clone --branch 3.10 --depth 1 https://github.com/python/cpython.git crates/ruff/resources/test/cpython
|
||||
- name: "Check CPython stability"
|
||||
run: cargo run --bin ruff_dev -- format-dev --stability-check crates/ruff/resources/test/cpython
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.1.0
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
2
.github/workflows/playground.yaml
vendored
2
.github/workflows/playground.yaml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.1.0
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.283 / 0.284
|
||||
|
||||
### The target Python version now defaults to 3.8 instead of 3.10 ([#6397](https://github.com/astral-sh/ruff/pull/6397))
|
||||
|
||||
Previously, when a target Python version was not specified, Ruff would use a default of Python 3.10. However, it is safer to default to an _older_ Python version to avoid assuming the availability of new features. We now default to the oldest supported Python version which is currently Python 3.8.
|
||||
|
||||
(We still support Python 3.7 but since [it has reached EOL](https://devguide.python.org/versions/#unsupported-versions) we've decided not to make it the default here.)
|
||||
|
||||
Note this change was announced in 0.0.283 but not active until 0.0.284.
|
||||
|
||||
## 0.0.277
|
||||
|
||||
### `.ipynb_checkpoints`, `.pyenv`, `.pytest_cache`, and `.vscode` are now excluded by default ([#5513](https://github.com/astral-sh/ruff/pull/5513))
|
||||
|
||||
@@ -131,6 +131,7 @@ At time of writing, the repository includes the following crates:
|
||||
- `crates/ruff_macros`: proc macro crate containing macros used by Ruff.
|
||||
- `crates/ruff_python_ast`: library crate containing Python-specific AST types and utilities.
|
||||
- `crates/ruff_python_codegen`: library crate containing utilities for generating Python source code.
|
||||
- `crates/ruff_python_codegen`: library crate containing utilities for generating Python source code.
|
||||
- `crates/ruff_python_formatter`: library crate implementing the Python formatter. Emits an
|
||||
intermediate representation for each node, which `ruff_formatter` prints based on the configured
|
||||
line length.
|
||||
@@ -571,7 +572,7 @@ An alternative is to convert the perf data to `flamegraph.svg` using
|
||||
[flamegraph](https://github.com/flamegraph-rs/flamegraph) (`cargo install flamegraph`):
|
||||
|
||||
```shell
|
||||
flamegraph --perfdata perf.data --no-inline
|
||||
flamegraph --perfdata perf.data
|
||||
```
|
||||
|
||||
#### Mac
|
||||
|
||||
59
Cargo.lock
generated
59
Cargo.lock
generated
@@ -14,18 +14,6 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
@@ -812,7 +800,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.286"
|
||||
version = "0.0.282"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -876,10 +864,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1005,16 +991,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imara-diff"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imperative"
|
||||
version = "1.0.4"
|
||||
@@ -2066,7 +2042,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.286"
|
||||
version = "0.0.282"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2130,7 +2106,6 @@ dependencies = [
|
||||
"typed-arena",
|
||||
"unicode-width",
|
||||
"unicode_names2",
|
||||
"uuid",
|
||||
"wsl",
|
||||
]
|
||||
|
||||
@@ -2144,7 +2119,6 @@ dependencies = [
|
||||
"ruff",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_formatter",
|
||||
"ruff_python_index",
|
||||
"ruff_python_parser",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2167,7 +2141,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.286"
|
||||
version = "0.0.282"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2223,7 +2197,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"ignore",
|
||||
"imara-diff",
|
||||
"indicatif",
|
||||
"indoc",
|
||||
"itertools",
|
||||
@@ -2343,7 +2316,6 @@ dependencies = [
|
||||
"insta",
|
||||
"is-macro",
|
||||
"itertools",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"ruff_formatter",
|
||||
"ruff_python_ast",
|
||||
@@ -2358,7 +2330,6 @@ dependencies = [
|
||||
"similar",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2382,6 +2353,7 @@ dependencies = [
|
||||
"is-macro",
|
||||
"itertools",
|
||||
"lexical-parse-float",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"unic-ucd-category",
|
||||
@@ -2403,7 +2375,6 @@ dependencies = [
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"static_assertions",
|
||||
"test-case",
|
||||
"tiny-keccak",
|
||||
"unic-emoji-char",
|
||||
"unic-ucd-ident",
|
||||
@@ -2429,7 +2400,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
"ruff_index",
|
||||
"ruff_python_ast",
|
||||
"ruff_python_parser",
|
||||
"ruff_python_stdlib",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
@@ -3349,26 +3319,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.4.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"rand",
|
||||
"uuid-macro-internal",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid-macro-internal"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7e1ba1f333bd65ce3c9f27de592fcbc256dafe3af2717f56d7c87761fbaccf4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.23",
|
||||
]
|
||||
checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
|
||||
@@ -49,8 +49,6 @@ toml = { version = "0.7.2" }
|
||||
tracing = "0.1.37"
|
||||
tracing-indicatif = "0.3.4"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
unicode-width = "0.1.10"
|
||||
uuid = { version = "1.4.1", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
# v1.0.1
|
||||
|
||||
10
README.md
10
README.md
@@ -30,7 +30,7 @@ An extremely fast Python linter, written in Rust.
|
||||
- 🤝 Python 3.11 compatibility
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 📏 Over [600 built-in rules](https://beta.ruff.rs/docs/rules/)
|
||||
- 📏 Over [500 built-in rules](https://beta.ruff.rs/docs/rules/)
|
||||
- ⚖️ [Near-parity](https://beta.ruff.rs/docs/faq/#how-does-ruff-compare-to-flake8) with the
|
||||
built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of dozens of Flake8 plugins, like flake8-bugbear
|
||||
@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.286
|
||||
rev: v0.0.282
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -211,8 +211,8 @@ line-length = 88
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
# Assume Python 3.8
|
||||
target-version = "py38"
|
||||
# Assume Python 3.10.
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
# Unlike Flake8, default to a complexity level of 10.
|
||||
@@ -233,7 +233,7 @@ linting command.
|
||||
|
||||
<!-- Begin section: Rules -->
|
||||
|
||||
**Ruff supports over 600 lint rules**, many of which are inspired by popular tools like Flake8,
|
||||
**Ruff supports over 500 lint rules**, many of which are inspired by popular tools like Flake8,
|
||||
isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implements every rule in
|
||||
Rust as a first-party feature.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.286"
|
||||
version = "0.0.282"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.286"
|
||||
version = "0.0.282"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -62,6 +62,8 @@ quick-junit = { version = "0.3.2" }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
|
||||
|
||||
schemars = { workspace = true, optional = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { workspace = true }
|
||||
@@ -75,9 +77,8 @@ strum_macros = { workspace = true }
|
||||
thiserror = { version = "1.0.43" }
|
||||
toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { workspace = true }
|
||||
unicode-width = { version = "0.1.10" }
|
||||
unicode_names2 = { version = "0.6.0", git = "https://github.com/youknowone/unicode_names2.git", rev = "4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde" }
|
||||
uuid = { workspace = true, features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -19,12 +19,3 @@ def foo(x, y, z):
|
||||
class A():
|
||||
pass
|
||||
# b = c
|
||||
|
||||
|
||||
dictionary = {
|
||||
# "key1": 123, # noqa: ERA001
|
||||
# "key2": 456,
|
||||
# "key3": 789, # test
|
||||
}
|
||||
|
||||
#import os # noqa
|
||||
|
||||
@@ -152,9 +152,3 @@ def f(a: Union[str, bytes, Any]) -> None: ...
|
||||
def f(a: Optional[Any]) -> None: ...
|
||||
def f(a: Annotated[Any, ...]) -> None: ...
|
||||
def f(a: "Union[str, bytes, Any]") -> None: ...
|
||||
|
||||
|
||||
class Foo:
|
||||
@decorator()
|
||||
def __init__(self: "Foo", foo: int):
|
||||
...
|
||||
|
||||
@@ -14,19 +14,3 @@ with open("/dev/shm/unit/test", "w") as f:
|
||||
# not ok by config
|
||||
with open("/foo/bar", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
# Using `tempfile` module should be ok
|
||||
import tempfile
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
with tempfile.NamedTemporaryFile(dir="/tmp") as f:
|
||||
f.write(b"def")
|
||||
|
||||
with tempfile.NamedTemporaryFile(dir="/var/tmp") as f:
|
||||
f.write(b"def")
|
||||
|
||||
with tempfile.TemporaryDirectory(dir="/dev/shm") as d:
|
||||
pass
|
||||
|
||||
with TemporaryDirectory(dir="/tmp") as d:
|
||||
pass
|
||||
|
||||
@@ -69,7 +69,6 @@ g_action.set_enabled(True)
|
||||
settings.set_enable_developer_extras(True)
|
||||
foo.is_(True)
|
||||
bar.is_not(False)
|
||||
next(iter([]), False)
|
||||
|
||||
class Registry:
|
||||
def __init__(self) -> None:
|
||||
|
||||
@@ -68,20 +68,6 @@ def this_is_also_wrong(value={}):
|
||||
...
|
||||
|
||||
|
||||
class Foo:
|
||||
@staticmethod
|
||||
def this_is_also_wrong_and_more_indented(value={}):
|
||||
pass
|
||||
|
||||
|
||||
def multiline_arg_wrong(value={
|
||||
|
||||
}):
|
||||
...
|
||||
|
||||
def single_line_func_wrong(value = {}): ...
|
||||
|
||||
|
||||
def and_this(value=set()):
|
||||
...
|
||||
|
||||
@@ -230,10 +216,6 @@ def timedelta_okay(value=dt.timedelta(hours=1)):
|
||||
def path_okay(value=Path(".")):
|
||||
pass
|
||||
|
||||
# B008 allow arbitrary call with immutable annotation
|
||||
def immutable_annotation_call(value: Sequence[int] = foo()):
|
||||
pass
|
||||
|
||||
# B006 and B008
|
||||
# We should handle arbitrary nesting of these B008.
|
||||
def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||
@@ -279,32 +261,3 @@ def mutable_annotations(
|
||||
d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def single_line_func_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""
|
||||
|
||||
|
||||
def single_line_func_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""
|
||||
...
|
||||
|
||||
|
||||
def single_line_func_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""; ...
|
||||
|
||||
|
||||
def single_line_func_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring"""; \
|
||||
...
|
||||
|
||||
|
||||
def single_line_func_wrong(value: dict[str, str] = {
|
||||
# This is a comment
|
||||
}):
|
||||
"""Docstring"""
|
||||
|
||||
|
||||
def single_line_func_wrong(value: dict[str, str] = {}) \
|
||||
: \
|
||||
"""Docstring"""
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import custom
|
||||
from custom import ImmutableTypeB
|
||||
|
||||
|
||||
def okay(foo: ImmutableTypeB = []):
|
||||
...
|
||||
|
||||
|
||||
def okay(foo: custom.ImmutableTypeA = []):
|
||||
...
|
||||
|
||||
|
||||
def okay(foo: custom.ImmutableTypeB = []):
|
||||
...
|
||||
|
||||
|
||||
def error_due_to_missing_import(foo: ImmutableTypeA = []):
|
||||
...
|
||||
@@ -1,7 +1,6 @@
|
||||
from typing import List
|
||||
|
||||
import fastapi
|
||||
import custom
|
||||
from fastapi import Query
|
||||
|
||||
|
||||
@@ -17,9 +16,5 @@ def okay(data: List[str] = Query(None)):
|
||||
...
|
||||
|
||||
|
||||
def okay(data: custom.ImmutableTypeA = foo()):
|
||||
...
|
||||
|
||||
|
||||
def error_due_to_missing_import(data: List[str] = Depends(None)):
|
||||
...
|
||||
|
||||
@@ -74,10 +74,3 @@ try:
|
||||
except (ValueError, binascii.Error):
|
||||
# binascii.Error is a subclass of ValueError.
|
||||
pass
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/6412
|
||||
try:
|
||||
pass
|
||||
except (ValueError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
@@ -22,10 +22,6 @@ tuple(
|
||||
"o"]
|
||||
)
|
||||
)
|
||||
set(set())
|
||||
set(list())
|
||||
set(tuple())
|
||||
sorted(reversed())
|
||||
|
||||
# Nested sorts with differing keyword arguments. Not flagged.
|
||||
sorted(sorted(x, key=lambda y: y))
|
||||
|
||||
@@ -15,6 +15,11 @@ filter(func, map(lambda v: v, nums))
|
||||
_ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
_ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
|
||||
# Error, but unfixable.
|
||||
# For simple expressions, this could be: `(x if x else 1 for x in nums)`.
|
||||
# For more complex expressions, this would differ: `(x + 2 if x else 3 for x in nums)`.
|
||||
map(lambda x=1: x, nums)
|
||||
|
||||
# False negatives.
|
||||
map(lambda x=2, y=1: x + y, nums, nums)
|
||||
set(map(lambda x, y: x, nums, nums))
|
||||
@@ -32,8 +37,3 @@ map(lambda x: lambda: x, range(4))
|
||||
|
||||
# Error: the `x` is overridden by the inner lambda.
|
||||
map(lambda x: lambda x: x, range(4))
|
||||
|
||||
# Ok because of the default parameters, and variadic arguments.
|
||||
map(lambda x=1: x, nums)
|
||||
map(lambda *args: len(args), range(4))
|
||||
map(lambda **kwargs: len(kwargs), range(4))
|
||||
|
||||
@@ -1,31 +1,22 @@
|
||||
def not_checked():
|
||||
import math
|
||||
import math # not checked
|
||||
|
||||
import altair # unconventional
|
||||
import matplotlib.pyplot # unconventional
|
||||
import numpy # unconventional
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
import tkinter # unconventional
|
||||
|
||||
def unconventional():
|
||||
import altair
|
||||
import matplotlib.pyplot
|
||||
import numpy
|
||||
import pandas
|
||||
import seaborn
|
||||
import tkinter
|
||||
import networkx
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
import numpy as nmp # unconventional
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
import tkinter as tkr # unconventional
|
||||
|
||||
|
||||
def unconventional_aliases():
|
||||
import altair as altr
|
||||
import matplotlib.pyplot as plot
|
||||
import numpy as nmp
|
||||
import pandas as pdas
|
||||
import seaborn as sbrn
|
||||
import tkinter as tkr
|
||||
import networkx as nxy
|
||||
|
||||
def conventional_aliases():
|
||||
import altair as alt
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import seaborn as sns
|
||||
import tkinter as tk
|
||||
import networkx as nx
|
||||
import altair as alt # conventional
|
||||
import matplotlib.pyplot as plt # conventional
|
||||
import numpy as np # conventional
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
import tkinter as tk # conventional
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# PIE808
|
||||
range(0, 10)
|
||||
|
||||
# OK
|
||||
range(x, 10)
|
||||
range(-15, 10)
|
||||
range(10)
|
||||
range(0)
|
||||
range(0, 10, x)
|
||||
range(0, 10, 1)
|
||||
range(0, 10, step=1)
|
||||
range(start=0, stop=10)
|
||||
range(0, stop=10)
|
||||
@@ -1,19 +1,20 @@
|
||||
import typing
|
||||
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: str | str # PYI016: Duplicate union member `str`
|
||||
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: int | int): # PYI016: Duplicate union member `int`
|
||||
print(arg1)
|
||||
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> str | str: # PYI016: Duplicate union member `str`
|
||||
return "my string"
|
||||
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: str | str | int # PYI016: Duplicate union member `str`
|
||||
field4: int | int | str # PYI016: Duplicate union member `int`
|
||||
@@ -32,55 +33,3 @@ field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
|
||||
# Should emit for unions with more than two cases
|
||||
field12: int | int | int # Error
|
||||
field13: int | int | int | int # Error
|
||||
|
||||
# Should emit for unions with more than two cases, even if not directly adjacent
|
||||
field14: int | int | str | int # Error
|
||||
|
||||
# Should emit for duplicate literal types; also covered by PYI030
|
||||
field15: typing.Literal[1] | typing.Literal[1] # Error
|
||||
|
||||
# Shouldn't emit if in new parent type
|
||||
field16: int | dict[int, str] # OK
|
||||
|
||||
# Shouldn't emit if not in a union parent
|
||||
field17: dict[int, int] # OK
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field18: typing.Union[
|
||||
set[
|
||||
int # foo
|
||||
],
|
||||
set[
|
||||
int # bar
|
||||
],
|
||||
] # Error, newline and comment will not be emitted in message
|
||||
|
||||
# Should emit in cases with `typing.Union` instead of `|`
|
||||
field19: typing.Union[int, int] # Error
|
||||
|
||||
# Should emit in cases with nested `typing.Union`
|
||||
field20: typing.Union[int, typing.Union[int, str]] # Error
|
||||
|
||||
# Should emit in cases with mixed `typing.Union` and `|`
|
||||
field21: typing.Union[int, int | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field23: set[ # foo
|
||||
int] | set[int]
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field24: typing.Union[int, typing.Union[int, int]] # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field25: typing.Union[int, int | int] # PYI016: Duplicate union member `int`
|
||||
|
||||
@@ -74,13 +74,3 @@ field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||
# Should emit in cases with newlines
|
||||
field23: set[ # foo
|
||||
int] | set[int]
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field24: typing.Union[int, typing.Union[int, int]] # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field25: typing.Union[int, int | int] # PYI016: Duplicate union member `int`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import builtins
|
||||
from typing import Union
|
||||
|
||||
|
||||
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||
x: type[int] | type[str] | type[float]
|
||||
y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||
@@ -8,9 +9,7 @@ z: Union[type[float], type[complex]]
|
||||
z: Union[type[float, int], type[complex]]
|
||||
|
||||
|
||||
def func(arg: type[int] | str | type[float]) -> None:
|
||||
...
|
||||
|
||||
def func(arg: type[int] | str | type[float]) -> None: ...
|
||||
|
||||
# OK
|
||||
x: type[int, str, float]
|
||||
@@ -18,14 +17,4 @@ y: builtins.type[int, str, complex]
|
||||
z: Union[float, complex]
|
||||
|
||||
|
||||
def func(arg: type[int, float] | str) -> None:
|
||||
...
|
||||
|
||||
|
||||
# OK
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
def func(arg: type[int, float] | str) -> None: ...
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import builtins
|
||||
from typing import Union
|
||||
|
||||
|
||||
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||
x: type[int] | type[str] | type[float]
|
||||
y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||
z: Union[type[float], type[complex]]
|
||||
z: Union[type[float, int], type[complex]]
|
||||
|
||||
|
||||
def func(arg: type[int] | str | type[float]) -> None: ...
|
||||
|
||||
# OK
|
||||
@@ -14,11 +16,5 @@ x: type[int, str, float]
|
||||
y: builtins.type[int, str, complex]
|
||||
z: Union[float, complex]
|
||||
|
||||
|
||||
def func(arg: type[int, float] | str) -> None: ...
|
||||
|
||||
# OK
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
@@ -64,8 +64,3 @@ def test_implicit_str_concat_no_parens(param1, param2, param3):
|
||||
@pytest.mark.parametrize((("param1, " "param2, " "param3")), [(1, 2, 3), (4, 5, 6)])
|
||||
def test_implicit_str_concat_with_multi_parens(param1, param2, param3):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("param1,param2"), [(1, 2), (3, 4)])
|
||||
def test_csv_with_parens(param1, param2):
|
||||
...
|
||||
|
||||
@@ -80,15 +80,3 @@ class Test(unittest.TestCase):
|
||||
|
||||
def test_assert_not_regexp_matches(self):
|
||||
self.assertNotRegex("abc", r"abc") # Error
|
||||
|
||||
def test_fail_if(self):
|
||||
self.failIf("abc") # Error
|
||||
|
||||
def test_fail_unless(self):
|
||||
self.failUnless("abc") # Error
|
||||
|
||||
def test_fail_unless_equal(self):
|
||||
self.failUnlessEqual(1, 2) # Error
|
||||
|
||||
def test_fail_if_equal(self):
|
||||
self.failIfEqual(1, 2) # Error
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from pickle import PicklingError, UnpicklingError
|
||||
import socket
|
||||
|
||||
import pytest
|
||||
@@ -21,12 +20,6 @@ def test_error_no_argument_given():
|
||||
with pytest.raises(socket.error):
|
||||
raise ValueError("Can't divide 1 by 0")
|
||||
|
||||
with pytest.raises(PicklingError):
|
||||
raise PicklingError("Can't pickle")
|
||||
|
||||
with pytest.raises(UnpicklingError):
|
||||
raise UnpicklingError("Can't unpickle")
|
||||
|
||||
|
||||
def test_error_match_is_empty():
|
||||
with pytest.raises(ValueError, match=None):
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x", [1, 1, 2])
|
||||
def test_error_literal(x):
|
||||
...
|
||||
|
||||
|
||||
a = 1
|
||||
b = 2
|
||||
c = 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x", [a, a, b, b, b, c])
|
||||
def test_error_expr_simple(x):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"x",
|
||||
[
|
||||
(a, b),
|
||||
# comment
|
||||
(a, b),
|
||||
(b, c),
|
||||
],
|
||||
)
|
||||
def test_error_expr_complex(x):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x", [a, b, (a), c, ((a))])
|
||||
def test_error_parentheses(x):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"x",
|
||||
[
|
||||
a,
|
||||
b,
|
||||
(a),
|
||||
c,
|
||||
((a)),
|
||||
],
|
||||
)
|
||||
def test_error_parentheses_trailing_comma(x):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize("x", [1, 2])
|
||||
def test_ok(x):
|
||||
...
|
||||
@@ -43,12 +43,3 @@ message
|
||||
assert something # OK
|
||||
assert something and something_else # Error
|
||||
assert something and something_else and something_third # Error
|
||||
|
||||
|
||||
def test_multiline():
|
||||
assert something and something_else; x = 1
|
||||
|
||||
x = 1; assert something and something_else
|
||||
|
||||
x = 1; \
|
||||
assert something and something_else
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import unittest
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def test_errors(self):
|
||||
with self.assertRaises(ValueError):
|
||||
raise ValueError
|
||||
with self.assertRaises(expected_exception=ValueError):
|
||||
raise ValueError
|
||||
|
||||
with self.failUnlessRaises(ValueError):
|
||||
raise ValueError
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "test"):
|
||||
raise ValueError("test")
|
||||
|
||||
with self.assertRaisesRegex(ValueError, expected_regex="test"):
|
||||
raise ValueError("test")
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
expected_exception=ValueError, expected_regex="test"
|
||||
):
|
||||
raise ValueError("test")
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
expected_regex="test", expected_exception=ValueError
|
||||
):
|
||||
raise ValueError("test")
|
||||
|
||||
with self.assertRaisesRegexp(ValueError, "test"):
|
||||
raise ValueError("test")
|
||||
|
||||
def test_unfixable_errors(self):
|
||||
with self.assertRaises(ValueError, msg="msg"):
|
||||
raise ValueError
|
||||
|
||||
with self.assertRaises(
|
||||
# comment
|
||||
ValueError
|
||||
):
|
||||
raise ValueError
|
||||
|
||||
with (
|
||||
self
|
||||
# comment
|
||||
.assertRaises(ValueError)
|
||||
):
|
||||
raise ValueError
|
||||
@@ -1,12 +0,0 @@
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def test_pytest_raises(self):
|
||||
with pytest.raises(ValueError):
|
||||
raise ValueError
|
||||
|
||||
def test_errors(self):
|
||||
with self.assertRaises(ValueError):
|
||||
raise ValueError
|
||||
@@ -19,20 +19,11 @@ raise TypeError ()
|
||||
raise TypeError \
|
||||
()
|
||||
|
||||
# RSE102
|
||||
raise TypeError \
|
||||
();
|
||||
|
||||
# RSE102
|
||||
raise TypeError(
|
||||
|
||||
)
|
||||
|
||||
# RSE102
|
||||
raise (TypeError) (
|
||||
|
||||
)
|
||||
|
||||
# RSE102
|
||||
raise TypeError(
|
||||
# Hello, world!
|
||||
@@ -61,10 +52,3 @@ class Class:
|
||||
|
||||
# OK
|
||||
raise Class.error()
|
||||
|
||||
|
||||
import ctypes
|
||||
|
||||
|
||||
# OK
|
||||
raise ctypes.WinError(1)
|
||||
|
||||
@@ -320,9 +320,3 @@ def end_of_statement():
|
||||
if True:
|
||||
return "" \
|
||||
; # type: ignore
|
||||
|
||||
|
||||
def end_of_file():
|
||||
if False:
|
||||
return 1
|
||||
x = 2 \
|
||||
|
||||
@@ -73,7 +73,3 @@ print(foo.__dict__)
|
||||
print(foo.__str__())
|
||||
print(foo().__class__)
|
||||
print(foo._asdict())
|
||||
|
||||
import os
|
||||
|
||||
os._exit()
|
||||
|
||||
@@ -31,8 +31,6 @@ for key in list(obj.keys()):
|
||||
|
||||
key in (obj or {}).keys() # SIM118
|
||||
|
||||
(key) in (obj or {}).keys() # SIM118
|
||||
|
||||
from typing import KeysView
|
||||
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
## Banned modules ##
|
||||
import torch
|
||||
|
||||
from torch import *
|
||||
|
||||
from tensorflow import a, b, c
|
||||
|
||||
import torch as torch_wearing_a_trenchcoat
|
||||
|
||||
# this should count as module level
|
||||
x = 1; import tensorflow
|
||||
|
||||
# banning a module also bans any submodules
|
||||
import torch.foo.bar
|
||||
|
||||
from tensorflow.foo import bar
|
||||
|
||||
from torch.foo.bar import *
|
||||
|
||||
# unlike TID251, inline imports are *not* banned
|
||||
def my_cool_function():
|
||||
import tensorflow.foo.bar
|
||||
|
||||
def another_cool_function():
|
||||
from torch.foo import bar
|
||||
|
||||
def import_alias():
|
||||
from torch.foo import bar
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import torch
|
||||
@@ -1,12 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
|
||||
|
||||
class Birthday(DeclarativeBase):
|
||||
|
||||
__tablename__ = "birthday"
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
day: Mapped[date]
|
||||
@@ -27,8 +27,6 @@ def f(cls, x):
|
||||
###
|
||||
lambda x: print("Hello, world!")
|
||||
|
||||
lambda: print("Hello, world!")
|
||||
|
||||
|
||||
class C:
|
||||
###
|
||||
@@ -204,14 +202,3 @@ class C:
|
||||
###
|
||||
def f(x: None) -> None:
|
||||
_ = cast(Any, _identity)(x=x)
|
||||
|
||||
###
|
||||
# Unused arguments with `locals`.
|
||||
###
|
||||
def f(bar: str):
|
||||
print(locals())
|
||||
|
||||
|
||||
class C:
|
||||
def __init__(self, x) -> None:
|
||||
print(locals())
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import os
|
||||
import pandas
|
||||
import foo.baz
|
||||
@@ -1,2 +0,0 @@
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# A copyright notice could go here
|
||||
|
||||
# A linter directive could go here
|
||||
|
||||
x = 1
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"math.pi"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"math.pi"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -21,29 +21,3 @@ while i < 10:
|
||||
print("error")
|
||||
|
||||
i += 1
|
||||
|
||||
# OK - no other way to write this
|
||||
for i in range(10):
|
||||
try:
|
||||
print(f"{i}")
|
||||
break
|
||||
except:
|
||||
print("error")
|
||||
|
||||
# OK - no other way to write this
|
||||
for i in range(10):
|
||||
try:
|
||||
print(f"{i}")
|
||||
continue
|
||||
except:
|
||||
print("error")
|
||||
|
||||
|
||||
# OK - no other way to write this
|
||||
for i in range(10):
|
||||
try:
|
||||
print(f"{i}")
|
||||
if i > 0:
|
||||
break
|
||||
except:
|
||||
print("error")
|
||||
|
||||
@@ -28,6 +28,3 @@ mdtypes_template = {
|
||||
'tag_full': [('mdtype', 'u4'), ('byte_count', 'u4')],
|
||||
'tag_smalldata':[('byte_count_mdtype', 'u4'), ('data', 'S4')],
|
||||
}
|
||||
|
||||
#: Okay
|
||||
a = (1,
|
||||
|
||||
@@ -30,10 +30,3 @@ def foo() -> None:
|
||||
|
||||
if __name__ == "__main__":
|
||||
import g
|
||||
|
||||
import h; import i
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import j; \
|
||||
import k
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
# aaaa
|
||||
# aaaaa
|
||||
# a
|
||||
# a
|
||||
# aa
|
||||
# aaa
|
||||
# aaaa
|
||||
# a
|
||||
# aa
|
||||
# aaa
|
||||
a = """ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
a = """ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
|
||||
if True: # noqa: E501
|
||||
[12]
|
||||
[12 ]
|
||||
[1,2]
|
||||
[1, 2]
|
||||
b = """ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
b = """ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
|
||||
c = """2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
c = """2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
|
||||
d = """💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
d = """💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A67ß9💣2ℝ4A6"""
|
||||
|
||||
@@ -25,12 +25,6 @@ if (True) == TrueElement or x == TrueElement:
|
||||
if res == True != False:
|
||||
pass
|
||||
|
||||
if(True) == TrueElement or x == TrueElement:
|
||||
pass
|
||||
|
||||
if (yield i) == True:
|
||||
print("even")
|
||||
|
||||
#: Okay
|
||||
if x not in y:
|
||||
pass
|
||||
|
||||
@@ -61,30 +61,3 @@ if x == types.X:
|
||||
|
||||
#: E721
|
||||
assert type(res) is int
|
||||
|
||||
|
||||
class Foo:
|
||||
def asdf(self, value: str | None):
|
||||
#: E721
|
||||
if type(value) is str:
|
||||
...
|
||||
|
||||
|
||||
class Foo:
|
||||
def type(self):
|
||||
pass
|
||||
|
||||
def asdf(self, value: str | None):
|
||||
#: E721
|
||||
if type(value) is str:
|
||||
...
|
||||
|
||||
|
||||
class Foo:
|
||||
def asdf(self, value: str | None):
|
||||
def type():
|
||||
pass
|
||||
|
||||
# Okay
|
||||
if type(value) is str:
|
||||
...
|
||||
|
||||
@@ -133,8 +133,3 @@ def scope():
|
||||
from collections.abc import Callable
|
||||
|
||||
f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
|
||||
|
||||
|
||||
class TemperatureScales(Enum):
|
||||
CELSIUS = (lambda deg_c: deg_c)
|
||||
FAHRENHEIT = (lambda deg_c: deg_c * 9 / 5 + 32)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
def func():
|
||||
"""\
|
||||
"""
|
||||
|
||||
|
||||
def func():
|
||||
"""\\
|
||||
"""
|
||||
|
||||
|
||||
def func():
|
||||
"""\ \
|
||||
"""
|
||||
@@ -92,23 +92,3 @@ match *0, 1, *2:
|
||||
case 0,:
|
||||
import x
|
||||
import y
|
||||
|
||||
|
||||
# Test: access a sub-importation via an alias.
|
||||
import foo.bar as bop
|
||||
import foo.bar.baz
|
||||
|
||||
print(bop.baz.read_csv("test.csv"))
|
||||
|
||||
# Test: isolated deletions.
|
||||
if TYPE_CHECKING:
|
||||
import a1
|
||||
|
||||
import a2
|
||||
|
||||
|
||||
match *0, 1, *2:
|
||||
case 0,:
|
||||
import b1
|
||||
|
||||
import b2
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"{bar}{}".format(1, bar=2, spam=3) # F522
|
||||
"{bar:{spam}}".format(bar=2, spam=3) # No issues
|
||||
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||
# Not fixable
|
||||
(''
|
||||
.format(x=2)) # F522
|
||||
.format(x=2))
|
||||
@@ -28,6 +28,6 @@
|
||||
"{1}{3}".format(1, 2, 3, 4) # F523, # F524
|
||||
"{1} {8}".format(0, 1) # F523, # F524
|
||||
|
||||
# Multiline
|
||||
# Not fixable
|
||||
(''
|
||||
.format(2))
|
||||
|
||||
@@ -70,13 +70,3 @@ import requests_mock as rm
|
||||
|
||||
def requests_mock(requests_mock: rm.Mocker):
|
||||
print(rm.ANY)
|
||||
|
||||
|
||||
import sklearn.base
|
||||
import mlflow.sklearn
|
||||
|
||||
|
||||
def f():
|
||||
import sklearn
|
||||
|
||||
mlflow
|
||||
|
||||
@@ -145,9 +145,3 @@ def f() -> None:
|
||||
obj = Foo()
|
||||
obj.do_thing()
|
||||
|
||||
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
except Exception as _:
|
||||
pass
|
||||
|
||||
@@ -154,14 +154,3 @@ def f() -> None:
|
||||
print("hello")
|
||||
except A as e :
|
||||
print("oh no!")
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
y = 2
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
y = 2
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
class Apples:
|
||||
def _init_(self): # [bad-dunder-name]
|
||||
pass
|
||||
|
||||
def __hello__(self): # [bad-dunder-name]
|
||||
print("hello")
|
||||
|
||||
def __init_(self): # [bad-dunder-name]
|
||||
# author likely unintentionally misspelled the correct init dunder.
|
||||
pass
|
||||
|
||||
def _init_(self): # [bad-dunder-name]
|
||||
# author likely unintentionally misspelled the correct init dunder.
|
||||
pass
|
||||
|
||||
def ___neg__(self): # [bad-dunder-name]
|
||||
# author likely accidentally added an additional `_`
|
||||
pass
|
||||
|
||||
def __inv__(self): # [bad-dunder-name]
|
||||
# author likely meant to call the invert dunder method
|
||||
pass
|
||||
|
||||
def hello(self):
|
||||
print("hello")
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def init(self):
|
||||
# valid name even though someone could accidentally mean __init__
|
||||
pass
|
||||
|
||||
def _protected_method(self):
|
||||
print("Protected")
|
||||
|
||||
def __private_method(self):
|
||||
print("Private")
|
||||
|
||||
@property
|
||||
def __doc__(self):
|
||||
return "Docstring"
|
||||
|
||||
|
||||
def __foo_bar__(): # this is not checked by the [bad-dunder-name] rule
|
||||
...
|
||||
@@ -14,12 +14,7 @@
|
||||
|
||||
"{:s} {:y}".format("hello", "world") # [bad-format-character]
|
||||
|
||||
"{:*^30s}".format("centered") # OK
|
||||
"{:{s}}".format("hello", s="s") # OK (nested placeholder value not checked)
|
||||
"{:{s:y}}".format("hello", s="s") # [bad-format-character] (nested placeholder format spec checked)
|
||||
"{0:.{prec}g}".format(1.23, prec=15) # OK (cannot validate after nested placeholder)
|
||||
"{0:.{foo}{bar}{foobar}y}".format(...) # OK (cannot validate after nested placeholders)
|
||||
"{0:.{foo}x{bar}y{foobar}g}".format(...) # OK (all nested placeholders are consumed without considering in between chars)
|
||||
"{:*^30s}".format("centered")
|
||||
|
||||
## f-strings
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ print("foo %(foo)d bar %(bar)d" % {"foo": "1", "bar": "2"})
|
||||
"%(key)d" % {"key": []}
|
||||
print("%d" % ("%s" % ("nested",),))
|
||||
"%d" % ((1, 2, 3),)
|
||||
"%d" % (1 if x > 0 else [])
|
||||
|
||||
# False negatives
|
||||
WORD = "abc"
|
||||
@@ -56,4 +55,3 @@ r'\%03o' % (ord(c),)
|
||||
"%d" % (len(foo),)
|
||||
'(%r, %r, %r, %r)' % (hostname, address, username, '$PASSWORD')
|
||||
'%r' % ({'server_school_roles': server_school_roles, 'is_school_multiserver_domain': is_school_multiserver_domain}, )
|
||||
"%d" % (1 if x > 0 else 2)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
class Person: # [eq-without-hash]
|
||||
class Person:
|
||||
def __init__(self):
|
||||
self.name = "monty"
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Person) and other.name == self.name
|
||||
|
||||
# OK
|
||||
class Language:
|
||||
def __init__(self):
|
||||
self.name = "python"
|
||||
@@ -15,9 +14,3 @@ class Language:
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
class MyClass:
|
||||
def __eq__(self, other):
|
||||
return True
|
||||
|
||||
__hash__ = None
|
||||
|
||||
@@ -10,4 +10,3 @@ os.getenv("AA", "GOOD" + "BAD")
|
||||
os.getenv("AA", "GOOD" + 1)
|
||||
os.getenv("AA", "GOOD %s" % "BAD")
|
||||
os.getenv("B", Z)
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ os.getenv(key="foo", default="bar")
|
||||
os.getenv(key=f"foo", default="bar")
|
||||
os.getenv(key="foo" + "bar", default=1)
|
||||
os.getenv(key=1 + "bar", default=1) # [invalid-envvar-value]
|
||||
os.getenv("PATH_TEST" if using_clear_path else "PATH_ORIG")
|
||||
os.getenv(1 if using_clear_path else "PATH_ORIG")
|
||||
|
||||
AA = "aa"
|
||||
os.getenv(AA)
|
||||
|
||||
@@ -19,10 +19,6 @@ logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
|
||||
# do not handle keyword arguments
|
||||
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d")
|
||||
|
||||
logging.info(msg="Hello %s")
|
||||
|
||||
logging.info(msg="Hello %s %s")
|
||||
|
||||
import warning
|
||||
|
||||
warning.warning("Hello %s %s", "World!")
|
||||
|
||||
@@ -15,10 +15,6 @@ logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
|
||||
# do not handle keyword arguments
|
||||
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d", {"objects": 1, "modifications": 1, "errors": 1})
|
||||
|
||||
logging.info(msg="Hello")
|
||||
|
||||
logging.info(msg="Hello", something="else")
|
||||
|
||||
import warning
|
||||
|
||||
warning.warning("Hello %s", "World!", "again")
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import abc
|
||||
|
||||
|
||||
class Person:
|
||||
def developer_greeting(self, name): # [no-self-use]
|
||||
print(f"Greetings {name}!")
|
||||
|
||||
def greeting_1(self): # [no-self-use]
|
||||
print("Hello!")
|
||||
|
||||
def greeting_2(self): # [no-self-use]
|
||||
print("Hi!")
|
||||
|
||||
|
||||
# OK
|
||||
def developer_greeting():
|
||||
print("Greetings developer!")
|
||||
|
||||
|
||||
# OK
|
||||
class Person:
|
||||
name = "Paris"
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __cmp__(self, other):
|
||||
print(24)
|
||||
|
||||
def __repr__(self):
|
||||
return "Person"
|
||||
|
||||
def func(self):
|
||||
...
|
||||
|
||||
def greeting_1(self):
|
||||
print(f"Hello from {self.name} !")
|
||||
|
||||
@staticmethod
|
||||
def greeting_2():
|
||||
print("Hi!")
|
||||
|
||||
|
||||
class Base(abc.ABC):
|
||||
"""abstract class"""
|
||||
|
||||
@abstractmethod
|
||||
def abstract_method(self):
|
||||
"""abstract method could not be a function"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Sub(Base):
|
||||
@override
|
||||
def abstract_method(self):
|
||||
print("concret method")
|
||||
|
||||
|
||||
class Prop:
|
||||
@property
|
||||
def count(self):
|
||||
return 24
|
||||
@@ -9,22 +9,13 @@ foo != "a" and foo != "b" and foo != "c"
|
||||
|
||||
foo == a or foo == "b" or foo == 3 # Mixed types.
|
||||
|
||||
# False negatives (the current implementation doesn't support Yoda conditions).
|
||||
"a" == foo or "b" == foo or "c" == foo
|
||||
|
||||
"a" != foo and "b" != foo and "c" != foo
|
||||
|
||||
"a" == foo or foo == "b" or "c" == foo
|
||||
|
||||
foo == bar or baz == foo or qux == foo
|
||||
|
||||
foo == "a" or "b" == foo or foo == "c"
|
||||
|
||||
foo != "a" and "b" != foo and foo != "c"
|
||||
|
||||
foo == "a" or foo == "b" or "c" == bar or "d" == bar # Multiple targets
|
||||
|
||||
foo.bar == "a" or foo.bar == "b" # Attributes.
|
||||
|
||||
# OK
|
||||
foo == "a" and foo == "b" and foo == "c" # `and` mixed with `==`.
|
||||
|
||||
@@ -41,13 +32,3 @@ foo not in {"a", "b", "c"} # Uses membership test already.
|
||||
foo == "a" # Single comparison.
|
||||
|
||||
foo != "a" # Single comparison.
|
||||
|
||||
foo == "a" == "b" or foo == "c" # Multiple comparisons.
|
||||
|
||||
foo == bar == "b" or foo == "c" # Multiple comparisons.
|
||||
|
||||
foo == foo or foo == bar # Self-comparison.
|
||||
|
||||
foo[0] == "a" or foo[0] == "b" # Subscripts.
|
||||
|
||||
foo() == "a" or foo() == "b" # Calls.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import subprocess
|
||||
|
||||
# Errors.
|
||||
subprocess.run("ls")
|
||||
subprocess.run("ls", shell=True)
|
||||
|
||||
# Non-errors.
|
||||
subprocess.run("ls", check=True)
|
||||
subprocess.run("ls", check=False)
|
||||
subprocess.run("ls", shell=True, check=True)
|
||||
subprocess.run("ls", shell=True, check=False)
|
||||
foo.run("ls") # Not a subprocess.run call.
|
||||
subprocess.bar("ls") # Not a subprocess.run call.
|
||||
@@ -1,3 +0,0 @@
|
||||
from sys import *
|
||||
|
||||
exit(0)
|
||||
@@ -1,7 +0,0 @@
|
||||
"""
|
||||
# coding=utf8""" # empty comment
|
||||
|
||||
"""
|
||||
Invalid coding declaration since it is nested inside a docstring
|
||||
The following empty comment tests for false positives as our implementation visits comments
|
||||
"""
|
||||
@@ -1,7 +0,0 @@
|
||||
# coding=utf8
|
||||
print("Hello world")
|
||||
|
||||
"""
|
||||
Regression test for https://github.com/astral-sh/ruff/issues/6756
|
||||
The leading space must be removed to prevent invalid syntax.
|
||||
"""
|
||||
@@ -1,7 +0,0 @@
|
||||
# coding=utf8
|
||||
print("Hello world")
|
||||
|
||||
"""
|
||||
Regression test for https://github.com/astral-sh/ruff/issues/6756
|
||||
The leading tab must be removed to prevent invalid syntax.
|
||||
"""
|
||||
@@ -1,6 +0,0 @@
|
||||
print("foo") # coding=utf8
|
||||
print("Hello world")
|
||||
|
||||
"""
|
||||
Invalid coding declaration due to a statement before the comment
|
||||
"""
|
||||
@@ -1,7 +0,0 @@
|
||||
x = 1 \
|
||||
# coding=utf8
|
||||
x = 2
|
||||
|
||||
"""
|
||||
Invalid coding declaration due to continuation on preceding line
|
||||
"""
|
||||
@@ -31,7 +31,6 @@ bool("foo")
|
||||
bool("")
|
||||
bool(b"")
|
||||
bool(1.0)
|
||||
int().denominator
|
||||
|
||||
# These become string or byte literals
|
||||
str()
|
||||
@@ -50,6 +49,3 @@ float(1.0)
|
||||
bool()
|
||||
bool(True)
|
||||
bool(False)
|
||||
|
||||
# These become a literal but retain parentheses
|
||||
int(1).denominator
|
||||
|
||||
@@ -32,30 +32,3 @@ print(
|
||||
)
|
||||
|
||||
'{' '0}'.format(1)
|
||||
|
||||
args = list(range(10))
|
||||
kwargs = {x: x for x in range(10)}
|
||||
|
||||
"{0}".format(*args)
|
||||
|
||||
"{0}".format(**kwargs)
|
||||
|
||||
"{0}_{1}".format(*args)
|
||||
|
||||
"{0}_{1}".format(1, *args)
|
||||
|
||||
"{0}_{1}".format(1, 2, *args)
|
||||
|
||||
"{0}_{1}".format(*args, 1, 2)
|
||||
|
||||
"{0}_{1}_{2}".format(1, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, *args, **kwargs)
|
||||
|
||||
"{1}_{0}".format(1, 2, *args)
|
||||
|
||||
"{1}_{0}".format(1, 2)
|
||||
|
||||
@@ -15,17 +15,3 @@ f"{0}".format(1)
|
||||
print(f"{0}".format(1))
|
||||
|
||||
''.format(1)
|
||||
|
||||
'{1} {0}'.format(*args)
|
||||
|
||||
"{1}_{0}".format(*args, 1)
|
||||
|
||||
"{1}_{0}".format(*args, 1, 2)
|
||||
|
||||
"{1}_{0}".format(1, **kwargs)
|
||||
|
||||
"{1}_{0}".format(1, foo=2)
|
||||
|
||||
"{1}_{0}".format(1, 2, **kwargs)
|
||||
|
||||
"{1}_{0}".format(1, 2, foo=3, bar=4)
|
||||
|
||||
28
crates/ruff/resources/test/fixtures/pyupgrade/UP030_2.py
vendored
Normal file
28
crates/ruff/resources/test/fixtures/pyupgrade/UP030_2.py
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# These SHOULD change
|
||||
|
||||
args = list(range(10))
|
||||
kwargs = {x: x for x in range(10)}
|
||||
|
||||
"{0}".format(*args)
|
||||
|
||||
"{0}".format(**kwargs)
|
||||
|
||||
"{0}_{1}".format(*args)
|
||||
|
||||
"{0}_{1}".format(1, *args)
|
||||
|
||||
"{1}_{0}".format(*args)
|
||||
|
||||
"{1}_{0}".format(1, *args)
|
||||
|
||||
"{0}_{1}".format(1, 2, *args)
|
||||
|
||||
"{0}_{1}".format(*args, 1, 2)
|
||||
|
||||
"{0}_{1}_{2}".format(1, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, *args, **kwargs)
|
||||
@@ -5,42 +5,11 @@ from typing import TypeAlias
|
||||
x: typing.TypeAlias = int
|
||||
x: TypeAlias = int
|
||||
|
||||
# UP040 simple generic
|
||||
|
||||
# UP040 with generics (todo)
|
||||
T = typing.TypeVar["T"]
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 call style generic
|
||||
T = typing.TypeVar("T")
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 bounded generic (todo)
|
||||
T = typing.TypeVar("T", bound=int)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
T = typing.TypeVar("T", int, str)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 contravariant generic (todo)
|
||||
T = typing.TypeVar("T", contravariant=True)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 covariant generic (todo)
|
||||
T = typing.TypeVar("T", covariant=True)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 in class scope
|
||||
T = typing.TypeVar["T"]
|
||||
class Foo:
|
||||
# reference to global variable
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# reference to class variable
|
||||
TCLS = typing.TypeVar["TCLS"]
|
||||
y: typing.TypeAlias = list[TCLS]
|
||||
|
||||
# UP040 wont add generics in fix
|
||||
T = typing.TypeVar(*args)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# OK
|
||||
x: TypeAlias
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
x = [1, 2, 3]
|
||||
y = [4, 5, 6]
|
||||
|
||||
# RUF017
|
||||
sum([x, y], start=[])
|
||||
sum([x, y], [])
|
||||
sum([[1, 2, 3], [4, 5, 6]], start=[])
|
||||
sum([[1, 2, 3], [4, 5, 6]], [])
|
||||
sum([[1, 2, 3], [4, 5, 6]],
|
||||
[])
|
||||
|
||||
# OK
|
||||
sum([x, y])
|
||||
sum([[1, 2, 3], [4, 5, 6]])
|
||||
@@ -1,5 +0,0 @@
|
||||
# ruff: noqa: RUF100
|
||||
|
||||
import os # noqa: F401
|
||||
|
||||
print(os.sep)
|
||||
@@ -1,11 +0,0 @@
|
||||
#import os # noqa
|
||||
#import os # noqa: ERA001
|
||||
|
||||
dictionary = {
|
||||
# "key1": 123, # noqa: ERA001
|
||||
# "key2": 456, # noqa
|
||||
# "key3": 789,
|
||||
}
|
||||
|
||||
|
||||
#import os # noqa: E501
|
||||
@@ -52,7 +52,3 @@ def good(a: int):
|
||||
def another_good(a):
|
||||
if a % 2 == 0:
|
||||
raise GoodArgCantBeEven(a)
|
||||
|
||||
|
||||
def another_good():
|
||||
raise NotImplementedError("This is acceptable too")
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
//! Interface for generating autofix edits from higher-level actions (e.g., "remove an argument").
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, Keyword, Ranged, Stmt};
|
||||
use ruff_python_ast::{
|
||||
self as ast, Arguments, ExceptHandler, Expr, Keyword, PySourceType, Ranged, Stmt,
|
||||
};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
|
||||
use ruff_python_trivia::{
|
||||
has_leading_content, is_python_whitespace, PythonWhitespace, SimpleTokenKind, SimpleTokenizer,
|
||||
};
|
||||
use ruff_python_parser::{lexer, AsMode};
|
||||
use ruff_python_trivia::{has_leading_content, is_python_whitespace, PythonWhitespace};
|
||||
use ruff_source_file::{Locator, NewlineWithTrailingNewline};
|
||||
use ruff_text_size::{TextLen, TextSize};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
use crate::autofix::codemods;
|
||||
|
||||
@@ -48,7 +48,7 @@ pub(crate) fn delete_stmt(
|
||||
} else if has_leading_content(stmt.start(), locator) {
|
||||
Edit::range_deletion(stmt.range())
|
||||
} else if let Some(start) = indexer.preceded_by_continuations(stmt.start(), locator) {
|
||||
Edit::deletion(start, stmt.end())
|
||||
Edit::range_deletion(TextRange::new(start, stmt.end()))
|
||||
} else {
|
||||
let range = locator.full_lines_range(stmt.range());
|
||||
Edit::range_deletion(range)
|
||||
@@ -89,52 +89,83 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||
argument: &T,
|
||||
arguments: &Arguments,
|
||||
parentheses: Parentheses,
|
||||
source: &str,
|
||||
locator: &Locator,
|
||||
source_type: PySourceType,
|
||||
) -> Result<Edit> {
|
||||
// Partition into arguments before and after the argument to remove.
|
||||
let (before, after): (Vec<_>, Vec<_>) = arguments
|
||||
.args
|
||||
.iter()
|
||||
.map(Expr::range)
|
||||
.chain(arguments.keywords.iter().map(Keyword::range))
|
||||
.filter(|range| argument.range() != *range)
|
||||
.partition(|range| range.start() < argument.start());
|
||||
// TODO(sbrugman): Preserve trailing comments.
|
||||
if arguments.keywords.len() + arguments.args.len() > 1 {
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
|
||||
if !after.is_empty() {
|
||||
// Case 1: argument or keyword is _not_ the last node, so delete from the start of the
|
||||
// argument to the end of the subsequent comma.
|
||||
let mut tokenizer = SimpleTokenizer::starts_at(argument.end(), source);
|
||||
if arguments
|
||||
.args
|
||||
.iter()
|
||||
.map(Expr::start)
|
||||
.chain(arguments.keywords.iter().map(Keyword::start))
|
||||
.any(|location| location > argument.start())
|
||||
{
|
||||
// Case 1: argument or keyword is _not_ the last node, so delete from the start of the
|
||||
// argument to the end of the subsequent comma.
|
||||
let mut seen_comma = false;
|
||||
for (tok, range) in lexer::lex_starts_at(
|
||||
locator.slice(arguments.range()),
|
||||
source_type.as_mode(),
|
||||
arguments.start(),
|
||||
)
|
||||
.flatten()
|
||||
{
|
||||
if seen_comma {
|
||||
if tok.is_non_logical_newline() {
|
||||
// Also delete any non-logical newlines after the comma.
|
||||
continue;
|
||||
}
|
||||
fix_end = Some(if tok.is_newline() {
|
||||
range.end()
|
||||
} else {
|
||||
range.start()
|
||||
});
|
||||
break;
|
||||
}
|
||||
if range.start() == argument.start() {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
if fix_start.is_some() && tok.is_comma() {
|
||||
seen_comma = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Case 2: argument or keyword is the last node, so delete from the start of the
|
||||
// previous comma to the end of the argument.
|
||||
for (tok, range) in lexer::lex_starts_at(
|
||||
locator.slice(arguments.range()),
|
||||
source_type.as_mode(),
|
||||
arguments.start(),
|
||||
)
|
||||
.flatten()
|
||||
{
|
||||
if range.start() == argument.start() {
|
||||
fix_end = Some(argument.end());
|
||||
break;
|
||||
}
|
||||
if tok.is_comma() {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the trailing comma.
|
||||
tokenizer
|
||||
.find(|token| token.kind == SimpleTokenKind::Comma)
|
||||
.context("Unable to find trailing comma")?;
|
||||
|
||||
// Find the next non-whitespace token.
|
||||
let next = tokenizer
|
||||
.find(|token| {
|
||||
token.kind != SimpleTokenKind::Whitespace && token.kind != SimpleTokenKind::Newline
|
||||
})
|
||||
.context("Unable to find next token")?;
|
||||
|
||||
Ok(Edit::deletion(argument.start(), next.start()))
|
||||
} else if let Some(previous) = before.iter().map(Ranged::end).max() {
|
||||
// Case 2: argument or keyword is the last node, so delete from the start of the
|
||||
// previous comma to the end of the argument.
|
||||
let mut tokenizer = SimpleTokenizer::starts_at(previous, source);
|
||||
|
||||
// Find the trailing comma.
|
||||
let comma = tokenizer
|
||||
.find(|token| token.kind == SimpleTokenKind::Comma)
|
||||
.context("Unable to find trailing comma")?;
|
||||
|
||||
Ok(Edit::deletion(comma.start(), argument.end()))
|
||||
match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Ok(Edit::deletion(start, end)),
|
||||
_ => {
|
||||
bail!("No fix could be constructed")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Case 3: argument or keyword is the only node, so delete the arguments (but preserve
|
||||
// parentheses, if needed).
|
||||
// Only one argument; remove it (but preserve parentheses, if needed).
|
||||
Ok(match parentheses {
|
||||
Parentheses::Remove => Edit::range_deletion(arguments.range()),
|
||||
Parentheses::Preserve => Edit::range_replacement("()".to_string(), arguments.range()),
|
||||
Parentheses::Remove => Edit::deletion(arguments.start(), arguments.end()),
|
||||
Parentheses::Preserve => {
|
||||
Edit::replacement("()".to_string(), arguments.start(), arguments.end())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -148,13 +179,16 @@ fn is_only<T: PartialEq>(vec: &[T], value: &T) -> bool {
|
||||
fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool {
|
||||
match parent {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. })
|
||||
| Stmt::ClassDef(ast::StmtClassDef { body, .. })
|
||||
| Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
| Stmt::With(ast::StmtWith { body, .. })
|
||||
| Stmt::AsyncWith(ast::StmtAsyncWith { body, .. }) => {
|
||||
if is_only(body, child) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. })
|
||||
| Stmt::AsyncFor(ast::StmtAsyncFor { body, orelse, .. })
|
||||
| Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if is_only(body, child) || is_only(orelse, child) {
|
||||
return true;
|
||||
@@ -178,7 +212,14 @@ fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool {
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
range: _,
|
||||
})
|
||||
| Stmt::TryStar(ast::StmtTryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
range: _,
|
||||
}) => {
|
||||
if is_only(body, child)
|
||||
|| is_only(orelse, child)
|
||||
@@ -226,25 +267,25 @@ fn trailing_semicolon(offset: TextSize, locator: &Locator) -> Option<TextSize> {
|
||||
fn next_stmt_break(semicolon: TextSize, locator: &Locator) -> TextSize {
|
||||
let start_location = semicolon + TextSize::from(1);
|
||||
|
||||
for line in
|
||||
NewlineWithTrailingNewline::with_offset(locator.after(start_location), start_location)
|
||||
{
|
||||
let contents = &locator.contents()[usize::from(start_location)..];
|
||||
for line in NewlineWithTrailingNewline::from(contents) {
|
||||
let trimmed = line.trim_whitespace();
|
||||
// Skip past any continuations.
|
||||
if trimmed.starts_with('\\') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return if trimmed.is_empty() {
|
||||
// If the line is empty, then despite the previous statement ending in a
|
||||
// semicolon, we know that it's not a multi-statement line.
|
||||
line.start()
|
||||
} else {
|
||||
// Otherwise, find the start of the next statement. (Or, anything that isn't
|
||||
// whitespace.)
|
||||
let relative_offset = line.find(|c: char| !is_python_whitespace(c)).unwrap();
|
||||
line.start() + TextSize::try_from(relative_offset).unwrap()
|
||||
};
|
||||
return start_location
|
||||
+ if trimmed.is_empty() {
|
||||
// If the line is empty, then despite the previous statement ending in a
|
||||
// semicolon, we know that it's not a multi-statement line.
|
||||
line.start()
|
||||
} else {
|
||||
// Otherwise, find the start of the next statement. (Or, anything that isn't
|
||||
// whitespace.)
|
||||
let relative_offset = line.find(|c: char| !is_python_whitespace(c)).unwrap();
|
||||
line.start() + TextSize::try_from(relative_offset).unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
locator.line_end(start_location)
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::registry::{AsRule, Rule};
|
||||
|
||||
pub(crate) mod codemods;
|
||||
pub(crate) mod edits;
|
||||
pub(crate) mod snippet;
|
||||
pub(crate) mod source_map;
|
||||
|
||||
pub(crate) struct FixResult {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// A snippet of source code for user-facing display, as in a diagnostic.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct SourceCodeSnippet(String);
|
||||
|
||||
impl SourceCodeSnippet {
|
||||
pub(crate) fn new(source_code: String) -> Self {
|
||||
Self(source_code)
|
||||
}
|
||||
|
||||
/// Return the full snippet for user-facing display, or `None` if the snippet should be
|
||||
/// truncated.
|
||||
pub(crate) fn full_display(&self) -> Option<&str> {
|
||||
if Self::should_truncate(&self.0) {
|
||||
None
|
||||
} else {
|
||||
Some(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a truncated snippet for user-facing display.
|
||||
pub(crate) fn truncated_display(&self) -> &str {
|
||||
if Self::should_truncate(&self.0) {
|
||||
"..."
|
||||
} else {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the source code should be truncated when included in a user-facing
|
||||
/// diagnostic.
|
||||
fn should_truncate(source_code: &str) -> bool {
|
||||
source_code.width() > 50 || source_code.contains(['\r', '\n'])
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
use ruff_python_ast::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
@@ -17,20 +16,14 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
return;
|
||||
}
|
||||
|
||||
for binding in &*checker.semantic.bindings {
|
||||
for binding in checker.semantic.bindings.iter() {
|
||||
if checker.enabled(Rule::UnusedVariable) {
|
||||
if binding.kind.is_bound_exception()
|
||||
&& !binding.is_used()
|
||||
&& !checker
|
||||
.settings
|
||||
.dummy_variable_rgx
|
||||
.is_match(binding.name(checker.locator))
|
||||
{
|
||||
if binding.kind.is_bound_exception() && !binding.is_used() {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
pyflakes::rules::UnusedVariable {
|
||||
name: binding.name(checker.locator).to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
binding.range,
|
||||
);
|
||||
if checker.patch(Rule::UnusedVariable) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
|
||||
@@ -7,6 +7,10 @@ use crate::rules::flake8_simplify;
|
||||
/// Run lint rules over a [`Comprehension`] syntax nodes.
|
||||
pub(crate) fn comprehension(comprehension: &Comprehension, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::InDictKeys) {
|
||||
flake8_simplify::rules::key_in_dict_comprehension(checker, comprehension);
|
||||
flake8_simplify::rules::key_in_dict_for(
|
||||
checker,
|
||||
&comprehension.target,
|
||||
&comprehension.iter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_python_ast::Stmt;
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_bugbear, perflint, pyupgrade};
|
||||
use crate::rules::{flake8_bugbear, perflint};
|
||||
|
||||
/// Run lint rules over all deferred for-loops in the [`SemanticModel`].
|
||||
pub(crate) fn deferred_for_loops(checker: &mut Checker) {
|
||||
@@ -11,18 +11,21 @@ pub(crate) fn deferred_for_loops(checker: &mut Checker) {
|
||||
for snapshot in for_loops {
|
||||
checker.semantic.restore(snapshot);
|
||||
|
||||
let Stmt::For(stmt_for) = checker.semantic.current_statement() else {
|
||||
unreachable!("Expected Stmt::For");
|
||||
};
|
||||
|
||||
if checker.enabled(Rule::UnusedLoopControlVariable) {
|
||||
flake8_bugbear::rules::unused_loop_control_variable(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::IncorrectDictIterator) {
|
||||
perflint::rules::incorrect_dict_iterator(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::YieldInForLoop) {
|
||||
pyupgrade::rules::yield_in_for_loop(checker, stmt_for);
|
||||
if let Stmt::For(ast::StmtFor {
|
||||
target, iter, body, ..
|
||||
})
|
||||
| Stmt::AsyncFor(ast::StmtAsyncFor {
|
||||
target, iter, body, ..
|
||||
}) = &checker.semantic.current_statement()
|
||||
{
|
||||
if checker.enabled(Rule::UnusedLoopControlVariable) {
|
||||
flake8_bugbear::rules::unused_loop_control_variable(checker, target, body);
|
||||
}
|
||||
if checker.enabled(Rule::IncorrectDictIterator) {
|
||||
perflint::rules::incorrect_dict_iterator(checker, target, iter);
|
||||
}
|
||||
} else {
|
||||
unreachable!("Expected Expr::For | Expr::AsyncFor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::Ranged;
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_ast::cast;
|
||||
use ruff_python_semantic::analyze::{branch_detection, visibility};
|
||||
use ruff_python_semantic::{Binding, BindingKind, ScopeKind};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -30,7 +30,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
Rule::UnusedPrivateTypedDict,
|
||||
Rule::UnusedStaticMethodArgument,
|
||||
Rule::UnusedVariable,
|
||||
Rule::NoSelfUse,
|
||||
]) {
|
||||
return;
|
||||
}
|
||||
@@ -83,7 +82,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
pylint::rules::GlobalVariableNotAssigned {
|
||||
name: (*name).to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -113,21 +112,25 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.map_or(true, |left| {
|
||||
binding.source.map_or(true, |right| {
|
||||
checker.semantic.different_branches(left, right)
|
||||
branch_detection::different_forks(
|
||||
left,
|
||||
right,
|
||||
checker.semantic.statements(),
|
||||
)
|
||||
})
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
let line = checker.locator.compute_line_index(shadowed.start());
|
||||
let line = checker.locator.compute_line_index(shadowed.range.start());
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::ImportShadowedByLoopVar {
|
||||
name: name.to_string(),
|
||||
line,
|
||||
},
|
||||
binding.range(),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -169,25 +172,18 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(node_id) = shadowed.source else {
|
||||
let Some(source) = shadowed.source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// If this is an overloaded function, abort.
|
||||
if shadowed.kind.is_function_definition() {
|
||||
if checker
|
||||
.semantic
|
||||
.statement(node_id)
|
||||
.as_function_def_stmt()
|
||||
.is_some_and(|function| {
|
||||
visibility::is_overload(
|
||||
&function.decorator_list,
|
||||
&checker.semantic,
|
||||
)
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if shadowed.kind.is_function_definition()
|
||||
&& visibility::is_overload(
|
||||
cast::decorator_list(checker.semantic.statement(source)),
|
||||
&checker.semantic,
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Only enforce cross-scope shadowing for imports.
|
||||
@@ -205,20 +201,24 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.map_or(true, |left| {
|
||||
binding.source.map_or(true, |right| {
|
||||
checker.semantic.different_branches(left, right)
|
||||
branch_detection::different_forks(
|
||||
left,
|
||||
right,
|
||||
checker.semantic.statements(),
|
||||
)
|
||||
})
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
let line = checker.locator.compute_line_index(shadowed.start());
|
||||
let line = checker.locator.compute_line_index(shadowed.range.start());
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
pyflakes::rules::RedefinedWhileUnused {
|
||||
name: (*name).to_string(),
|
||||
line,
|
||||
},
|
||||
binding.range(),
|
||||
binding.range,
|
||||
);
|
||||
if let Some(range) = binding.parent_range(&checker.semantic) {
|
||||
diagnostic.set_parent(range.start());
|
||||
@@ -241,7 +241,10 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
flake8_pyi::rules::unused_private_typed_dict(checker, scope, &mut diagnostics);
|
||||
}
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Lambda(_)) {
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(_) | ScopeKind::AsyncFunction(_) | ScopeKind::Lambda(_)
|
||||
) {
|
||||
if checker.enabled(Rule::UnusedVariable) {
|
||||
pyflakes::rules::unused_variable(checker, scope, &mut diagnostics);
|
||||
}
|
||||
@@ -267,7 +270,10 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(_) | ScopeKind::AsyncFunction(_) | ScopeKind::Module
|
||||
) {
|
||||
if enforce_typing_imports {
|
||||
let runtime_imports: Vec<&Binding> = checker
|
||||
.semantic
|
||||
@@ -303,12 +309,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
pyflakes::rules::unused_import(checker, scope, &mut diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
if scope.kind.is_function() {
|
||||
if checker.enabled(Rule::NoSelfUse) {
|
||||
pylint::rules::no_self_use(checker, scope, &mut diagnostics);
|
||||
}
|
||||
}
|
||||
}
|
||||
checker.diagnostics.extend(diagnostics);
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
expr.start(),
|
||||
));
|
||||
|
||||
if pydocstyle::helpers::should_ignore_docstring(expr) {
|
||||
if pydocstyle::helpers::should_ignore_docstring(contents) {
|
||||
#[allow(deprecated)]
|
||||
let location = checker.locator.compute_source_location(expr.start());
|
||||
warn_user!(
|
||||
|
||||
@@ -80,9 +80,17 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
Rule::RedundantLiteralUnion,
|
||||
Rule::UnnecessaryTypeUnion,
|
||||
]) {
|
||||
// Avoid duplicate checks if the parent is a union, since these rules already
|
||||
// Avoid duplicate checks if the parent is an `Union[...]` since these rules
|
||||
// traverse nested unions.
|
||||
if !checker.semantic.in_nested_union() {
|
||||
let is_unchecked_union = checker
|
||||
.semantic
|
||||
.current_expression_grandparent()
|
||||
.and_then(Expr::as_subscript_expr)
|
||||
.map_or(true, |parent| {
|
||||
!checker.semantic.match_typing_expr(&parent.value, "Union")
|
||||
});
|
||||
|
||||
if is_unchecked_union {
|
||||
if checker.enabled(Rule::UnnecessaryLiteralUnion) {
|
||||
flake8_pyi::rules::unnecessary_literal_union(checker, expr);
|
||||
}
|
||||
@@ -198,7 +206,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
ExprContext::Store => {
|
||||
if checker.enabled(Rule::NonLowercaseVariableInFunction) {
|
||||
if checker.semantic.current_scope().kind.is_function() {
|
||||
if checker.semantic.current_scope().kind.is_any_function() {
|
||||
// Ignore globals.
|
||||
if !checker
|
||||
.semantic
|
||||
@@ -261,7 +269,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pylint::rules::load_before_global_declaration(checker, id, expr);
|
||||
}
|
||||
}
|
||||
Expr::Attribute(attribute) => {
|
||||
Expr::Attribute(ast::ExprAttribute { attr, value, .. }) => {
|
||||
// Ex) typing.List[...]
|
||||
if checker.any_enabled(&[
|
||||
Rule::FutureRewritableTypeAnnotation,
|
||||
@@ -323,7 +331,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::CollectionsNamedTuple) {
|
||||
flake8_pyi::rules::collections_named_tuple(checker, expr);
|
||||
}
|
||||
pandas_vet::rules::attr(checker, attribute);
|
||||
pandas_vet::rules::attr(checker, attr, value, expr);
|
||||
}
|
||||
Expr::Call(
|
||||
call @ ast::ExprCall {
|
||||
@@ -381,34 +389,34 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
Ok(summary) => {
|
||||
if checker.enabled(Rule::StringDotFormatExtraNamedArguments) {
|
||||
pyflakes::rules::string_dot_format_extra_named_arguments(
|
||||
checker, call, &summary, keywords,
|
||||
checker, &summary, keywords, location,
|
||||
);
|
||||
}
|
||||
if checker
|
||||
.enabled(Rule::StringDotFormatExtraPositionalArguments)
|
||||
{
|
||||
pyflakes::rules::string_dot_format_extra_positional_arguments(
|
||||
checker, call, &summary, args,
|
||||
checker, &summary, args, location,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::StringDotFormatMissingArguments) {
|
||||
pyflakes::rules::string_dot_format_missing_argument(
|
||||
checker, call, &summary, args, keywords,
|
||||
checker, &summary, args, keywords, location,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::StringDotFormatMixingAutomatic) {
|
||||
pyflakes::rules::string_dot_format_mixing_automatic(
|
||||
checker, call, &summary,
|
||||
checker, &summary, location,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::FormatLiterals) {
|
||||
pyupgrade::rules::format_literals(checker, call, &summary);
|
||||
pyupgrade::rules::format_literals(checker, &summary, expr);
|
||||
}
|
||||
if checker.enabled(Rule::FString) {
|
||||
pyupgrade::rules::f_strings(
|
||||
checker,
|
||||
call,
|
||||
&summary,
|
||||
expr,
|
||||
value,
|
||||
checker.settings.line_length,
|
||||
);
|
||||
@@ -418,7 +426,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
|
||||
if checker.enabled(Rule::BadStringFormatCharacter) {
|
||||
pylint::rules::bad_string_format_character::call(
|
||||
checker, val, location,
|
||||
checker,
|
||||
val.as_str(),
|
||||
location,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -432,7 +442,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pyupgrade::rules::deprecated_unittest_alias(checker, func);
|
||||
}
|
||||
if checker.enabled(Rule::SuperCallWithParameters) {
|
||||
pyupgrade::rules::super_call_with_parameters(checker, call);
|
||||
pyupgrade::rules::super_call_with_parameters(checker, expr, func, args);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryEncodeUTF8) {
|
||||
pyupgrade::rules::unnecessary_encode_utf8(checker, call);
|
||||
@@ -441,11 +451,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pyupgrade::rules::redundant_open_modes(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::NativeLiterals) {
|
||||
pyupgrade::rules::native_literals(
|
||||
checker,
|
||||
call,
|
||||
checker.semantic().current_expression_parent(),
|
||||
);
|
||||
pyupgrade::rules::native_literals(checker, expr, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::OpenAlias) {
|
||||
pyupgrade::rules::open_alias(checker, expr, func);
|
||||
@@ -535,9 +541,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryDictKwargs) {
|
||||
flake8_pie::rules::unnecessary_dict_kwargs(checker, expr, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryRangeStart) {
|
||||
flake8_pie::rules::unnecessary_range_start(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::ExecBuiltin) {
|
||||
flake8_bandit::rules::exec_used(checker, func);
|
||||
}
|
||||
@@ -680,8 +683,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
checker, expr, func, args, keywords,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::BooleanPositionalValueInCall) {
|
||||
flake8_boolean_trap::rules::boolean_positional_value_in_call(checker, args, func);
|
||||
if checker.enabled(Rule::BooleanPositionalValueInFunctionCall) {
|
||||
flake8_boolean_trap::rules::check_boolean_positional_value_in_function_call(
|
||||
checker, args, func,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::Debugger) {
|
||||
flake8_debugger::rules::debugger_call(checker, expr, func);
|
||||
@@ -763,19 +768,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::PytestUnittestRaisesAssertion) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pytest_style::rules::unittest_raises_assertion(checker, call)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::SubprocessPopenPreexecFn) {
|
||||
pylint::rules::subprocess_popen_preexec_fn(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::SubprocessRunWithoutCheck) {
|
||||
pylint::rules::subprocess_run_without_check(checker, call);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::PytestRaisesWithoutException,
|
||||
Rule::PytestRaisesTooBroad,
|
||||
@@ -880,9 +875,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnsupportedMethodCallOnAll) {
|
||||
flake8_pyi::rules::unsupported_method_call_on_all(checker, func);
|
||||
}
|
||||
if checker.enabled(Rule::QuadraticListSummation) {
|
||||
ruff::rules::quadratic_list_summation(checker, call);
|
||||
}
|
||||
}
|
||||
Expr::Dict(ast::ExprDict {
|
||||
keys,
|
||||
@@ -931,7 +923,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pylint::rules::await_outside_async(checker, expr);
|
||||
}
|
||||
}
|
||||
Expr::FString(ast::ExprFString { values, .. }) => {
|
||||
Expr::JoinedStr(ast::ExprJoinedStr { values, range: _ }) => {
|
||||
if checker.enabled(Rule::FStringMissingPlaceholders) {
|
||||
pyflakes::rules::f_string_missing_placeholders(expr, values, checker);
|
||||
}
|
||||
@@ -958,7 +950,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
range: _,
|
||||
}) => {
|
||||
if let Expr::Constant(ast::ExprConstant {
|
||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
}) = left.as_ref()
|
||||
{
|
||||
@@ -1092,34 +1084,47 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid duplicate checks if the parent is a union, since these rules already
|
||||
// Avoid duplicate checks if the parent is an `|` since these rules
|
||||
// traverse nested unions.
|
||||
if !checker.semantic.in_nested_union() {
|
||||
if checker.enabled(Rule::DuplicateUnionMember)
|
||||
&& checker.semantic.in_type_definition()
|
||||
{
|
||||
flake8_pyi::rules::duplicate_union_member(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryLiteralUnion) {
|
||||
flake8_pyi::rules::unnecessary_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantLiteralUnion) {
|
||||
flake8_pyi::rules::redundant_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryTypeUnion) {
|
||||
flake8_pyi::rules::unnecessary_type_union(checker, expr);
|
||||
}
|
||||
let is_unchecked_union = !matches!(
|
||||
checker.semantic.current_expression_parent(),
|
||||
Some(Expr::BinOp(ast::ExprBinOp {
|
||||
op: Operator::BitOr,
|
||||
..
|
||||
}))
|
||||
);
|
||||
if checker.enabled(Rule::DuplicateUnionMember)
|
||||
&& checker.semantic.in_type_definition()
|
||||
&& is_unchecked_union
|
||||
{
|
||||
flake8_pyi::rules::duplicate_union_member(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryLiteralUnion) && is_unchecked_union {
|
||||
flake8_pyi::rules::unnecessary_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantLiteralUnion) && is_unchecked_union {
|
||||
flake8_pyi::rules::redundant_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryTypeUnion) && is_unchecked_union {
|
||||
flake8_pyi::rules::unnecessary_type_union(checker, expr);
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp(
|
||||
unary_op @ ast::ExprUnaryOp {
|
||||
op,
|
||||
operand,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.any_enabled(&[Rule::NotInTest, Rule::NotIsTest]) {
|
||||
pycodestyle::rules::not_tests(checker, unary_op);
|
||||
Expr::UnaryOp(ast::ExprUnaryOp {
|
||||
op,
|
||||
operand,
|
||||
range: _,
|
||||
}) => {
|
||||
let check_not_in = checker.enabled(Rule::NotInTest);
|
||||
let check_not_is = checker.enabled(Rule::NotIsTest);
|
||||
if check_not_in || check_not_is {
|
||||
pycodestyle::rules::not_tests(
|
||||
checker,
|
||||
expr,
|
||||
*op,
|
||||
operand,
|
||||
check_not_in,
|
||||
check_not_is,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::UnaryPrefixIncrementDecrement) {
|
||||
flake8_bugbear::rules::unary_prefix_increment_decrement(
|
||||
@@ -1144,8 +1149,18 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.any_enabled(&[Rule::NoneComparison, Rule::TrueFalseComparison]) {
|
||||
pycodestyle::rules::literal_comparisons(checker, compare);
|
||||
let check_none_comparisons = checker.enabled(Rule::NoneComparison);
|
||||
let check_true_false_comparisons = checker.enabled(Rule::TrueFalseComparison);
|
||||
if check_none_comparisons || check_true_false_comparisons {
|
||||
pycodestyle::rules::literal_comparisons(
|
||||
checker,
|
||||
expr,
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
check_none_comparisons,
|
||||
check_true_false_comparisons,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::IsLiteral) {
|
||||
pyflakes::rules::invalid_literal_comparison(checker, left, ops, comparators, expr);
|
||||
@@ -1182,7 +1197,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pylint::rules::magic_value_comparison(checker, left, comparators);
|
||||
}
|
||||
if checker.enabled(Rule::InDictKeys) {
|
||||
flake8_simplify::rules::key_in_dict_compare(checker, compare);
|
||||
flake8_simplify::rules::key_in_dict_compare(checker, expr, left, ops, comparators);
|
||||
}
|
||||
if checker.enabled(Rule::YodaConditions) {
|
||||
flake8_simplify::rules::yoda_conditions(checker, expr, left, ops, comparators);
|
||||
@@ -1228,7 +1243,13 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedTempFile) {
|
||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, expr, value);
|
||||
if let Some(diagnostic) = flake8_bandit::rules::hardcoded_tmp_directory(
|
||||
expr,
|
||||
value,
|
||||
&checker.settings.flake8_bandit.hardcoded_tmp_directory,
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnicodeKindPrefix) {
|
||||
pyupgrade::rules::unicode_kind_prefix(checker, expr, kind.as_deref());
|
||||
|
||||
@@ -6,6 +6,9 @@ use crate::rules::{flake8_bugbear, flake8_pyi, ruff};
|
||||
|
||||
/// Run lint rules over a [`Parameters`] syntax node.
|
||||
pub(crate) fn parameters(parameters: &Parameters, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::MutableArgumentDefault) {
|
||||
flake8_bugbear::rules::mutable_argument_default(checker, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::FunctionCallInDefaultArgument) {
|
||||
flake8_bugbear::rules::function_call_in_argument_default(checker, parameters);
|
||||
}
|
||||
|
||||
@@ -69,18 +69,24 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(
|
||||
function_def @ ast::StmtFunctionDef {
|
||||
is_async,
|
||||
name,
|
||||
decorator_list,
|
||||
returns,
|
||||
parameters,
|
||||
body,
|
||||
type_params,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
name,
|
||||
decorator_list,
|
||||
returns,
|
||||
parameters,
|
||||
body,
|
||||
type_params,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
|
||||
name,
|
||||
decorator_list,
|
||||
returns,
|
||||
parameters,
|
||||
body,
|
||||
type_params,
|
||||
..
|
||||
}) => {
|
||||
if checker.enabled(Rule::DjangoNonLeadingReceiverDecorator) {
|
||||
flake8_django::rules::non_leading_receiver_decorator(checker, decorator_list);
|
||||
}
|
||||
@@ -145,11 +151,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_pyi::rules::non_self_return_type(
|
||||
checker,
|
||||
stmt,
|
||||
*is_async,
|
||||
name,
|
||||
decorator_list,
|
||||
returns.as_ref().map(AsRef::as_ref),
|
||||
parameters,
|
||||
stmt.is_async_function_def_stmt(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::CustomTypeVarReturnType) {
|
||||
@@ -175,7 +181,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::BadExitAnnotation) {
|
||||
flake8_pyi::rules::bad_exit_annotation(checker, *is_async, name, parameters);
|
||||
flake8_pyi::rules::bad_exit_annotation(
|
||||
checker,
|
||||
stmt.is_async_function_def_stmt(),
|
||||
name,
|
||||
parameters,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantNumericUnion) {
|
||||
flake8_pyi::rules::redundant_numeric_union(checker, parameters);
|
||||
@@ -206,9 +217,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::CachedInstanceMethod) {
|
||||
flake8_bugbear::rules::cached_instance_method(checker, decorator_list);
|
||||
}
|
||||
if checker.enabled(Rule::MutableArgumentDefault) {
|
||||
flake8_bugbear::rules::mutable_argument_default(checker, function_def);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnnecessaryReturnNone,
|
||||
Rule::ImplicitReturnValue,
|
||||
@@ -300,7 +308,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.any_enabled(&[
|
||||
Rule::PytestParametrizeNamesWrongType,
|
||||
Rule::PytestParametrizeValuesWrongType,
|
||||
Rule::PytestDuplicateParametrizeTestCases,
|
||||
]) {
|
||||
flake8_pytest_style::rules::parametrize(checker, decorator_list);
|
||||
}
|
||||
@@ -310,16 +317,16 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
]) {
|
||||
flake8_pytest_style::rules::marks(checker, decorator_list);
|
||||
}
|
||||
if checker.enabled(Rule::BooleanTypeHintPositionalArgument) {
|
||||
flake8_boolean_trap::rules::boolean_type_hint_positional_argument(
|
||||
if checker.enabled(Rule::BooleanPositionalArgInFunctionDefinition) {
|
||||
flake8_boolean_trap::rules::check_positional_boolean_in_def(
|
||||
checker,
|
||||
name,
|
||||
decorator_list,
|
||||
parameters,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::BooleanDefaultValuePositionalArgument) {
|
||||
flake8_boolean_trap::rules::boolean_default_value_positional_argument(
|
||||
if checker.enabled(Rule::BooleanDefaultValueInFunctionDefinition) {
|
||||
flake8_boolean_trap::rules::check_boolean_default_value_in_function_definition(
|
||||
checker,
|
||||
name,
|
||||
decorator_list,
|
||||
@@ -338,6 +345,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::FStringDocstring) {
|
||||
flake8_bugbear::rules::f_string_docstring(checker, body);
|
||||
}
|
||||
if checker.enabled(Rule::YieldInForLoop) {
|
||||
pyupgrade::rules::yield_in_for_loop(checker, stmt);
|
||||
}
|
||||
if let ScopeKind::Class(class_def) = checker.semantic.current_scope().kind {
|
||||
if checker.enabled(Rule::BuiltinAttributeShadowing) {
|
||||
flake8_builtins::rules::builtin_method_shadowing(
|
||||
@@ -464,17 +474,17 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_pyi::rules::pass_statement_stub_body(checker, body);
|
||||
}
|
||||
if checker.enabled(Rule::PassInClassBody) {
|
||||
flake8_pyi::rules::pass_in_class_body(checker, class_def);
|
||||
flake8_pyi::rules::pass_in_class_body(checker, stmt, body);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::EllipsisInNonEmptyClassBody) {
|
||||
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, body);
|
||||
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, stmt, body);
|
||||
}
|
||||
if checker.enabled(Rule::PytestIncorrectMarkParenthesesStyle) {
|
||||
flake8_pytest_style::rules::marks(checker, decorator_list);
|
||||
}
|
||||
if checker.enabled(Rule::DuplicateClassFieldDefinition) {
|
||||
flake8_pie::rules::duplicate_class_field_definition(checker, body);
|
||||
flake8_pie::rules::duplicate_class_field_definition(checker, stmt, body);
|
||||
}
|
||||
if checker.enabled(Rule::NonUniqueEnums) {
|
||||
flake8_pie::rules::non_unique_enums(checker, stmt, body);
|
||||
@@ -509,16 +519,17 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::SingleStringSlots) {
|
||||
pylint::rules::single_string_slots(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::BadDunderMethodName) {
|
||||
pylint::rules::bad_dunder_method_name(checker, body);
|
||||
}
|
||||
}
|
||||
Stmt::Import(ast::StmtImport { names, range: _ }) => {
|
||||
if checker.enabled(Rule::MultipleImportsOnOneLine) {
|
||||
pycodestyle::rules::multiple_imports_on_one_line(checker, stmt, names);
|
||||
}
|
||||
if checker.enabled(Rule::ModuleImportNotAtTopOfFile) {
|
||||
pycodestyle::rules::module_import_not_at_top_of_file(checker, stmt);
|
||||
pycodestyle::rules::module_import_not_at_top_of_file(
|
||||
checker,
|
||||
stmt,
|
||||
checker.locator,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
for name in names {
|
||||
@@ -554,29 +565,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::BannedApi) {
|
||||
flake8_tidy_imports::rules::banned_api(
|
||||
flake8_tidy_imports::rules::name_or_parent_is_banned(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchNameOrParent(
|
||||
flake8_tidy_imports::matchers::MatchNameOrParent {
|
||||
module: &alias.name,
|
||||
},
|
||||
),
|
||||
&alias,
|
||||
&alias.name,
|
||||
alias,
|
||||
);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::BannedModuleLevelImports) {
|
||||
flake8_tidy_imports::rules::banned_module_level_imports(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchNameOrParent(
|
||||
flake8_tidy_imports::matchers::MatchNameOrParent {
|
||||
module: &alias.name,
|
||||
},
|
||||
),
|
||||
&alias,
|
||||
);
|
||||
}
|
||||
|
||||
if !checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::UselessImportAlias) {
|
||||
pylint::rules::useless_import_alias(checker, alias);
|
||||
@@ -691,7 +685,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
let module = module.as_deref();
|
||||
let level = level.map(|level| level.to_u32());
|
||||
if checker.enabled(Rule::ModuleImportNotAtTopOfFile) {
|
||||
pycodestyle::rules::module_import_not_at_top_of_file(checker, stmt);
|
||||
pycodestyle::rules::module_import_not_at_top_of_file(
|
||||
checker,
|
||||
stmt,
|
||||
checker.locator,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
for name in names {
|
||||
@@ -727,56 +725,16 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if let Some(module) =
|
||||
helpers::resolve_imported_module_path(level, module, checker.module_path)
|
||||
{
|
||||
flake8_tidy_imports::rules::banned_api(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchNameOrParent(
|
||||
flake8_tidy_imports::matchers::MatchNameOrParent { module: &module },
|
||||
),
|
||||
&stmt,
|
||||
);
|
||||
flake8_tidy_imports::rules::name_or_parent_is_banned(checker, &module, stmt);
|
||||
|
||||
for alias in names {
|
||||
if &alias.name == "*" {
|
||||
continue;
|
||||
}
|
||||
flake8_tidy_imports::rules::banned_api(
|
||||
flake8_tidy_imports::rules::name_is_banned(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchName(
|
||||
flake8_tidy_imports::matchers::MatchName {
|
||||
module: &module,
|
||||
member: &alias.name,
|
||||
},
|
||||
),
|
||||
&alias,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::BannedModuleLevelImports) {
|
||||
if let Some(module) =
|
||||
helpers::resolve_imported_module_path(level, module, checker.module_path)
|
||||
{
|
||||
flake8_tidy_imports::rules::banned_module_level_imports(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchNameOrParent(
|
||||
flake8_tidy_imports::matchers::MatchNameOrParent { module: &module },
|
||||
),
|
||||
&stmt,
|
||||
);
|
||||
|
||||
for alias in names {
|
||||
if &alias.name == "*" {
|
||||
continue;
|
||||
}
|
||||
flake8_tidy_imports::rules::banned_module_level_imports(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchName(
|
||||
flake8_tidy_imports::matchers::MatchName {
|
||||
module: &module,
|
||||
member: &alias.name,
|
||||
},
|
||||
),
|
||||
&alias,
|
||||
format!("{module}.{}", alias.name),
|
||||
alias,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1097,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pygrep_hooks::rules::non_existent_mock_method(checker, test);
|
||||
}
|
||||
}
|
||||
Stmt::With(with_stmt @ ast::StmtWith { items, body, .. }) => {
|
||||
Stmt::With(ast::StmtWith { items, body, .. })
|
||||
| Stmt::AsyncWith(ast::StmtAsyncWith { items, body, .. }) => {
|
||||
if checker.enabled(Rule::AssertRaisesException) {
|
||||
flake8_bugbear::rules::assert_raises_exception(checker, items);
|
||||
}
|
||||
@@ -1149,7 +1108,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::MultipleWithStatements) {
|
||||
flake8_simplify::rules::multiple_with_statements(
|
||||
checker,
|
||||
with_stmt,
|
||||
stmt,
|
||||
body,
|
||||
checker.semantic.current_statement_parent(),
|
||||
);
|
||||
}
|
||||
@@ -1168,21 +1128,22 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
perflint::rules::try_except_in_loop(checker, body);
|
||||
}
|
||||
}
|
||||
Stmt::For(
|
||||
for_stmt @ ast::StmtFor {
|
||||
target,
|
||||
body,
|
||||
iter,
|
||||
orelse,
|
||||
is_async,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnusedLoopControlVariable,
|
||||
Rule::IncorrectDictIterator,
|
||||
Rule::YieldInForLoop,
|
||||
]) {
|
||||
Stmt::For(ast::StmtFor {
|
||||
target,
|
||||
body,
|
||||
iter,
|
||||
orelse,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFor(ast::StmtAsyncFor {
|
||||
target,
|
||||
body,
|
||||
iter,
|
||||
orelse,
|
||||
..
|
||||
}) => {
|
||||
if checker.any_enabled(&[Rule::UnusedLoopControlVariable, Rule::IncorrectDictIterator])
|
||||
{
|
||||
checker.deferred.for_loops.push(checker.semantic.snapshot());
|
||||
}
|
||||
if checker.enabled(Rule::LoopVariableOverridesIterator) {
|
||||
@@ -1203,6 +1164,17 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::IterationOverSet) {
|
||||
pylint::rules::iteration_over_set(checker, iter);
|
||||
}
|
||||
if stmt.is_for_stmt() {
|
||||
if checker.enabled(Rule::ReimplementedBuiltin) {
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt);
|
||||
}
|
||||
if checker.enabled(Rule::InDictKeys) {
|
||||
flake8_simplify::rules::key_in_dict_for(checker, target, iter);
|
||||
}
|
||||
if checker.enabled(Rule::TryExceptInLoop) {
|
||||
perflint::rules::try_except_in_loop(checker, body);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::ManualListComprehension) {
|
||||
perflint::rules::manual_list_comprehension(checker, target, body);
|
||||
}
|
||||
@@ -1212,24 +1184,20 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryListCast) {
|
||||
perflint::rules::unnecessary_list_cast(checker, iter);
|
||||
}
|
||||
if !is_async {
|
||||
if checker.enabled(Rule::ReimplementedBuiltin) {
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt);
|
||||
}
|
||||
if checker.enabled(Rule::InDictKeys) {
|
||||
flake8_simplify::rules::key_in_dict_for(checker, for_stmt);
|
||||
}
|
||||
if checker.enabled(Rule::TryExceptInLoop) {
|
||||
perflint::rules::try_except_in_loop(checker, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
range: _,
|
||||
})
|
||||
| Stmt::TryStar(ast::StmtTryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
range: _,
|
||||
}) => {
|
||||
if checker.enabled(Rule::JumpStatementInFinally) {
|
||||
flake8_bugbear::rules::jump_statement_in_finally(checker, finalbody);
|
||||
@@ -1371,7 +1339,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if !checker
|
||||
.semantic
|
||||
.current_scopes()
|
||||
.any(|scope| scope.kind.is_function())
|
||||
.any(|scope| scope.kind.is_any_function())
|
||||
{
|
||||
if checker.enabled(Rule::UnprefixedTypeParam) {
|
||||
flake8_pyi::rules::prefix_type_params(checker, value, targets);
|
||||
@@ -1436,7 +1404,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if !checker
|
||||
.semantic
|
||||
.current_scopes()
|
||||
.any(|scope| scope.kind.is_function())
|
||||
.any(|scope| scope.kind.is_any_function())
|
||||
{
|
||||
flake8_pyi::rules::annotated_assignment_default_in_stub(
|
||||
checker, target, value, annotation,
|
||||
|
||||
@@ -32,8 +32,8 @@ use itertools::Itertools;
|
||||
use log::error;
|
||||
use ruff_python_ast::{
|
||||
self as ast, Arguments, Comprehension, Constant, ElifElseClause, ExceptHandler, Expr,
|
||||
ExprContext, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Ranged,
|
||||
Stmt, Suite, UnaryOp,
|
||||
ExprContext, Keyword, Parameter, ParameterWithDefault, Parameters, Pattern, Ranged, Stmt,
|
||||
Suite, UnaryOp,
|
||||
};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
@@ -52,8 +52,7 @@ use ruff_python_parser::typing::{parse_type_annotation, AnnotationKind};
|
||||
use ruff_python_semantic::analyze::{typing, visibility};
|
||||
use ruff_python_semantic::{
|
||||
BindingFlags, BindingId, BindingKind, Exceptions, Export, FromImport, Globals, Import, Module,
|
||||
ModuleKind, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, StarImport,
|
||||
SubmoduleImport,
|
||||
ModuleKind, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, StarImport, SubmoduleImport,
|
||||
};
|
||||
use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
use ruff_source_file::Locator;
|
||||
@@ -177,12 +176,13 @@ impl<'a> Checker<'a> {
|
||||
///
|
||||
/// If the current expression in the context is not an f-string, returns ``None``.
|
||||
pub(crate) fn f_string_quote_style(&self) -> Option<Quote> {
|
||||
if !self.semantic.in_f_string() {
|
||||
let model = &self.semantic;
|
||||
if !model.in_f_string() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find the quote character used to start the containing f-string.
|
||||
let expr = self.semantic.current_expression()?;
|
||||
let expr = model.current_expression()?;
|
||||
let string_range = self.indexer.f_string_range(expr.start())?;
|
||||
let trailing_quote = trailing_quote(self.locator.slice(string_range))?;
|
||||
|
||||
@@ -194,6 +194,20 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`IsolationLevel`] for fixes in the current context.
|
||||
///
|
||||
/// The primary use-case for fix isolation is to ensure that we don't delete all statements
|
||||
/// in a given indented block, which would cause a syntax error. We therefore need to ensure
|
||||
/// that we delete at most one statement per indented block per fixer pass. Fix isolation should
|
||||
/// thus be applied whenever we delete a statement, but can otherwise be omitted.
|
||||
pub(crate) fn isolation(&self, parent: Option<&Stmt>) -> IsolationLevel {
|
||||
parent
|
||||
.and_then(|stmt| self.semantic.statement_id(stmt))
|
||||
.map_or(IsolationLevel::default(), |node_id| {
|
||||
IsolationLevel::Group(node_id.into())
|
||||
})
|
||||
}
|
||||
|
||||
/// The [`Locator`] for the current file, which enables extraction of source code from byte
|
||||
/// offsets.
|
||||
pub(crate) const fn locator(&self) -> &'a Locator<'a> {
|
||||
@@ -242,18 +256,6 @@ impl<'a> Checker<'a> {
|
||||
pub(crate) const fn any_enabled(&self, rules: &[Rule]) -> bool {
|
||||
self.settings.rules.any_enabled(rules)
|
||||
}
|
||||
|
||||
/// Returns the [`IsolationLevel`] to isolate fixes for a given node.
|
||||
///
|
||||
/// The primary use-case for fix isolation is to ensure that we don't delete all statements
|
||||
/// in a given indented block, which would cause a syntax error. We therefore need to ensure
|
||||
/// that we delete at most one statement per indented block per fixer pass. Fix isolation should
|
||||
/// thus be applied whenever we delete a statement, but can otherwise be omitted.
|
||||
pub(crate) fn isolation(node_id: Option<NodeId>) -> IsolationLevel {
|
||||
node_id
|
||||
.map(|node_id| IsolationLevel::Group(node_id.into()))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Visitor<'b> for Checker<'a>
|
||||
@@ -262,7 +264,7 @@ where
|
||||
{
|
||||
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
||||
// Step 0: Pre-processing
|
||||
self.semantic.push_node(stmt);
|
||||
self.semantic.push_statement(stmt);
|
||||
|
||||
// Track whether we've seen docstrings, non-imports, etc.
|
||||
match stmt {
|
||||
@@ -453,16 +455,22 @@ where
|
||||
|
||||
// Step 2: Traversal
|
||||
match stmt {
|
||||
Stmt::FunctionDef(
|
||||
function_def @ ast::StmtFunctionDef {
|
||||
body,
|
||||
parameters,
|
||||
decorator_list,
|
||||
returns,
|
||||
type_params,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
body,
|
||||
parameters,
|
||||
decorator_list,
|
||||
returns,
|
||||
type_params,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
|
||||
body,
|
||||
parameters,
|
||||
decorator_list,
|
||||
type_params,
|
||||
returns,
|
||||
..
|
||||
}) => {
|
||||
// Visit the decorators and arguments, but avoid the body, which will be
|
||||
// deferred.
|
||||
for decorator in decorator_list {
|
||||
@@ -523,7 +531,8 @@ where
|
||||
}
|
||||
|
||||
let definition = docstrings::extraction::extract_definition(
|
||||
ExtractionTarget::Function(function_def),
|
||||
ExtractionTarget::Function,
|
||||
stmt,
|
||||
self.semantic.definition_id,
|
||||
&self.semantic.definitions,
|
||||
);
|
||||
@@ -531,7 +540,8 @@ where
|
||||
|
||||
self.semantic.push_scope(match &stmt {
|
||||
Stmt::FunctionDef(stmt) => ScopeKind::Function(stmt),
|
||||
_ => unreachable!("Expected Stmt::FunctionDef"),
|
||||
Stmt::AsyncFunctionDef(stmt) => ScopeKind::AsyncFunction(stmt),
|
||||
_ => unreachable!("Expected Stmt::FunctionDef | Stmt::AsyncFunctionDef"),
|
||||
});
|
||||
|
||||
self.deferred.functions.push(self.semantic.snapshot());
|
||||
@@ -565,7 +575,8 @@ where
|
||||
}
|
||||
|
||||
let definition = docstrings::extraction::extract_definition(
|
||||
ExtractionTarget::Class(class_def),
|
||||
ExtractionTarget::Class,
|
||||
stmt,
|
||||
self.semantic.definition_id,
|
||||
&self.semantic.definitions,
|
||||
);
|
||||
@@ -598,7 +609,14 @@ where
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
range: _,
|
||||
})
|
||||
| Stmt::TryStar(ast::StmtTryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
range: _,
|
||||
}) => {
|
||||
let mut handled_exceptions = Exceptions::empty();
|
||||
for type_ in extract_handled_exceptions(handlers) {
|
||||
@@ -618,28 +636,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the `body`, then the `handlers`, then the `orelse`, then the
|
||||
// `finalbody`, but treat the body and the `orelse` as a single branch for
|
||||
// flow analysis purposes.
|
||||
let branch = self.semantic.push_branch();
|
||||
self.semantic.handled_exceptions.push(handled_exceptions);
|
||||
self.visit_body(body);
|
||||
self.semantic.handled_exceptions.pop();
|
||||
self.semantic.pop_branch();
|
||||
|
||||
for except_handler in handlers {
|
||||
self.semantic.push_branch();
|
||||
self.visit_except_handler(except_handler);
|
||||
self.semantic.pop_branch();
|
||||
}
|
||||
|
||||
self.semantic.set_branch(branch);
|
||||
self.visit_body(orelse);
|
||||
self.semantic.pop_branch();
|
||||
|
||||
self.semantic.push_branch();
|
||||
self.visit_body(finalbody);
|
||||
self.semantic.pop_branch();
|
||||
}
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
target,
|
||||
@@ -719,7 +725,6 @@ where
|
||||
) => {
|
||||
self.visit_boolean_test(test);
|
||||
|
||||
self.semantic.push_branch();
|
||||
if typing::is_type_checking_block(stmt_if, &self.semantic) {
|
||||
if self.semantic.at_top_level() {
|
||||
self.importer.visit_type_checking_block(stmt);
|
||||
@@ -728,12 +733,9 @@ where
|
||||
} else {
|
||||
self.visit_body(body);
|
||||
}
|
||||
self.semantic.pop_branch();
|
||||
|
||||
for clause in elif_else_clauses {
|
||||
self.semantic.push_branch();
|
||||
self.visit_elif_else_clause(clause);
|
||||
self.semantic.pop_branch();
|
||||
}
|
||||
}
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
@@ -741,7 +743,8 @@ where
|
||||
|
||||
// Step 3: Clean-up
|
||||
match stmt {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. }) => {
|
||||
let scope_id = self.semantic.scope_id;
|
||||
self.deferred.scopes.push(scope_id);
|
||||
self.semantic.pop_scope(); // Function scope
|
||||
@@ -774,7 +777,7 @@ where
|
||||
analyze::statement(stmt, self);
|
||||
|
||||
self.semantic.flags = flags_snapshot;
|
||||
self.semantic.pop_node();
|
||||
self.semantic.pop_statement();
|
||||
}
|
||||
|
||||
fn visit_annotation(&mut self, expr: &'b Expr) {
|
||||
@@ -810,7 +813,7 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
self.semantic.push_node(expr);
|
||||
self.semantic.push_expression(expr);
|
||||
|
||||
// Store the flags prior to any further descent, so that we can restore them after visiting
|
||||
// the node.
|
||||
@@ -889,20 +892,18 @@ where
|
||||
},
|
||||
) => {
|
||||
// Visit the default arguments, but avoid the body, which will be deferred.
|
||||
if let Some(parameters) = parameters {
|
||||
for ParameterWithDefault {
|
||||
default,
|
||||
parameter: _,
|
||||
range: _,
|
||||
} in parameters
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(¶meters.args)
|
||||
.chain(¶meters.kwonlyargs)
|
||||
{
|
||||
if let Some(expr) = &default {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
for ParameterWithDefault {
|
||||
default,
|
||||
parameter: _,
|
||||
range: _,
|
||||
} in parameters
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(¶meters.args)
|
||||
.chain(¶meters.kwonlyargs)
|
||||
{
|
||||
if let Some(expr) = &default {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1206,7 +1207,7 @@ where
|
||||
));
|
||||
}
|
||||
}
|
||||
Expr::FString(_) => {
|
||||
Expr::JoinedStr(_) => {
|
||||
self.semantic.flags |= SemanticModelFlags::F_STRING;
|
||||
visitor::walk_expr(self, expr);
|
||||
}
|
||||
@@ -1230,7 +1231,7 @@ where
|
||||
analyze::expression(expr, self);
|
||||
|
||||
self.semantic.flags = flags_snapshot;
|
||||
self.semantic.pop_node();
|
||||
self.semantic.pop_expression();
|
||||
}
|
||||
|
||||
fn visit_except_handler(&mut self, except_handler: &'b ExceptHandler) {
|
||||
@@ -1285,7 +1286,7 @@ where
|
||||
|
||||
fn visit_format_spec(&mut self, format_spec: &'b Expr) {
|
||||
match format_spec {
|
||||
Expr::FString(ast::ExprFString { values, .. }) => {
|
||||
Expr::JoinedStr(ast::ExprJoinedStr { values, range: _ }) => {
|
||||
for value in values {
|
||||
self.visit_expr(value);
|
||||
}
|
||||
@@ -1368,17 +1369,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_match_case(&mut self, match_case: &'b MatchCase) {
|
||||
self.visit_pattern(&match_case.pattern);
|
||||
if let Some(expr) = &match_case.guard {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
|
||||
self.semantic.push_branch();
|
||||
self.visit_body(&match_case.body);
|
||||
self.semantic.pop_branch();
|
||||
}
|
||||
|
||||
fn visit_type_param(&mut self, type_param: &'b ast::TypeParam) {
|
||||
// Step 1: Binding
|
||||
match type_param {
|
||||
@@ -1636,7 +1626,7 @@ impl<'a> Checker<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
if parent.is_for_stmt() {
|
||||
if matches!(parent, Stmt::For(_) | Stmt::AsyncFor(_)) {
|
||||
self.add_binding(
|
||||
id,
|
||||
expr.range(),
|
||||
@@ -1835,14 +1825,19 @@ impl<'a> Checker<'a> {
|
||||
for snapshot in deferred_functions {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if let Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
body, parameters, ..
|
||||
}) = self.semantic.current_statement()
|
||||
{
|
||||
self.visit_parameters(parameters);
|
||||
self.visit_body(body);
|
||||
} else {
|
||||
unreachable!("Expected Stmt::FunctionDef")
|
||||
match &self.semantic.current_statement() {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
body, parameters, ..
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
|
||||
body, parameters, ..
|
||||
}) => {
|
||||
self.visit_parameters(parameters);
|
||||
self.visit_body(body);
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Expected Stmt::FunctionDef | Stmt::AsyncFunctionDef")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1862,9 +1857,7 @@ impl<'a> Checker<'a> {
|
||||
range: _,
|
||||
}) = expr
|
||||
{
|
||||
if let Some(parameters) = parameters {
|
||||
self.visit_parameters(parameters);
|
||||
}
|
||||
self.visit_parameters(parameters);
|
||||
self.visit_expr(body);
|
||||
} else {
|
||||
unreachable!("Expected Expr::Lambda");
|
||||
@@ -1885,7 +1878,7 @@ impl<'a> Checker<'a> {
|
||||
.map(|binding_id| &self.semantic.bindings[binding_id])
|
||||
.filter_map(|binding| match &binding.kind {
|
||||
BindingKind::Export(Export { names }) => {
|
||||
Some(names.iter().map(|name| (*name, binding.range())))
|
||||
Some(names.iter().map(|name| (*name, binding.range)))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user