Compare commits

..

28 Commits

Author SHA1 Message Date
Charlie Marsh
138b06c98a Bump version to 0.0.92 2022-10-30 18:04:30 -04:00
Charlie Marsh
2415d73260 Remove RustPython fork (#523) 2022-10-30 18:04:05 -04:00
Charlie Marsh
b060ae2f22 Move SourceCodeLocator to its own module (#522) 2022-10-30 15:51:59 -04:00
Charlie Marsh
9aa91d3d3c Add a cargo bench for SourceCodeLocator (#521) 2022-10-30 13:50:42 -04:00
Charlie Marsh
dcedb801e5 Tweak a few check messages (#520) 2022-10-30 13:13:37 -04:00
Charlie Marsh
d3e7fdabb5 Implement consistent newline handling for SourceCodeLocator (#519) 2022-10-30 13:11:08 -04:00
Charlie Marsh
dfa5a4f0f7 Fix 9/32 flake8-bugbear reference 2022-10-30 13:03:39 -04:00
Charlie Marsh
58a2d600da Avoid flagging D202 for inner functions and classes (#518) 2022-10-30 13:02:55 -04:00
Harutaka Kawamura
7ecbfe4f6a Implement B006 (#515) 2022-10-30 12:57:57 -04:00
Charlie Marsh
d8248104a7 Avoid re-indenting empty lines in D207 (#517) 2022-10-30 09:41:54 -04:00
Charlie Marsh
6eb09122c0 Add final newline in SourceCodeLocator 2022-10-29 19:23:16 -04:00
Charlie Marsh
f84c1f1fa1 Bump version to 0.0.91 2022-10-29 18:49:26 -04:00
Charlie Marsh
3aa9528229 Avoid flake8-comprehensions errors for dicts with kwargs (#512) 2022-10-29 18:49:06 -04:00
Charlie Marsh
5a3f06bab1 Bump version to 0.0.90 2022-10-29 18:34:38 -04:00
Charlie Marsh
db59d5b558 Use a single SourceCodeLocator everywhere (#510) 2022-10-29 18:23:24 -04:00
Charlie Marsh
2fcbf3ab62 Simplify SourceCodeLocator offset computation (#509) 2022-10-29 18:13:42 -04:00
Anders Kaseorg
fa9b10be72 Remove leading space from C416 message (#508) 2022-10-29 17:40:50 -04:00
Charlie Marsh
c495cef529 Move pyproject.toml logging to debug (#506) 2022-10-29 17:07:46 -04:00
Charlie Marsh
c0c8dff6ce Implement configuration options for pep8-naming (#505) 2022-10-29 17:00:30 -04:00
Charlie Marsh
80b00cc89f Add error code categories to table of contents (#504) 2022-10-29 16:39:55 -04:00
Charlie Marsh
934db3d179 Bump version to 0.0.89 2022-10-29 15:39:17 -04:00
Charlie Marsh
6a040a0405 Update checks_gen.rs 2022-10-29 15:39:02 -04:00
Harutaka Kawamura
2821ef0f69 Implement B013 (#503) 2022-10-29 15:36:29 -04:00
Harutaka Kawamura
343d931ddb Ignore unittest methods and functions in N802 (#502) 2022-10-29 15:36:09 -04:00
Harutaka Kawamura
3fc257f71b Implement N806, 815, 816, 818 (#501) 2022-10-29 15:35:56 -04:00
Charlie Marsh
6dbb0a17e9 Update README to reflect selection groups 2022-10-28 19:15:46 -04:00
Charlie Marsh
ae5ad6a4ac Bump version to 0.0.88 2022-10-28 19:11:04 -04:00
Charlie Marsh
549af6c584 Regenerate CheckCodePrefix 2022-10-28 19:10:45 -04:00
211 changed files with 3574 additions and 2462 deletions

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.87
rev: v0.0.92
hooks:
- id: ruff

169
Cargo.lock generated
View File

@@ -37,6 +37,12 @@ dependencies = [
"libc",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "annotate-snippets"
version = "0.6.1"
@@ -368,6 +374,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.73"
@@ -416,6 +428,45 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"bitflags",
"clap_lex 0.2.4",
"indexmap",
"textwrap 0.16.0",
]
[[package]]
name = "clap"
version = "4.0.15"
@@ -425,7 +476,7 @@ dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex",
"clap_lex 0.3.0",
"once_cell",
"strsim",
"termcolor",
@@ -444,6 +495,15 @@ dependencies = [
"syn",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "clap_lex"
version = "0.3.0"
@@ -548,6 +608,42 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "criterion"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap 3.2.23",
"criterion-plot",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
@@ -1041,6 +1137,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1571,6 +1673,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
version = "0.2.3"
@@ -1779,6 +1887,34 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "plotters"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
[[package]]
name = "polling"
version = "2.3.0"
@@ -2054,18 +2190,19 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.87"
version = "0.0.92"
dependencies = [
"anyhow",
"assert_cmd",
"bincode",
"cacache",
"chrono",
"clap",
"clap 4.0.15",
"clearscreen",
"codegen",
"colored",
"common-path",
"criterion",
"dirs 4.0.0",
"fern",
"filetime",
@@ -2089,7 +2226,7 @@ dependencies = [
"strum",
"strum_macros",
"test-case",
"textwrap",
"textwrap 0.15.1",
"titlecase",
"toml",
"update-informer",
@@ -2111,7 +2248,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=1b253a12705f84972cd76e8dc1cdaaccb233e5a5#1b253a12705f84972cd76e8dc1cdaaccb233e5a5"
source = "git+https://github.com/RustPython/RustPython.git?rev=77b821a1941019fe34f73ce17cea013ae1b98fd0#77b821a1941019fe34f73ce17cea013ae1b98fd0"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -2121,7 +2258,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=1b253a12705f84972cd76e8dc1cdaaccb233e5a5#1b253a12705f84972cd76e8dc1cdaaccb233e5a5"
source = "git+https://github.com/RustPython/RustPython.git?rev=77b821a1941019fe34f73ce17cea013ae1b98fd0#77b821a1941019fe34f73ce17cea013ae1b98fd0"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -2144,7 +2281,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=1b253a12705f84972cd76e8dc1cdaaccb233e5a5#1b253a12705f84972cd76e8dc1cdaaccb233e5a5"
source = "git+https://github.com/RustPython/RustPython.git?rev=77b821a1941019fe34f73ce17cea013ae1b98fd0#77b821a1941019fe34f73ce17cea013ae1b98fd0"
dependencies = [
"bincode",
"bitflags",
@@ -2161,7 +2298,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=1b253a12705f84972cd76e8dc1cdaaccb233e5a5#1b253a12705f84972cd76e8dc1cdaaccb233e5a5"
source = "git+https://github.com/RustPython/RustPython.git?rev=77b821a1941019fe34f73ce17cea013ae1b98fd0#77b821a1941019fe34f73ce17cea013ae1b98fd0"
dependencies = [
"ahash",
"anyhow",
@@ -2549,6 +2686,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.37"
@@ -2589,6 +2732,16 @@ dependencies = [
"crunchy",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.6.0"

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.87"
version = "0.0.92"
edition = "2021"
[lib]
@@ -26,9 +26,9 @@ once_cell = { version = "1.13.1" }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/charliermarsh/RustPython.git", rev = "1b253a12705f84972cd76e8dc1cdaaccb233e5a5" }
rustpython-common = { git = "https://github.com/charliermarsh/RustPython.git", rev = "1b253a12705f84972cd76e8dc1cdaaccb233e5a5" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/charliermarsh/RustPython.git", rev = "1b253a12705f84972cd76e8dc1cdaaccb233e5a5" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "77b821a1941019fe34f73ce17cea013ae1b98fd0" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "77b821a1941019fe34f73ce17cea013ae1b98fd0" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "77b821a1941019fe34f73ce17cea013ae1b98fd0" }
serde = { version = "1.0.143", features = ["derive"] }
serde_json = { version = "1.0.83" }
strum = { version = "0.24.1", features = ["strum_macros"] }
@@ -51,6 +51,7 @@ getrandom = { version = "0.2.7", features = ["js"] }
[dev-dependencies]
assert_cmd = { version = "2.0.4" }
codegen = { version = "0.2.0" }
criterion = { version = "0.4.0" }
insta = { version = "1.19.1", features = ["yaml"] }
test-case = { version = "2.2.2" }
@@ -69,3 +70,7 @@ opt-level = 3
[profile.dev.package.similar]
opt-level = 3
[[bench]]
name = "source_code_locator"
harness = false

164
README.md
View File

@@ -38,13 +38,25 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
1. [Installation and Usage](#installation-and-usage)
2. [Configuration](#configuration)
3. [Supported Rules](#supported-rules)
4. [Editor Integrations](#editor-integrations)
5. [FAQ](#faq)
6. [Development](#development)
7. [Releases](#releases)
8. [Benchmarks](#benchmarks)
9. [License](#license)
10. [Contributing](#contributing)
1. [Pyflakes](#pyflakes)
2. [pycodestyle (error)](#pycodestyle-error)
3. [pycodestyle (warning)](#pycodestyle-warning)
4. [pydocstyle](#pydocstyle)
5. [pyupgrade](#pyupgrade)
6. [pep8-naming](#pep8-naming)
7. [flake8-comprehensions](#flake8-comprehensions)
8. [flake8-bugbear](#flake8-bugbear)
9. [flake8-builtins](#flake8-builtins)
10. [flake8-print](#flake8-print)
11. [flake8-quotes](#flake8-quotes)
12. [Meta rules](#meta-rules)
5. [Editor Integrations](#editor-integrations)
6. [FAQ](#faq)
7. [Development](#development)
8. [Releases](#releases)
9. [Benchmarks](#benchmarks)
10. [License](#license)
11. [Contributing](#contributing)
## Installation and Usage
@@ -77,7 +89,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.87
rev: v0.0.92
hooks:
- id: ruff
```
@@ -94,10 +106,8 @@ For example, you could configure Ruff to only enforce a subset of rules with:
```toml
[tool.ruff]
line-length = 88
select = [
"F401",
"F403",
]
select = ["E", "F"]
ignore = ["E501"]
per-file-ignores = [
"__init__.py:F401",
"path/to/file.py:F401"
@@ -236,8 +246,7 @@ add `noqa` directives to all failing lines, with the appropriate error codes.**
## Supported Rules
By default, Ruff enables all `E`, `W`, and `F` error codes, which correspond to those built-in to
Flake8.
By default, Ruff enables all `E` and `F` error codes, which correspond to those built-in to Flake8.
The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` command-line option.
@@ -263,7 +272,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
| F634 | IfTuple | If test is a tuple, which is always `True` | |
| F701 | BreakOutsideLoop | `break` outside loop | |
| F702 | ContinueOutsideLoop | `continue` not properly in loop | |
| F704 | YieldOutsideFunction | `yield` or `yield from` statement outside of a function/method | |
| F704 | YieldOutsideFunction | `yield` or `yield from` statement outside of a function | |
| F706 | ReturnOutsideFunction | `return` statement outside of a function/method | |
| F707 | DefaultExceptNotLast | An `except:` block as not the last exception handler | |
| F722 | ForwardAnnotationSyntaxError | Syntax error in forward annotation: `...` | |
@@ -332,7 +341,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
| D400 | EndsInPeriod | First line should end with a period | |
| D402 | NoSignature | First line should not be the function's signature | |
| D403 | FirstLineCapitalized | First word of the first line should be properly capitalized | |
| D404 | NoThisPrefix | First word of the docstring should not be `This` | |
| D404 | NoThisPrefix | First word of the docstring should not be 'This' | |
| D405 | CapitalizeSectionName | Section name should be properly capitalized ("returns") | 🛠 |
| D406 | NewLineAfterSectionName | Section name should end with a newline ("Returns") | 🛠 |
| D407 | DashedUnderlineAfterSection | Missing dashed underline after section ("Returns") | 🛠 |
@@ -346,7 +355,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
| D415 | EndsInPunctuation | First line should end with a period, question mark, or exclamation point | |
| D416 | SectionNameEndsInColon | Section name should end with a colon ("Returns") | 🛠 |
| D417 | DocumentAllArguments | Missing argument descriptions in the docstring: `x`, `y` | |
| D418 | SkipDocstring | Function decorated with @overload shouldn't contain a docstring | |
| D418 | SkipDocstring | Function decorated with `@overload` shouldn't contain a docstring | |
| D419 | NonEmpty | Docstring is empty | |
### pyupgrade
@@ -371,12 +380,16 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
| N803 | InvalidArgumentName | Argument name `...` should be lowercase | |
| N804 | InvalidFirstArgumentNameForClassMethod | First argument of a class method should be named `cls` | |
| N805 | InvalidFirstArgumentNameForMethod | First argument of a method should be named `self` | |
| N806 | NonLowercaseVariableInFunction | Variable `...` in function should be lowercase | |
| N807 | DunderFunctionName | Function name should not start and end with `__` | |
| N811 | ConstantImportedAsNonConstant | Constant `...` imported as non-constant `...` | |
| N812 | LowercaseImportedAsNonLowercase | Lowercase `...` imported as non-lowercase `...` | |
| N813 | CamelcaseImportedAsLowercase | Camelcase `...` imported as lowercase `...` | |
| N814 | CamelcaseImportedAsConstant | Camelcase `...` imported as constant `...` | |
| N815 | MixedCaseVariableInClassScope | Variable `mixedCase` in class scope should not be mixedCase | |
| N816 | MixedCaseVariableInGlobalScope | Variable `mixedCase` in global scope should not be mixedCase | |
| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` | |
| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix | |
### flake8-comprehensions
@@ -396,7 +409,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
| C413 | UnnecessaryCallAroundSorted | Unnecessary `(list\|reversed)` call around `sorted()` | |
| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` | |
| C415 | UnnecessarySubscriptReversal | Unnecessary subscript reversal of iterable within `(reversed\|set\|sorted)()` | |
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | |
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | |
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
### flake8-bugbear
@@ -404,8 +417,10 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| B002 | UnaryPrefixIncrement | Python does not support the unary prefix increment. | |
| B006 | MutableArgumentDefault | Do not use mutable data structures for argument defaults. | |
| B007 | UnusedLoopControlVariable | Loop control variable `i` not used within the loop body. | 🛠 |
| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 |
| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError:` instead of `except (ValueError,):`. | |
| B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` | 🛠 |
| B017 | NoAssertRaisesException | `assertRaises(Exception):` should be considered evil. | |
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | |
@@ -500,6 +515,7 @@ Ruff re-implements some of the most popular Flake8 plugins and related code qual
including:
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`yesqa`](https://github.com/asottile/yesqa)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
@@ -507,7 +523,7 @@ including:
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (9/32)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (10/32)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (8/34)
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
@@ -522,13 +538,14 @@ Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis F
Today, Ruff can be used to replace Flake8 when used with any of the following plugins:
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (9/32)
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (10/32)
Ruff also implements the functionality that you get from [`yesqa`](https://github.com/asottile/yesqa),
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (8/34).
@@ -557,35 +574,55 @@ Yes! To enable a specific docstring convention, start by enabling all `pydocstyl
then selectively disabling based on your [preferred convention](https://www.pydocstyle.org/en/latest/error_codes.html#default-conventions).
For example, if you're coming from `flake8-docstrings`, the following configuration is equivalent to
`--docstring-convention numpy`:
`--docstring-convention=numpy`:
```toml
[tool.ruff]
extend-select = [
"D100",
"D101",
"D102",
"D103",
"D104",
"D105",
"D106",
"D200",
"D201",
"D202",
extend-select = ["D"]
extend-ignore = [
"D107",
"D203",
"D212",
"D213",
"D402",
"D413",
"D415",
"D416",
"D417",
]
```
Similarly, the following is equivalent to `--docstring-convention=google`:
```toml
[tool.ruff]
extend-select = ["D"]
extend-ignore = [
"D203",
"D204",
"D205",
"D206",
"D207",
"D208",
"D209",
"D210",
"D211",
"D213",
"D215",
"D400",
"D404",
"D406",
"D407",
"D408",
"D409",
"D413",
]
```
Similarly, the following is equivalent to `--docstring-convention=pep8`:
```toml
[tool.ruff]
extend-select = ["D"]
extend-ignore = [
"D203",
"D212",
"D213",
"D214",
"D215",
"D300",
"D400",
"D402",
"D403",
"D404",
"D405",
"D406",
@@ -594,51 +631,10 @@ extend-select = [
"D409",
"D410",
"D411",
"D412",
"D413",
"D418",
"D419",
]
```
Similarly, the following is equivalent to `--docstring-convention google`:
```toml
[tool.ruff]
extend-select = [
"D100",
"D101",
"D102",
"D103",
"D104",
"D105",
"D106",
"D107",
"D200",
"D201",
"D202",
"D205",
"D206",
"D207",
"D208",
"D209",
"D210",
"D211",
"D212",
"D214",
"D300",
"D402",
"D403",
"D405",
"D410",
"D411",
"D412",
"D414",
"D415",
"D416",
"D417",
"D418",
"D419",
]
```

View File

@@ -0,0 +1,16 @@
use std::path::Path;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use ruff::fs;
use ruff::source_code_locator::compute_offsets;
fn criterion_benchmark(c: &mut Criterion) {
let contents = fs::read_file(Path::new("resources/test/fixtures/D.py")).unwrap();
c.bench_function("compute_offsets", |b| {
b.iter(|| compute_offsets(black_box(&contents)))
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

5
foo.py Normal file
View File

@@ -0,0 +1,5 @@
def function_with_nesting():
"""Foo bar documentation."""
@overload
def nested_overloaded_func(a: int) -> str:
...

187
resources/test/fixtures/B006_B008.py vendored Normal file
View File

@@ -0,0 +1,187 @@
import collections
import datetime as dt
import logging
import operator
import random
import re
import time
import types
from operator import attrgetter, itemgetter, methodcaller
from types import MappingProxyType
# B006
# Allow immutable literals/calls/comprehensions
def this_is_okay(value=(1, 2, 3)):
...
async def and_this_also(value=tuple()):
pass
def frozenset_also_okay(value=frozenset()):
pass
def mappingproxytype_okay(
value=MappingProxyType({}), value2=types.MappingProxyType({})
):
pass
def re_compile_ok(value=re.compile("foo")):
pass
def operators_ok(
v=operator.attrgetter("foo"),
v2=operator.itemgetter("foo"),
v3=operator.methodcaller("foo"),
):
pass
def operators_ok_unqualified(
v=attrgetter("foo"),
v2=itemgetter("foo"),
v3=methodcaller("foo"),
):
pass
def kwonlyargs_immutable(*, value=()):
...
# Flag mutable literals/comprehensions
def this_is_wrong(value=[1, 2, 3]):
...
def this_is_also_wrong(value={}):
...
def and_this(value=set()):
...
def this_too(value=collections.OrderedDict()):
...
async def async_this_too(value=collections.defaultdict()):
...
def dont_forget_me(value=collections.deque()):
...
# N.B. we're also flagging the function call in the comprehension
def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]):
pass
def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}):
pass
def set_comprehension_also_not_okay(default={i**2 for i in range(3)}):
pass
def kwonlyargs_mutable(*, value=[]):
...
# Recommended approach for mutable defaults
def do_this_instead(value=None):
if value is None:
value = set()
# B008
# Flag function calls as default args (including if they are part of a sub-expression)
def in_fact_all_calls_are_wrong(value=time.time()):
...
def f(when=dt.datetime.now() + dt.timedelta(days=7)):
pass
def can_even_catch_lambdas(a=(lambda x: x)()):
...
# Recommended approach for function calls as default args
LOGGER = logging.getLogger(__name__)
def do_this_instead_of_calls_in_defaults(logger=LOGGER):
# That makes it more obvious that this one value is reused.
...
# Handle inf/infinity/nan special case
def float_inf_okay(value=float("inf")):
pass
def float_infinity_okay(value=float("infinity")):
pass
def float_plus_infinity_okay(value=float("+infinity")):
pass
def float_minus_inf_okay(value=float("-inf")):
pass
def float_nan_okay(value=float("nan")):
pass
def float_minus_NaN_okay(value=float("-NaN")):
pass
def float_infinity_literal(value=float("1e999")):
pass
# But don't allow standard floats
def float_int_is_wrong(value=float(3)):
pass
def float_str_not_inf_or_nan_is_wrong(value=float("3.14")):
pass
# B006 and B008
# We should handle arbitrary nesting of these B008.
def nested_combo(a=[float(3), dt.datetime.now()]):
pass
# Don't flag nested B006 since we can't guarantee that
# it isn't made mutable by the outer operation.
def no_nested_b006(a=map(lambda s: s.upper(), ["a", "b", "c"])):
pass
# B008-ception.
def nested_b008(a=random.randint(0, dt.datetime.now().year)):
pass
# Ignore lambda contents since they are evaluated at call time.
def foo(f=lambda x: print(x)):
f(1)

8
resources/test/fixtures/B013.py vendored Normal file
View File

@@ -0,0 +1,8 @@
try:
pass
except (ValueError,):
pass
except AttributeError:
pass
except (ImportError, TypeError):
pass

View File

@@ -1 +1,2 @@
d = dict((x, x) for x in range(3))
dict((x, x) for x in range(3))
dict(((x, x) for x in range(3)), z=3)

View File

@@ -1 +1,2 @@
d = dict([(i, i) for i in range(3)])
dict([(i, i) for i in range(3)])
dict([(i, i) for i in range(3)], z=4)

View File

@@ -1,3 +1,6 @@
import unittest
def Bad():
pass
@@ -24,3 +27,15 @@ def _good():
def good_func():
pass
def tearDownModule():
pass
class Test(unittest.TestCase):
def tearDown(self):
return super().tearDown()
def testTest(self):
assert True

4
resources/test/fixtures/N806.py vendored Normal file
View File

@@ -0,0 +1,4 @@
def f():
lower = 0
Camel = 0
CONSTANT = 0

6
resources/test/fixtures/N815.py vendored Normal file
View File

@@ -0,0 +1,6 @@
class C:
lower = 0
CONSTANT = 0
mixedCase = 0
_mixedCase = 0
mixed_Case = 0

5
resources/test/fixtures/N816.py vendored Normal file
View File

@@ -0,0 +1,5 @@
lower = 0
CONSTANT = 0
mixedCase = 0
_mixedCase = 0
mixed_Case = 0

10
resources/test/fixtures/N818.py vendored Normal file
View File

@@ -0,0 +1,10 @@
class Error(Exception):
pass
class AnotherError(Exception):
pass
class C(Exception):
pass

View File

@@ -14,3 +14,25 @@ inline-quotes = "single"
multiline-quotes = "double"
docstring-quotes = "double"
avoid-escape = true
[tool.ruff.pep8-naming]
ignore-names = [
"setUp",
"tearDown",
"setUpClass",
"tearDownClass",
"setUpModule",
"tearDownModule",
"asyncSetUp",
"asyncTearDown",
"setUpTestData",
"failureException",
"longMessage",
"maxDiff",
]
classmethod-decorators = [
"classmethod",
]
staticmethod-decorators = [
"staticmethod",
]

View File

@@ -137,7 +137,7 @@ pub fn to_absolute(relative: &Location, base: &Location) -> Location {
if relative.row() == 1 {
Location::new(
relative.row() + base.row() - 1,
relative.column() + base.column() - 1,
relative.column() + base.column(),
)
} else {
Location::new(relative.row() + base.row() - 1, relative.column())

View File

@@ -1,6 +1,6 @@
use rustpython_parser::ast::{Constant, Expr, ExprKind, Location, Stmt, StmtKind};
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
use crate::ast::types::{BindingKind, Range, Scope};
use crate::ast::types::{BindingKind, Scope};
/// Extract the names bound to a given __all__ assignment.
pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
@@ -117,82 +117,3 @@ pub fn is_unpacking_assignment(stmt: &Stmt) -> bool {
}
false
}
/// Struct used to efficiently slice source code at (row, column) Locations.
pub struct SourceCodeLocator<'a> {
content: &'a str,
offsets: Vec<Vec<usize>>,
}
impl<'a> SourceCodeLocator<'a> {
pub fn new(content: &'a str) -> Self {
SourceCodeLocator {
content,
offsets: Self::compute_offsets(content),
}
}
fn compute_offsets(content: &str) -> Vec<Vec<usize>> {
let mut offsets = vec![];
let mut offset = 0;
for line in content.lines() {
let mut newline = 0;
let mut line_offsets: Vec<usize> = vec![];
for (i, char) in line.char_indices() {
line_offsets.push(offset + i);
newline = i + char.len_utf8();
}
line_offsets.push(offset + newline);
offsets.push(line_offsets);
offset += newline + 1;
}
offsets.push(vec![offset]);
offsets
}
pub fn slice_source_code_at(&self, location: &Location) -> &'a str {
let offset = self.offsets[location.row() - 1][location.column() - 1];
&self.content[offset..]
}
pub fn slice_source_code_range(&self, range: &Range) -> &'a str {
let start = self.offsets[range.location.row() - 1][range.location.column() - 1];
let end = self.offsets[range.end_location.row() - 1][range.end_location.column() - 1];
&self.content[start..end]
}
pub fn partition_source_code_at(
&self,
outer: &Range,
inner: &Range,
) -> (&'a str, &'a str, &'a str) {
let outer_start = self.offsets[outer.location.row() - 1][outer.location.column() - 1];
let outer_end = self.offsets[outer.end_location.row() - 1][outer.end_location.column() - 1];
let inner_start = self.offsets[inner.location.row() - 1][inner.location.column() - 1];
let inner_end = self.offsets[inner.end_location.row() - 1][inner.end_location.column() - 1];
(
&self.content[outer_start..inner_start],
&self.content[inner_start..inner_end],
&self.content[inner_end..outer_end],
)
}
}
#[cfg(test)]
mod tests {
use super::SourceCodeLocator;
#[test]
fn source_code_locator_init() {
let content = "# \u{4e9c}\nclass Foo:\n \"\"\".\"\"\"";
let locator = SourceCodeLocator::new(content);
assert_eq!(locator.offsets.len(), 4);
assert_eq!(locator.offsets[0], [0, 1, 2, 5]);
assert_eq!(locator.offsets[1], [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
assert_eq!(
locator.offsets[2],
[17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
);
assert_eq!(locator.offsets[3], [29]);
}
}

View File

@@ -69,19 +69,18 @@ fn apply_fixes<'a>(fixes: impl Iterator<Item = &'a mut Fix>, contents: &str) ->
if fix.patch.location.row() > last_pos.row() {
if last_pos.row() > 0 || last_pos.column() > 0 {
output.push_str(&lines[last_pos.row() - 1][last_pos.column() - 1..]);
output.push_str(&lines[last_pos.row() - 1][last_pos.column()..]);
output.push('\n');
}
for line in &lines[last_pos.row()..fix.patch.location.row() - 1] {
output.push_str(line);
output.push('\n');
}
output
.push_str(&lines[fix.patch.location.row() - 1][..fix.patch.location.column() - 1]);
output.push_str(&lines[fix.patch.location.row() - 1][..fix.patch.location.column()]);
output.push_str(&fix.patch.content);
} else {
output.push_str(
&lines[last_pos.row() - 1][last_pos.column() - 1..fix.patch.location.column() - 1],
&lines[last_pos.row() - 1][last_pos.column()..fix.patch.location.column()],
);
output.push_str(&fix.patch.content);
}
@@ -95,7 +94,7 @@ fn apply_fixes<'a>(fixes: impl Iterator<Item = &'a mut Fix>, contents: &str) ->
&& (last_pos.row() - 1) < lines.len()
&& (last_pos.row() > 0 || last_pos.column() > 0)
{
output.push_str(&lines[last_pos.row() - 1][last_pos.column() - 1..]);
output.push_str(&lines[last_pos.row() - 1][last_pos.column()..]);
output.push('\n');
}
if last_pos.row() < lines.len() {
@@ -133,8 +132,8 @@ mod tests {
let mut fixes = vec![Fix {
patch: Patch {
content: "Bar".to_string(),
location: Location::new(1, 9),
end_location: Location::new(1, 15),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
},
applied: false,
}];
@@ -159,8 +158,8 @@ mod tests {
let mut fixes = vec![Fix {
patch: Patch {
content: "".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 16),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
applied: false,
}];
@@ -186,16 +185,16 @@ mod tests {
Fix {
patch: Patch {
content: "".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 17),
location: Location::new(1, 7),
end_location: Location::new(1, 16),
},
applied: false,
},
Fix {
patch: Patch {
content: "".to_string(),
location: Location::new(1, 17),
end_location: Location::new(1, 24),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
},
applied: false,
},
@@ -222,16 +221,16 @@ mod tests {
Fix {
patch: Patch {
content: "".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 16),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
applied: false,
},
Fix {
patch: Patch {
content: "ignored".to_string(),
location: Location::new(1, 10),
end_location: Location::new(1, 12),
location: Location::new(1, 9),
end_location: Location::new(1, 11),
},
applied: false,
},

View File

@@ -82,8 +82,8 @@ pub fn remove_stmt(stmt: &Stmt, parent: Option<&Stmt>, deleted: &[&Stmt]) -> Res
// Otherwise, nuke the entire line.
// TODO(charlie): This logic assumes that there are no multi-statement physical lines.
Ok(Fix::deletion(
Location::new(stmt.location.row(), 1),
Location::new(stmt.end_location.unwrap().row() + 1, 1),
Location::new(stmt.location.row(), 0),
Location::new(stmt.end_location.unwrap().row() + 1, 0),
))
}
}

View File

@@ -5,7 +5,6 @@ use std::ops::Deref;
use std::path::Path;
use log::error;
use once_cell::unsync::OnceCell;
use rustpython_parser::ast::{
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
KeywordData, Operator, Stmt, StmtKind, Suite,
@@ -13,7 +12,7 @@ use rustpython_parser::ast::{
use rustpython_parser::parser;
use crate::ast::helpers::{extract_handler_names, match_name_or_attr, SubscriptKind};
use crate::ast::operations::{extract_all_names, SourceCodeLocator};
use crate::ast::operations::extract_all_names;
use crate::ast::relocate::relocate_expr;
use crate::ast::types::{
Binding, BindingContext, BindingKind, CheckLocator, FunctionScope, ImportKind, Range, Scope,
@@ -28,6 +27,7 @@ use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::python::future::ALL_FEATURE_NAMES;
use crate::settings::types::PythonVersion;
use crate::settings::Settings;
use crate::source_code_locator::SourceCodeLocator;
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
use crate::{
docstrings, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_print, pep8_naming,
@@ -39,13 +39,11 @@ pub const GLOBAL_SCOPE_INDEX: usize = 0;
pub struct Checker<'a> {
// Input data.
path: &'a Path,
content: &'a str,
autofix: &'a fixer::Mode,
pub(crate) settings: &'a Settings,
pub(crate) locator: &'a SourceCodeLocator<'a>,
// Computed checks.
checks: Vec<Check>,
// Efficient source-code slicing.
locator: OnceCell<SourceCodeLocator<'a>>,
// Docstring tracking.
docstrings: Vec<(Definition<'a>, Visibility)>,
// Edit tracking.
@@ -79,14 +77,13 @@ impl<'a> Checker<'a> {
settings: &'a Settings,
autofix: &'a fixer::Mode,
path: &'a Path,
content: &'a str,
locator: &'a SourceCodeLocator,
) -> Checker<'a> {
Checker {
settings,
autofix,
path,
content,
locator: OnceCell::new(),
locator,
checks: Default::default(),
docstrings: Default::default(),
deletions: Default::default(),
@@ -114,12 +111,6 @@ impl<'a> Checker<'a> {
}
}
/// Get access to a lazily-initialized `SourceCodeLocator` for the file contents.
pub fn get_locator(&self) -> &SourceCodeLocator {
self.locator
.get_or_init(|| SourceCodeLocator::new(self.content))
}
/// Return `true` if a patch should be generated under the given autofix `Mode`.
pub fn patch(&self) -> bool {
self.autofix.patch()
@@ -236,7 +227,11 @@ where
}
if self.settings.enabled.contains(&CheckCode::N802) {
if let Some(check) = pep8_naming::checks::invalid_function_name(stmt, name) {
if let Some(check) = pep8_naming::checks::invalid_function_name(
stmt,
name,
&self.settings.pep8_naming,
) {
self.checks.push(check);
}
}
@@ -247,6 +242,7 @@ where
self.current_scope(),
decorator_list,
args,
&self.settings.pep8_naming,
)
{
self.checks.push(check);
@@ -258,6 +254,7 @@ where
self.current_scope(),
decorator_list,
args,
&self.settings.pep8_naming,
) {
self.checks.push(check);
}
@@ -363,6 +360,14 @@ where
}
}
if self.settings.enabled.contains(&CheckCode::N818) {
if let Some(check) =
pep8_naming::checks::error_suffix_on_exception_name(stmt, bases, name)
{
self.checks.push(check);
}
}
self.check_builtin_shadowing(
name,
self.locate_check(Range::from_located(stmt)),
@@ -382,7 +387,7 @@ where
}
StmtKind::Import { names } => {
if self.settings.enabled.contains(&CheckCode::E402) {
if self.seen_import_boundary && stmt.location.column() == 1 {
if self.seen_import_boundary && stmt.location.column() == 0 {
self.checks.push(Check::new(
CheckKind::ModuleImportNotAtTopOfFile,
self.locate_check(Range::from_located(stmt)),
@@ -488,7 +493,7 @@ where
level,
} => {
if self.settings.enabled.contains(&CheckCode::E402) {
if self.seen_import_boundary && stmt.location.column() == 1 {
if self.seen_import_boundary && stmt.location.column() == 0 {
self.checks.push(Check::new(
CheckKind::ModuleImportNotAtTopOfFile,
self.locate_check(Range::from_located(stmt)),
@@ -706,6 +711,9 @@ where
{
flake8_bugbear::plugins::duplicate_exceptions(self, stmt, handlers);
}
if self.settings.enabled.contains(&CheckCode::B013) {
flake8_bugbear::plugins::redundant_tuple_in_exception_handler(self, handlers);
}
}
StmtKind::Assign { targets, value, .. } => {
if self.settings.enabled.contains(&CheckCode::E731) {
@@ -916,7 +924,6 @@ where
func,
args,
keywords,
..
} => {
if self.settings.enabled.contains(&CheckCode::U005) {
pyupgrade::plugins::deprecated_unittest_alias(self, func);
@@ -936,25 +943,25 @@ where
// flake8-comprehensions
if self.settings.enabled.contains(&CheckCode::C400) {
if let Some(check) =
flake8_comprehensions::checks::unnecessary_generator_list(expr, func, args)
{
if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_list(
expr, func, args, keywords,
) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C401) {
if let Some(check) =
flake8_comprehensions::checks::unnecessary_generator_set(expr, func, args)
{
if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_set(
expr, func, args, keywords,
) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C402) {
if let Some(check) =
flake8_comprehensions::checks::unnecessary_generator_dict(expr, func, args)
{
if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_dict(
expr, func, args, keywords,
) {
self.checks.push(check);
};
}
@@ -962,7 +969,7 @@ where
if self.settings.enabled.contains(&CheckCode::C403) {
if let Some(check) =
flake8_comprehensions::checks::unnecessary_list_comprehension_set(
expr, func, args,
expr, func, args, keywords,
)
{
self.checks.push(check);
@@ -972,7 +979,7 @@ where
if self.settings.enabled.contains(&CheckCode::C404) {
if let Some(check) =
flake8_comprehensions::checks::unnecessary_list_comprehension_dict(
expr, func, args,
expr, func, args, keywords,
)
{
self.checks.push(check);
@@ -980,17 +987,17 @@ where
}
if self.settings.enabled.contains(&CheckCode::C405) {
if let Some(check) =
flake8_comprehensions::checks::unnecessary_literal_set(expr, func, args)
{
if let Some(check) = flake8_comprehensions::checks::unnecessary_literal_set(
expr, func, args, keywords,
) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C406) {
if let Some(check) =
flake8_comprehensions::checks::unnecessary_literal_dict(expr, func, args)
{
if let Some(check) = flake8_comprehensions::checks::unnecessary_literal_dict(
expr, func, args, keywords,
) {
self.checks.push(check);
};
}
@@ -1496,6 +1503,9 @@ where
self.checks
.extend(pyflakes::checks::duplicate_arguments(arguments));
}
if self.settings.enabled.contains(&CheckCode::B006) {
flake8_bugbear::plugins::mutable_argument_default(self, arguments)
}
// Bind, but intentionally avoid walking default expressions, as we handle them upstream.
for arg in &arguments.posonlyargs {
@@ -1799,6 +1809,30 @@ impl<'a> Checker<'a> {
}
}
if self.settings.enabled.contains(&CheckCode::N806) {
if let Some(check) =
pep8_naming::checks::non_lowercase_variable_in_function(current, expr, id)
{
self.checks.push(check);
}
}
if self.settings.enabled.contains(&CheckCode::N815) {
if let Some(check) =
pep8_naming::checks::mixed_case_variable_in_class_scope(current, expr, id)
{
self.checks.push(check);
}
}
if self.settings.enabled.contains(&CheckCode::N816) {
if let Some(check) =
pep8_naming::checks::mixed_case_variable_in_global_scope(current, expr, id)
{
self.checks.push(check);
}
}
if matches!(parent.node, StmtKind::AnnAssign { value: None, .. }) {
self.add_binding(
id.to_string(),
@@ -2105,7 +2139,7 @@ impl<'a> Checker<'a> {
ImportKind::ImportFrom => pyflakes::fixes::remove_unused_import_froms,
};
match removal_fn(self.get_locator(), &full_names, child, parent, &deleted) {
match removal_fn(self.locator, &full_names, child, parent, &deleted) {
Ok(fix) => Some(fix),
Err(e) => {
error!("Failed to fix unused imports: {}", e);
@@ -2265,12 +2299,12 @@ impl<'a> Checker<'a> {
pub fn check_ast(
python_ast: &Suite,
contents: &str,
locator: &SourceCodeLocator,
settings: &Settings,
autofix: &fixer::Mode,
path: &Path,
) -> Vec<Check> {
let mut checker = Checker::new(settings, autofix, path, contents);
let mut checker = Checker::new(settings, autofix, path, locator);
checker.push_scope(Scope::new(ScopeKind::Module));
checker.bind_builtins();

View File

@@ -94,8 +94,8 @@ pub fn check_lines(
let check = Check::new(
CheckKind::LineTooLong(line_length, settings.line_length),
Range {
location: Location::new(lineno + 1, 1),
end_location: Location::new(lineno + 1, line_length + 1),
location: Location::new(lineno + 1, 0),
end_location: Location::new(lineno + 1, line_length),
},
);
@@ -164,14 +164,14 @@ pub fn check_lines(
let mut check = Check::new(
CheckKind::UnusedNOQA(None),
Range {
location: Location::new(row + 1, start + 1),
end_location: Location::new(row + 1, end + 1),
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),
},
);
if autofix.patch() {
check.amend(Fix::deletion(
Location::new(row + 1, start + 1),
Location::new(row + 1, lines[row].chars().count() + 1),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
}
line_checks.push(check);
@@ -192,21 +192,21 @@ pub fn check_lines(
let mut check = Check::new(
CheckKind::UnusedNOQA(Some(invalid_codes)),
Range {
location: Location::new(row + 1, start + 1),
end_location: Location::new(row + 1, end + 1),
location: Location::new(row + 1, start),
end_location: Location::new(row + 1, end),
},
);
if autofix.patch() {
if valid_codes.is_empty() {
check.amend(Fix::deletion(
Location::new(row + 1, start + 1),
Location::new(row + 1, lines[row].chars().count() + 1),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
} else {
check.amend(Fix::replacement(
format!(" # noqa: {}", valid_codes.join(", ")),
Location::new(row + 1, start + 1),
Location::new(row + 1, lines[row].chars().count() + 1),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
}
}

View File

@@ -2,14 +2,14 @@
use rustpython_parser::lexer::{LexResult, Tok};
use crate::ast::operations::SourceCodeLocator;
use crate::checks::{Check, CheckCode};
use crate::flake8_quotes::docstring_detection::StateMachine;
use crate::source_code_locator::SourceCodeLocator;
use crate::{flake8_quotes, pycodestyle, Settings};
pub fn check_tokens(
checks: &mut Vec<Check>,
contents: &str,
locator: &SourceCodeLocator,
tokens: &[LexResult],
settings: &Settings,
) {
@@ -19,16 +19,13 @@ pub fn check_tokens(
| settings.enabled.contains(&CheckCode::Q002)
| settings.enabled.contains(&CheckCode::Q003);
// TODO(charlie): Use a shared SourceCodeLocator between this site and the AST traversal.
let locator = SourceCodeLocator::new(contents);
let mut state_machine = StateMachine::new();
for (start, tok, end) in tokens.iter().flatten() {
// W605
if enforce_invalid_escape_sequence {
if matches!(tok, Tok::String { .. }) {
checks.extend(pycodestyle::checks::invalid_escape_sequence(
&locator, start, end,
locator, start, end,
));
}
}
@@ -38,7 +35,7 @@ pub fn check_tokens(
let is_docstring = state_machine.consume(tok);
if matches!(tok, Tok::String { .. }) {
if let Some(check) = flake8_quotes::checks::quotes(
&locator,
locator,
start,
end,
is_docstring,

View File

@@ -78,8 +78,10 @@ pub enum CheckCode {
A003,
// flake8-bugbear
B002,
B006,
B007,
B011,
B013,
B014,
B017,
B025,
@@ -168,12 +170,16 @@ pub enum CheckCode {
N803,
N804,
N805,
N806,
N807,
N811,
N812,
N813,
N814,
N815,
N816,
N817,
N818,
// Meta
M001,
}
@@ -282,8 +288,10 @@ pub enum CheckKind {
BuiltinAttributeShadowing(String),
// flake8-bugbear
UnaryPrefixIncrement,
MutableArgumentDefault,
UnusedLoopControlVariable(String),
DoNotAssertFalse,
RedundantTupleInExceptionHandler(String),
DuplicateHandlerException(Vec<String>),
NoAssertRaisesException,
DuplicateTryBlockException(String),
@@ -372,12 +380,16 @@ pub enum CheckKind {
InvalidArgumentName(String),
InvalidFirstArgumentNameForClassMethod,
InvalidFirstArgumentNameForMethod,
NonLowercaseVariableInFunction(String),
DunderFunctionName,
ConstantImportedAsNonConstant(String, String),
LowercaseImportedAsNonLowercase(String, String),
CamelcaseImportedAsLowercase(String, String),
CamelcaseImportedAsConstant(String, String),
MixedCaseVariableInClassScope(String),
MixedCaseVariableInGlobalScope(String),
CamelcaseImportedAsAcronym(String, String),
ErrorSuffixOnExceptionName(String),
// Meta
UnusedNOQA(Option<Vec<String>>),
}
@@ -455,8 +467,12 @@ impl CheckCode {
CheckCode::A003 => CheckKind::BuiltinAttributeShadowing("...".to_string()),
// flake8-bugbear
CheckCode::B002 => CheckKind::UnaryPrefixIncrement,
CheckCode::B006 => CheckKind::MutableArgumentDefault,
CheckCode::B007 => CheckKind::UnusedLoopControlVariable("i".to_string()),
CheckCode::B011 => CheckKind::DoNotAssertFalse,
CheckCode::B013 => {
CheckKind::RedundantTupleInExceptionHandler("ValueError".to_string())
}
CheckCode::B014 => CheckKind::DuplicateHandlerException(vec!["ValueError".to_string()]),
CheckCode::B017 => CheckKind::NoAssertRaisesException,
CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
@@ -567,6 +583,7 @@ impl CheckCode {
CheckCode::N803 => CheckKind::InvalidArgumentName("...".to_string()),
CheckCode::N804 => CheckKind::InvalidFirstArgumentNameForClassMethod,
CheckCode::N805 => CheckKind::InvalidFirstArgumentNameForMethod,
CheckCode::N806 => CheckKind::NonLowercaseVariableInFunction("...".to_string()),
CheckCode::N807 => CheckKind::DunderFunctionName,
CheckCode::N811 => {
CheckKind::ConstantImportedAsNonConstant("...".to_string(), "...".to_string())
@@ -580,9 +597,12 @@ impl CheckCode {
CheckCode::N814 => {
CheckKind::CamelcaseImportedAsConstant("...".to_string(), "...".to_string())
}
CheckCode::N815 => CheckKind::MixedCaseVariableInClassScope("mixedCase".to_string()),
CheckCode::N816 => CheckKind::MixedCaseVariableInGlobalScope("mixedCase".to_string()),
CheckCode::N817 => {
CheckKind::CamelcaseImportedAsAcronym("...".to_string(), "...".to_string())
}
CheckCode::N818 => CheckKind::ErrorSuffixOnExceptionName("...".to_string()),
// Meta
CheckCode::M001 => CheckKind::UnusedNOQA(None),
}
@@ -638,8 +658,10 @@ impl CheckCode {
CheckCode::A002 => CheckCategory::Flake8Builtins,
CheckCode::A003 => CheckCategory::Flake8Builtins,
CheckCode::B002 => CheckCategory::Flake8Bugbear,
CheckCode::B006 => CheckCategory::Flake8Bugbear,
CheckCode::B007 => CheckCategory::Flake8Bugbear,
CheckCode::B011 => CheckCategory::Flake8Bugbear,
CheckCode::B013 => CheckCategory::Flake8Bugbear,
CheckCode::B014 => CheckCategory::Flake8Bugbear,
CheckCode::B017 => CheckCategory::Flake8Bugbear,
CheckCode::B025 => CheckCategory::Flake8Bugbear,
@@ -722,12 +744,16 @@ impl CheckCode {
CheckCode::N803 => CheckCategory::PEP8Naming,
CheckCode::N804 => CheckCategory::PEP8Naming,
CheckCode::N805 => CheckCategory::PEP8Naming,
CheckCode::N806 => CheckCategory::PEP8Naming,
CheckCode::N807 => CheckCategory::PEP8Naming,
CheckCode::N811 => CheckCategory::PEP8Naming,
CheckCode::N812 => CheckCategory::PEP8Naming,
CheckCode::N813 => CheckCategory::PEP8Naming,
CheckCode::N814 => CheckCategory::PEP8Naming,
CheckCode::N815 => CheckCategory::PEP8Naming,
CheckCode::N816 => CheckCategory::PEP8Naming,
CheckCode::N817 => CheckCategory::PEP8Naming,
CheckCode::N818 => CheckCategory::PEP8Naming,
CheckCode::M001 => CheckCategory::Meta,
}
}
@@ -789,8 +815,10 @@ impl CheckKind {
CheckKind::BuiltinAttributeShadowing(_) => &CheckCode::A003,
// flake8-bugbear
CheckKind::UnaryPrefixIncrement => &CheckCode::B002,
CheckKind::MutableArgumentDefault => &CheckCode::B006,
CheckKind::UnusedLoopControlVariable(_) => &CheckCode::B007,
CheckKind::DoNotAssertFalse => &CheckCode::B011,
CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013,
CheckKind::DuplicateHandlerException(_) => &CheckCode::B014,
CheckKind::NoAssertRaisesException => &CheckCode::B017,
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
@@ -879,12 +907,16 @@ impl CheckKind {
CheckKind::InvalidArgumentName(_) => &CheckCode::N803,
CheckKind::InvalidFirstArgumentNameForClassMethod => &CheckCode::N804,
CheckKind::InvalidFirstArgumentNameForMethod => &CheckCode::N805,
CheckKind::NonLowercaseVariableInFunction(..) => &CheckCode::N806,
CheckKind::DunderFunctionName => &CheckCode::N807,
CheckKind::ConstantImportedAsNonConstant(..) => &CheckCode::N811,
CheckKind::LowercaseImportedAsNonLowercase(..) => &CheckCode::N812,
CheckKind::CamelcaseImportedAsLowercase(..) => &CheckCode::N813,
CheckKind::CamelcaseImportedAsConstant(..) => &CheckCode::N814,
CheckKind::MixedCaseVariableInClassScope(..) => &CheckCode::N815,
CheckKind::MixedCaseVariableInGlobalScope(..) => &CheckCode::N816,
CheckKind::CamelcaseImportedAsAcronym(..) => &CheckCode::N817,
CheckKind::ErrorSuffixOnExceptionName(..) => &CheckCode::N818,
// Meta
CheckKind::UnusedNOQA(_) => &CheckCode::M001,
}
@@ -1023,7 +1055,7 @@ impl CheckKind {
format!("Local variable `{name}` is assigned to but never used")
}
CheckKind::YieldOutsideFunction => {
"`yield` or `yield from` statement outside of a function/method".to_string()
"`yield` or `yield from` statement outside of a function".to_string()
}
// pycodestyle warnings
CheckKind::NoNewLineAtEndOfFile => "No newline at end of file".to_string(),
@@ -1040,11 +1072,15 @@ impl CheckKind {
}
// flake8-bugbear
CheckKind::UnaryPrefixIncrement => "Python does not support the unary prefix increment. Writing `++n` is equivalent to `+(+(n))`, which equals `n`. You meant `n += 1`.".to_string(),
CheckKind::MutableArgumentDefault => "Do not use mutable data structures for argument defaults.".to_string(),
CheckKind::UnusedLoopControlVariable(name) => format!("Loop control variable `{name}` not used within the loop body. If this is intended, start the name with an underscore."),
CheckKind::DoNotAssertFalse => {
"Do not `assert False` (`python -O` removes these calls), raise `AssertionError()`"
.to_string()
}
CheckKind::RedundantTupleInExceptionHandler(name) => {
format!("A length-one tuple literal is redundant. Write `except {name}:` instead of `except ({name},):`.")
}
CheckKind::DuplicateHandlerException(names) => {
if names.len() == 1 {
let name = &names[0];
@@ -1120,7 +1156,7 @@ impl CheckKind {
format!("Unnecessary subscript reversal of iterable within `{func}()`")
}
CheckKind::UnnecessaryComprehension(obj_type) => {
format!(" Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`)")
format!("Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`)")
}
CheckKind::UnnecessaryMap(obj_type) => {
if obj_type == "generator" {
@@ -1161,7 +1197,7 @@ impl CheckKind {
}
CheckKind::UselessMetaclassType => "`__metaclass__ = type` is implied".to_string(),
CheckKind::DeprecatedUnittestAlias(alias, target) => {
format!("`{}` is deprecated, use `{}` instead", alias, target)
format!("`{alias}` is deprecated, use `{target}` instead")
}
CheckKind::UselessObjectInheritance(name) => {
format!("Class `{name}` inherits from object")
@@ -1231,10 +1267,10 @@ impl CheckKind {
CheckKind::PublicNestedClass => "Missing docstring in public nested class".to_string(),
CheckKind::PublicInit => "Missing docstring in `__init__`".to_string(),
CheckKind::NoThisPrefix => {
"First word of the docstring should not be `This`".to_string()
"First word of the docstring should not be 'This'".to_string()
}
CheckKind::SkipDocstring => {
"Function decorated with @overload shouldn't contain a docstring".to_string()
"Function decorated with `@overload` shouldn't contain a docstring".to_string()
}
CheckKind::CapitalizeSectionName(name) => {
format!("Section name should be properly capitalized (\"{name}\")")
@@ -1305,6 +1341,9 @@ impl CheckKind {
CheckKind::InvalidFirstArgumentNameForMethod => {
"First argument of a method should be named `self`".to_string()
}
CheckKind::NonLowercaseVariableInFunction(name) => {
format!("Variable `{name}` in function should be lowercase")
}
CheckKind::DunderFunctionName => {
"Function name should not start and end with `__`".to_string()
}
@@ -1320,9 +1359,18 @@ impl CheckKind {
CheckKind::CamelcaseImportedAsConstant(name, asname) => {
format!("Camelcase `{name}` imported as constant `{asname}`")
}
CheckKind::MixedCaseVariableInClassScope(name) => {
format!("Variable `{name}` in class scope should not be mixedCase")
}
CheckKind::MixedCaseVariableInGlobalScope(name) => {
format!("Variable `{name}` in global scope should not be mixedCase")
}
CheckKind::CamelcaseImportedAsAcronym(name, asname) => {
format!("Camelcase `{name}` imported as acronym `{asname}`")
}
CheckKind::ErrorSuffixOnExceptionName(name) => {
format!("Exception name `{name}` should be named with an Error suffix")
}
// Meta
CheckKind::UnusedNOQA(codes) => match codes {
None => "Unused `noqa` directive".to_string(),

View File

@@ -17,9 +17,11 @@ pub enum CheckCodePrefix {
B0,
B00,
B002,
B006,
B007,
B01,
B011,
B013,
B014,
B017,
B02,
@@ -184,13 +186,24 @@ pub enum CheckCodePrefix {
N803,
N804,
N805,
N806,
N807,
N81,
N811,
N812,
N813,
N814,
N815,
N816,
N817,
N818,
Q,
Q0,
Q00,
Q000,
Q001,
Q002,
Q003,
T,
T2,
T20,
@@ -235,25 +248,36 @@ impl CheckCodePrefix {
CheckCodePrefix::A003 => vec![CheckCode::A003],
CheckCodePrefix::B => vec![
CheckCode::B002,
CheckCode::B006,
CheckCode::B007,
CheckCode::B011,
CheckCode::B013,
CheckCode::B014,
CheckCode::B017,
CheckCode::B025,
],
CheckCodePrefix::B0 => vec![
CheckCode::B002,
CheckCode::B006,
CheckCode::B007,
CheckCode::B011,
CheckCode::B013,
CheckCode::B014,
CheckCode::B017,
CheckCode::B025,
],
CheckCodePrefix::B00 => vec![CheckCode::B002, CheckCode::B007],
CheckCodePrefix::B00 => vec![CheckCode::B002, CheckCode::B006, CheckCode::B007],
CheckCodePrefix::B002 => vec![CheckCode::B002],
CheckCodePrefix::B006 => vec![CheckCode::B006],
CheckCodePrefix::B007 => vec![CheckCode::B007],
CheckCodePrefix::B01 => vec![CheckCode::B011, CheckCode::B014, CheckCode::B017],
CheckCodePrefix::B01 => vec![
CheckCode::B011,
CheckCode::B013,
CheckCode::B014,
CheckCode::B017,
],
CheckCodePrefix::B011 => vec![CheckCode::B011],
CheckCodePrefix::B013 => vec![CheckCode::B013],
CheckCodePrefix::B014 => vec![CheckCode::B014],
CheckCodePrefix::B017 => vec![CheckCode::B017],
CheckCodePrefix::B02 => vec![CheckCode::B025],
@@ -716,12 +740,16 @@ impl CheckCodePrefix {
CheckCode::N803,
CheckCode::N804,
CheckCode::N805,
CheckCode::N806,
CheckCode::N807,
CheckCode::N811,
CheckCode::N812,
CheckCode::N813,
CheckCode::N814,
CheckCode::N815,
CheckCode::N816,
CheckCode::N817,
CheckCode::N818,
],
CheckCodePrefix::N8 => vec![
CheckCode::N801,
@@ -729,12 +757,16 @@ impl CheckCodePrefix {
CheckCode::N803,
CheckCode::N804,
CheckCode::N805,
CheckCode::N806,
CheckCode::N807,
CheckCode::N811,
CheckCode::N812,
CheckCode::N813,
CheckCode::N814,
CheckCode::N815,
CheckCode::N816,
CheckCode::N817,
CheckCode::N818,
],
CheckCodePrefix::N80 => vec![
CheckCode::N801,
@@ -742,6 +774,7 @@ impl CheckCodePrefix {
CheckCode::N803,
CheckCode::N804,
CheckCode::N805,
CheckCode::N806,
CheckCode::N807,
],
CheckCodePrefix::N801 => vec![CheckCode::N801],
@@ -749,19 +782,48 @@ impl CheckCodePrefix {
CheckCodePrefix::N803 => vec![CheckCode::N803],
CheckCodePrefix::N804 => vec![CheckCode::N804],
CheckCodePrefix::N805 => vec![CheckCode::N805],
CheckCodePrefix::N806 => vec![CheckCode::N806],
CheckCodePrefix::N807 => vec![CheckCode::N807],
CheckCodePrefix::N81 => vec![
CheckCode::N811,
CheckCode::N812,
CheckCode::N813,
CheckCode::N814,
CheckCode::N815,
CheckCode::N816,
CheckCode::N817,
CheckCode::N818,
],
CheckCodePrefix::N811 => vec![CheckCode::N811],
CheckCodePrefix::N812 => vec![CheckCode::N812],
CheckCodePrefix::N813 => vec![CheckCode::N813],
CheckCodePrefix::N814 => vec![CheckCode::N814],
CheckCodePrefix::N815 => vec![CheckCode::N815],
CheckCodePrefix::N816 => vec![CheckCode::N816],
CheckCodePrefix::N817 => vec![CheckCode::N817],
CheckCodePrefix::N818 => vec![CheckCode::N818],
CheckCodePrefix::Q => vec![
CheckCode::Q000,
CheckCode::Q001,
CheckCode::Q002,
CheckCode::Q003,
],
CheckCodePrefix::Q0 => vec![
CheckCode::Q000,
CheckCode::Q001,
CheckCode::Q002,
CheckCode::Q003,
],
CheckCodePrefix::Q00 => vec![
CheckCode::Q000,
CheckCode::Q001,
CheckCode::Q002,
CheckCode::Q003,
],
CheckCodePrefix::Q000 => vec![CheckCode::Q000],
CheckCodePrefix::Q001 => vec![CheckCode::Q001],
CheckCodePrefix::Q002 => vec![CheckCode::Q002],
CheckCodePrefix::Q003 => vec![CheckCode::Q003],
CheckCodePrefix::T => vec![CheckCode::T201, CheckCode::T203],
CheckCodePrefix::T2 => vec![CheckCode::T201, CheckCode::T203],
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
@@ -829,9 +891,11 @@ impl CheckCodePrefix {
CheckCodePrefix::B0 => PrefixSpecificity::Hundreds,
CheckCodePrefix::B00 => PrefixSpecificity::Tens,
CheckCodePrefix::B002 => PrefixSpecificity::Explicit,
CheckCodePrefix::B006 => PrefixSpecificity::Explicit,
CheckCodePrefix::B007 => PrefixSpecificity::Explicit,
CheckCodePrefix::B01 => PrefixSpecificity::Tens,
CheckCodePrefix::B011 => PrefixSpecificity::Explicit,
CheckCodePrefix::B013 => PrefixSpecificity::Explicit,
CheckCodePrefix::B014 => PrefixSpecificity::Explicit,
CheckCodePrefix::B017 => PrefixSpecificity::Explicit,
CheckCodePrefix::B02 => PrefixSpecificity::Tens,
@@ -996,13 +1060,24 @@ impl CheckCodePrefix {
CheckCodePrefix::N803 => PrefixSpecificity::Explicit,
CheckCodePrefix::N804 => PrefixSpecificity::Explicit,
CheckCodePrefix::N805 => PrefixSpecificity::Explicit,
CheckCodePrefix::N806 => PrefixSpecificity::Explicit,
CheckCodePrefix::N807 => PrefixSpecificity::Explicit,
CheckCodePrefix::N81 => PrefixSpecificity::Tens,
CheckCodePrefix::N811 => PrefixSpecificity::Explicit,
CheckCodePrefix::N812 => PrefixSpecificity::Explicit,
CheckCodePrefix::N813 => PrefixSpecificity::Explicit,
CheckCodePrefix::N814 => PrefixSpecificity::Explicit,
CheckCodePrefix::N815 => PrefixSpecificity::Explicit,
CheckCodePrefix::N816 => PrefixSpecificity::Explicit,
CheckCodePrefix::N817 => PrefixSpecificity::Explicit,
CheckCodePrefix::N818 => PrefixSpecificity::Explicit,
CheckCodePrefix::Q => PrefixSpecificity::Category,
CheckCodePrefix::Q0 => PrefixSpecificity::Hundreds,
CheckCodePrefix::Q00 => PrefixSpecificity::Tens,
CheckCodePrefix::Q000 => PrefixSpecificity::Explicit,
CheckCodePrefix::Q001 => PrefixSpecificity::Explicit,
CheckCodePrefix::Q002 => PrefixSpecificity::Explicit,
CheckCodePrefix::Q003 => PrefixSpecificity::Explicit,
CheckCodePrefix::T => PrefixSpecificity::Category,
CheckCodePrefix::T2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::T20 => PrefixSpecificity::Tens,

View File

@@ -27,8 +27,8 @@ pub fn leading_space(line: &str) -> String {
/// Extract the leading indentation from a docstring.
pub fn indentation<'a>(checker: &'a Checker, docstring: &Expr) -> &'a str {
let range = Range::from_located(docstring);
checker.get_locator().slice_source_code_range(&Range {
location: Location::new(range.location.row(), 1),
checker.locator.slice_source_code_range(&Range {
location: Location::new(range.location.row(), 0),
end_location: Location::new(range.location.row(), range.location.column()),
})
}

View File

@@ -2,11 +2,15 @@ pub use assert_false::assert_false;
pub use assert_raises_exception::assert_raises_exception;
pub use duplicate_exceptions::duplicate_exceptions;
pub use duplicate_exceptions::duplicate_handler_exceptions;
pub use mutable_argument_default::mutable_argument_default;
pub use redundant_tuple_in_exception_handler::redundant_tuple_in_exception_handler;
pub use unary_prefix_increment::unary_prefix_increment;
pub use unused_loop_control_variable::unused_loop_control_variable;
mod assert_false;
mod assert_raises_exception;
mod duplicate_exceptions;
mod mutable_argument_default;
mod redundant_tuple_in_exception_handler;
mod unary_prefix_increment;
mod unused_loop_control_variable;

View File

@@ -0,0 +1,62 @@
use rustpython_ast::{Arguments, ExprKind};
use crate::ast::types::{CheckLocator, Range};
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
/// B006
pub fn mutable_argument_default(checker: &mut Checker, arguments: &Arguments) {
for expr in arguments
.defaults
.iter()
.chain(arguments.kw_defaults.iter())
{
match &expr.node {
ExprKind::List { .. }
| ExprKind::Dict { .. }
| ExprKind::Set { .. }
| ExprKind::ListComp { .. }
| ExprKind::DictComp { .. }
| ExprKind::SetComp { .. } => {
checker.add_check(Check::new(
CheckKind::MutableArgumentDefault,
checker.locate_check(Range::from_located(expr)),
));
}
ExprKind::Call { func, .. } => match &func.node {
ExprKind::Name { id, .. }
if id == "dict"
|| id == "list"
|| id == "set"
|| id == "Counter"
|| id == "OrderedDict"
|| id == "defaultdict"
|| id == "deque" =>
{
checker.add_check(Check::new(
CheckKind::MutableArgumentDefault,
checker.locate_check(Range::from_located(expr)),
));
}
ExprKind::Attribute { value, attr, .. }
if (attr == "Counter"
|| attr == "OrderedDict"
|| attr == "defaultdict"
|| attr == "deque") =>
{
match &value.node {
ExprKind::Name { id, .. } if id == "collections" => {
checker.add_check(Check::new(
CheckKind::MutableArgumentDefault,
checker.locate_check(Range::from_located(expr)),
));
}
_ => {}
}
}
_ => {}
},
_ => {}
}
}
}

View File

@@ -0,0 +1,22 @@
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind};
use crate::ast::types::{CheckLocator, Range};
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
/// B013
pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[Excepthandler]) {
for handler in handlers {
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
if let Some(type_) = type_ {
if let ExprKind::Tuple { elts, .. } = &type_.node {
if elts.len() == 1 {
checker.add_check(Check::new(
CheckKind::RedundantTupleInExceptionHandler(elts[0].to_string()),
checker.locate_check(Range::from_located(type_)),
));
}
}
}
}
}

View File

@@ -1,5 +1,7 @@
use num_bigint::BigInt;
use rustpython_ast::{Comprehension, Constant, Expr, ExprKind, KeywordData, Located, Unaryop};
use rustpython_ast::{
Comprehension, Constant, Expr, ExprKind, Keyword, KeywordData, Located, Unaryop,
};
use crate::ast::types::Range;
use crate::checks::{Check, CheckKind};
@@ -16,7 +18,11 @@ fn exactly_one_argument_with_matching_function<'a>(
name: &str,
func: &Expr,
args: &'a [Expr],
keywords: &[Keyword],
) -> Option<&'a ExprKind> {
if !keywords.is_empty() {
return None;
}
if args.len() != 1 {
return None;
}
@@ -38,8 +44,13 @@ fn first_argument_with_matching_function<'a>(
}
/// C400 (`list(generator)`)
pub fn unnecessary_generator_list(expr: &Expr, func: &Expr, args: &[Expr]) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("list", func, args)?;
pub fn unnecessary_generator_list(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("list", func, args, keywords)?;
if let ExprKind::GeneratorExp { .. } = argument {
return Some(Check::new(
CheckKind::UnnecessaryGeneratorList,
@@ -50,8 +61,13 @@ pub fn unnecessary_generator_list(expr: &Expr, func: &Expr, args: &[Expr]) -> Op
}
/// C401 (`set(generator)`)
pub fn unnecessary_generator_set(expr: &Expr, func: &Expr, args: &[Expr]) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("set", func, args)?;
pub fn unnecessary_generator_set(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("set", func, args, keywords)?;
if let ExprKind::GeneratorExp { .. } = argument {
return Some(Check::new(
CheckKind::UnnecessaryGeneratorSet,
@@ -62,8 +78,13 @@ pub fn unnecessary_generator_set(expr: &Expr, func: &Expr, args: &[Expr]) -> Opt
}
/// C402 (`dict((x, y) for x, y in iterable)`)
pub fn unnecessary_generator_dict(expr: &Expr, func: &Expr, args: &[Expr]) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("dict", func, args)?;
pub fn unnecessary_generator_dict(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("dict", func, args, keywords)?;
if let ExprKind::GeneratorExp { elt, .. } = argument {
match &elt.node {
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
@@ -83,8 +104,9 @@ pub fn unnecessary_list_comprehension_set(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("set", func, args)?;
let argument = exactly_one_argument_with_matching_function("set", func, args, keywords)?;
if let ExprKind::ListComp { .. } = &argument {
return Some(Check::new(
CheckKind::UnnecessaryListComprehensionSet,
@@ -99,8 +121,9 @@ pub fn unnecessary_list_comprehension_dict(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("dict", func, args)?;
let argument = exactly_one_argument_with_matching_function("dict", func, args, keywords)?;
if let ExprKind::ListComp { elt, .. } = &argument {
match &elt.node {
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
@@ -116,8 +139,13 @@ pub fn unnecessary_list_comprehension_dict(
}
/// C405 (`set([1, 2])`)
pub fn unnecessary_literal_set(expr: &Expr, func: &Expr, args: &[Expr]) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("set", func, args)?;
pub fn unnecessary_literal_set(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("set", func, args, keywords)?;
let kind = match argument {
ExprKind::List { .. } => "list",
ExprKind::Tuple { .. } => "tuple",
@@ -130,8 +158,13 @@ pub fn unnecessary_literal_set(expr: &Expr, func: &Expr, args: &[Expr]) -> Optio
}
/// C406 (`dict([(1, 2)])`)
pub fn unnecessary_literal_dict(expr: &Expr, func: &Expr, args: &[Expr]) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("dict", func, args)?;
pub fn unnecessary_literal_dict(
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("dict", func, args, keywords)?;
let (kind, elts) = match argument {
ExprKind::Tuple { elts, .. } => ("tuple", elts),
ExprKind::List { elts, .. } => ("list", elts),

View File

@@ -1,9 +1,9 @@
use rustpython_ast::Location;
use crate::ast::operations::SourceCodeLocator;
use crate::ast::types::Range;
use crate::checks::{Check, CheckKind};
use crate::flake8_quotes::settings::{Quote, Settings};
use crate::source_code_locator::SourceCodeLocator;
fn good_single(quote: &Quote) -> char {
match quote {

View File

@@ -1,4 +1,4 @@
//! Settings for the `flake_quotes` plugin.
//! Settings for the `flake-quotes` plugin.
use serde::{Deserialize, Serialize};
@@ -27,12 +27,12 @@ pub struct Settings {
}
impl Settings {
pub fn from_config(config: Options) -> Self {
pub fn from_options(options: Options) -> Self {
Self {
inline_quotes: config.inline_quotes.unwrap_or(Quote::Single),
multiline_quotes: config.multiline_quotes.unwrap_or(Quote::Double),
docstring_quotes: config.docstring_quotes.unwrap_or(Quote::Double),
avoid_escape: config.avoid_escape.unwrap_or(true),
inline_quotes: options.inline_quotes.unwrap_or(Quote::Single),
multiline_quotes: options.multiline_quotes.unwrap_or(Quote::Double),
docstring_quotes: options.docstring_quotes.unwrap_or(Quote::Double),
avoid_escape: options.avoid_escape.unwrap_or(true),
}
}
}

View File

@@ -6,45 +6,45 @@ expression: checks
BadQuotesMultilineString: single
location:
row: 5
column: 1
column: 0
end_location:
row: 7
column: 4
column: 3
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 16
column: 5
column: 4
end_location:
row: 18
column: 8
column: 7
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 21
column: 21
column: 20
end_location:
row: 22
column: 38
column: 37
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 30
column: 9
column: 8
end_location:
row: 32
column: 12
column: 11
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 35
column: 13
column: 12
end_location:
row: 37
column: 16
column: 15
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesMultilineString: single
location:
row: 3
column: 5
column: 4
end_location:
row: 3
column: 28
column: 27
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 5
column: 23
column: 22
end_location:
row: 5
column: 44
column: 43
fix: ~

View File

@@ -6,45 +6,45 @@ expression: checks
BadQuotesMultilineString: single
location:
row: 3
column: 5
column: 4
end_location:
row: 3
column: 27
column: 26
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 11
column: 5
column: 4
end_location:
row: 11
column: 27
column: 26
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 15
column: 39
column: 38
end_location:
row: 17
column: 3
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 17
column: 4
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 17
column: 5
end_location:
row: 17
column: 20
column: 19
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 21
column: 5
column: 4
end_location:
row: 21
column: 28
column: 27
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesMultilineString: single
location:
row: 4
column: 1
column: 0
end_location:
row: 6
column: 4
column: 3
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 9
column: 1
column: 0
end_location:
row: 11
column: 4
column: 3
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesMultilineString: single
location:
row: 2
column: 1
column: 0
end_location:
row: 2
column: 32
column: 31
fix: ~
- kind:
BadQuotesMultilineString: single
location:
row: 6
column: 1
column: 0
end_location:
row: 6
column: 32
column: 31
fix: ~

View File

@@ -6,27 +6,27 @@ expression: checks
BadQuotesDocstring: double
location:
row: 1
column: 1
column: 0
end_location:
row: 3
column: 4
column: 3
fix: ~
- kind:
BadQuotesDocstring: double
location:
row: 14
column: 5
column: 4
end_location:
row: 16
column: 8
column: 7
fix: ~
- kind:
BadQuotesDocstring: double
location:
row: 26
column: 9
column: 8
end_location:
row: 28
column: 12
column: 11
fix: ~

View File

@@ -6,27 +6,27 @@ expression: checks
BadQuotesDocstring: double
location:
row: 2
column: 5
column: 4
end_location:
row: 2
column: 54
fix: ~
- kind:
BadQuotesDocstring: double
location:
row: 6
column: 9
end_location:
row: 6
column: 58
fix: ~
- kind:
BadQuotesDocstring: double
location:
row: 9
column: 29
end_location:
row: 9
column: 53
fix: ~
- kind:
BadQuotesDocstring: double
location:
row: 6
column: 8
end_location:
row: 6
column: 57
fix: ~
- kind:
BadQuotesDocstring: double
location:
row: 9
column: 28
end_location:
row: 9
column: 52
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesDocstring: double
location:
row: 2
column: 5
column: 4
end_location:
row: 2
column: 57
column: 56
fix: ~
- kind:
BadQuotesDocstring: double
location:
row: 8
column: 5
column: 4
end_location:
row: 10
column: 8
column: 7
fix: ~

View File

@@ -6,9 +6,9 @@ expression: checks
BadQuotesDocstring: double
location:
row: 1
column: 1
column: 0
end_location:
row: 3
column: 4
column: 3
fix: ~

View File

@@ -6,9 +6,9 @@ expression: checks
BadQuotesDocstring: double
location:
row: 1
column: 1
column: 0
end_location:
row: 1
column: 50
column: 49
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesInlineString: single
location:
row: 1
column: 25
column: 24
end_location:
row: 1
column: 46
column: 45
fix: ~
- kind:
BadQuotesInlineString: single
location:
row: 2
column: 25
column: 24
end_location:
row: 2
column: 47
column: 46
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: AvoidQuoteEscape
location:
row: 1
column: 26
column: 25
end_location:
row: 1
column: 48
column: 47
fix: ~

View File

@@ -6,9 +6,9 @@ expression: checks
BadQuotesMultilineString: single
location:
row: 1
column: 5
column: 4
end_location:
row: 3
column: 13
column: 12
fix: ~

View File

@@ -6,27 +6,27 @@ expression: checks
BadQuotesDocstring: single
location:
row: 1
column: 1
column: 0
end_location:
row: 3
column: 4
column: 3
fix: ~
- kind:
BadQuotesDocstring: single
location:
row: 12
column: 5
column: 4
end_location:
row: 14
column: 8
column: 7
fix: ~
- kind:
BadQuotesDocstring: single
location:
row: 24
column: 9
column: 8
end_location:
row: 26
column: 12
column: 11
fix: ~

View File

@@ -6,27 +6,27 @@ expression: checks
BadQuotesDocstring: single
location:
row: 2
column: 5
column: 4
end_location:
row: 2
column: 54
fix: ~
- kind:
BadQuotesDocstring: single
location:
row: 6
column: 9
end_location:
row: 6
column: 58
fix: ~
- kind:
BadQuotesDocstring: single
location:
row: 9
column: 29
end_location:
row: 9
column: 53
fix: ~
- kind:
BadQuotesDocstring: single
location:
row: 6
column: 8
end_location:
row: 6
column: 57
fix: ~
- kind:
BadQuotesDocstring: single
location:
row: 9
column: 28
end_location:
row: 9
column: 52
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesDocstring: single
location:
row: 2
column: 5
column: 4
end_location:
row: 2
column: 57
column: 56
fix: ~
- kind:
BadQuotesDocstring: single
location:
row: 8
column: 5
column: 4
end_location:
row: 10
column: 8
column: 7
fix: ~

View File

@@ -6,9 +6,9 @@ expression: checks
BadQuotesDocstring: single
location:
row: 1
column: 1
column: 0
end_location:
row: 3
column: 4
column: 3
fix: ~

View File

@@ -6,9 +6,9 @@ expression: checks
BadQuotesDocstring: single
location:
row: 1
column: 1
column: 0
end_location:
row: 1
column: 50
column: 49
fix: ~

View File

@@ -6,54 +6,54 @@ expression: checks
BadQuotesMultilineString: double
location:
row: 5
column: 1
column: 0
end_location:
row: 7
column: 4
column: 3
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 11
column: 21
column: 20
end_location:
row: 13
column: 4
column: 3
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 18
column: 5
column: 4
end_location:
row: 20
column: 8
column: 7
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 23
column: 21
column: 20
end_location:
row: 24
column: 38
column: 37
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 32
column: 9
column: 8
end_location:
row: 34
column: 12
column: 11
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 37
column: 13
column: 12
end_location:
row: 39
column: 16
column: 15
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesMultilineString: double
location:
row: 3
column: 5
column: 4
end_location:
row: 3
column: 28
column: 27
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 5
column: 23
column: 22
end_location:
row: 5
column: 44
column: 43
fix: ~

View File

@@ -6,45 +6,45 @@ expression: checks
BadQuotesMultilineString: double
location:
row: 3
column: 5
column: 4
end_location:
row: 3
column: 27
column: 26
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 11
column: 5
column: 4
end_location:
row: 11
column: 27
column: 26
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 15
column: 39
column: 38
end_location:
row: 17
column: 3
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 17
column: 4
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 17
column: 5
end_location:
row: 17
column: 20
column: 19
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 21
column: 5
column: 4
end_location:
row: 21
column: 28
column: 27
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesMultilineString: double
location:
row: 4
column: 1
column: 0
end_location:
row: 6
column: 4
column: 3
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 9
column: 1
column: 0
end_location:
row: 11
column: 4
column: 3
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesMultilineString: double
location:
row: 2
column: 1
column: 0
end_location:
row: 2
column: 32
column: 31
fix: ~
- kind:
BadQuotesMultilineString: double
location:
row: 6
column: 1
column: 0
end_location:
row: 6
column: 32
column: 31
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BadQuotesInlineString: double
location:
row: 1
column: 25
column: 24
end_location:
row: 1
column: 46
column: 45
fix: ~
- kind:
BadQuotesInlineString: double
location:
row: 2
column: 25
column: 24
end_location:
row: 2
column: 47
column: 46
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: AvoidQuoteEscape
location:
row: 1
column: 26
column: 25
end_location:
row: 1
column: 48
column: 47
fix: ~

View File

@@ -6,9 +6,9 @@ expression: checks
BadQuotesMultilineString: double
location:
row: 1
column: 5
column: 4
end_location:
row: 3
column: 13
column: 12
fix: ~

View File

@@ -44,10 +44,11 @@ mod pyflakes;
mod python;
mod pyupgrade;
pub mod settings;
pub mod source_code_locator;
pub mod visibility;
/// Run ruff over Python source code directly.
pub fn check(path: &Path, contents: &str, quiet: bool) -> Result<Vec<Message>> {
pub fn check(path: &Path, contents: &str) -> Result<Vec<Message>> {
// Find the project root and pyproject.toml.
let project_root = pyproject::find_project_root(&[path.to_path_buf()]);
match &project_root {
@@ -60,11 +61,8 @@ pub fn check(path: &Path, contents: &str, quiet: bool) -> Result<Vec<Message>> {
None => debug!("Unable to find pyproject.toml; using default settings..."),
};
let settings = Settings::from_configuration(Configuration::from_pyproject(
&pyproject,
&project_root,
quiet,
)?);
let settings =
Settings::from_configuration(Configuration::from_pyproject(&pyproject, &project_root)?);
// Tokenize once.
let tokens: Vec<LexResult> = tokenize(contents);

View File

@@ -6,7 +6,10 @@ use std::path::Path;
use anyhow::Result;
#[cfg(not(target_family = "wasm"))]
use log::debug;
use rustpython_ast::{Mod, Suite};
use rustpython_parser::error::ParseError;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::parser::Mode;
use rustpython_parser::{lexer, parser};
use crate::ast::types::Range;
@@ -20,6 +23,7 @@ use crate::code_gen::SourceGenerator;
use crate::message::Message;
use crate::noqa::add_noqa;
use crate::settings::Settings;
use crate::source_code_locator::SourceCodeLocator;
use crate::{cache, fs, noqa};
/// Collect tokens up to and including the first error.
@@ -35,6 +39,17 @@ pub(crate) fn tokenize(contents: &str) -> Vec<LexResult> {
tokens
}
/// Parse a full Python program from its tokens.
pub(crate) fn parse_program_tokens(
lxr: Vec<LexResult>,
source_path: &str,
) -> Result<Suite, ParseError> {
parser::parse_tokens(lxr, Mode::Module, source_path).map(|top| match top {
Mod::Module { body, .. } => body,
_ => unreachable!(),
})
}
pub(crate) fn check_path(
path: &Path,
contents: &str,
@@ -46,13 +61,16 @@ pub(crate) fn check_path(
// Aggregate all checks.
let mut checks: Vec<Check> = vec![];
// Initialize the SourceCodeLocator (which computes offsets lazily).
let locator = SourceCodeLocator::new(contents);
// Run the token-based checks.
if settings
.enabled
.iter()
.any(|check_code| matches!(check_code.lint_source(), LintSource::Tokens))
{
check_tokens(&mut checks, contents, &tokens, settings);
check_tokens(&mut checks, &locator, &tokens, settings);
}
// Run the AST-based checks.
@@ -61,9 +79,9 @@ pub(crate) fn check_path(
.iter()
.any(|check_code| matches!(check_code.lint_source(), LintSource::AST))
{
match parser::parse_program_tokens(tokens, "<filename>") {
match parse_program_tokens(tokens, "<filename>") {
Ok(python_ast) => {
checks.extend(check_ast(&python_ast, contents, settings, autofix, path))
checks.extend(check_ast(&python_ast, &locator, settings, autofix, path))
}
Err(parse_error) => {
if settings.enabled.contains(&CheckCode::E999) {
@@ -216,7 +234,7 @@ pub fn autoformat_path(path: &Path) -> Result<()> {
let tokens: Vec<LexResult> = tokenize(&contents);
// Generate the AST.
let python_ast = parser::parse_program_tokens(tokens, "<filename>")?;
let python_ast = parse_program_tokens(tokens, "<filename>")?;
let mut generator: SourceGenerator = Default::default();
generator.unparse_suite(&python_ast)?;
write(path, generator.generate()?)?;
@@ -252,8 +270,10 @@ mod tests {
#[test_case(CheckCode::A002, Path::new("A002.py"); "A002")]
#[test_case(CheckCode::A003, Path::new("A003.py"); "A003")]
#[test_case(CheckCode::B002, Path::new("B002.py"); "B002")]
#[test_case(CheckCode::B006, Path::new("B006_B008.py"); "B006")]
#[test_case(CheckCode::B007, Path::new("B007.py"); "B007")]
#[test_case(CheckCode::B011, Path::new("B011.py"); "B011")]
#[test_case(CheckCode::B013, Path::new("B013.py"); "B013")]
#[test_case(CheckCode::B014, Path::new("B014.py"); "B014")]
#[test_case(CheckCode::B017, Path::new("B017.py"); "B017")]
#[test_case(CheckCode::B025, Path::new("B025.py"); "B025")]
@@ -368,12 +388,16 @@ mod tests {
#[test_case(CheckCode::N803, Path::new("N803.py"); "N803")]
#[test_case(CheckCode::N804, Path::new("N804.py"); "N804")]
#[test_case(CheckCode::N805, Path::new("N805.py"); "N805")]
#[test_case(CheckCode::N806, Path::new("N806.py"); "N806")]
#[test_case(CheckCode::N807, Path::new("N807.py"); "N807")]
#[test_case(CheckCode::N811, Path::new("N811.py"); "N811")]
#[test_case(CheckCode::N812, Path::new("N812.py"); "N812")]
#[test_case(CheckCode::N813, Path::new("N813.py"); "N813")]
#[test_case(CheckCode::N814, Path::new("N814.py"); "N814")]
#[test_case(CheckCode::N815, Path::new("N815.py"); "N815")]
#[test_case(CheckCode::N816, Path::new("N816.py"); "N816")]
#[test_case(CheckCode::N817, Path::new("N817.py"); "N817")]
#[test_case(CheckCode::N818, Path::new("N818.py"); "N818")]
#[test_case(CheckCode::T201, Path::new("T201.py"); "T201")]
#[test_case(CheckCode::T203, Path::new("T203.py"); "T203")]
#[test_case(CheckCode::U001, Path::new("U001.py"); "U001")]

View File

@@ -261,7 +261,7 @@ fn inner_main() -> Result<ExitCode> {
.map(|pair| PerFileIgnore::new(pair, &project_root))
.collect();
let mut configuration = Configuration::from_pyproject(&pyproject, &project_root, cli.quiet)?;
let mut configuration = Configuration::from_pyproject(&pyproject, &project_root)?;
if !exclude.is_empty() {
configuration.exclude = exclude;
}

View File

@@ -246,8 +246,8 @@ ghi
let checks = vec![Check::new(
CheckKind::UnusedVariable("x".to_string()),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
location: Location::new(1, 0),
end_location: Location::new(1, 0),
},
)];
let contents = "x = 1";
@@ -260,15 +260,15 @@ ghi
Check::new(
CheckKind::AmbiguousVariableName("x".to_string()),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
location: Location::new(1, 0),
end_location: Location::new(1, 0),
},
),
Check::new(
CheckKind::UnusedVariable("x".to_string()),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
location: Location::new(1, 0),
end_location: Location::new(1, 0),
},
),
];
@@ -282,15 +282,15 @@ ghi
Check::new(
CheckKind::AmbiguousVariableName("x".to_string()),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
location: Location::new(1, 0),
end_location: Location::new(1, 0),
},
),
Check::new(
CheckKind::UnusedVariable("x".to_string()),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
location: Location::new(1, 0),
end_location: Location::new(1, 0),
},
),
];

View File

@@ -1,9 +1,11 @@
use itertools::Itertools;
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
use crate::ast::types::{Range, Scope, ScopeKind};
use crate::ast::types::{FunctionScope, Range, Scope, ScopeKind};
use crate::checks::{Check, CheckKind};
use crate::pep8_naming::settings::Settings;
/// N801
pub fn invalid_class_name(class_def: &Stmt, name: &str) -> Option<Check> {
let stripped = name.strip_prefix('_').unwrap_or(name);
if !stripped
@@ -21,8 +23,14 @@ pub fn invalid_class_name(class_def: &Stmt, name: &str) -> Option<Check> {
None
}
pub fn invalid_function_name(func_def: &Stmt, name: &str) -> Option<Check> {
if name.chars().any(|c| c.is_uppercase()) {
/// N802
pub fn invalid_function_name(func_def: &Stmt, name: &str, settings: &Settings) -> Option<Check> {
if !is_lower(name)
&& !settings
.ignore_names
.iter()
.any(|ignore_name| ignore_name == name)
{
return Some(Check::new(
CheckKind::InvalidFunctionName(name.to_string()),
Range::from_located(func_def),
@@ -31,8 +39,9 @@ pub fn invalid_function_name(func_def: &Stmt, name: &str) -> Option<Check> {
None
}
/// N803
pub fn invalid_argument_name(location: Range, name: &str) -> Option<Check> {
if name.chars().any(|c| c.is_uppercase()) {
if !is_lower(name) {
return Some(Check::new(
CheckKind::InvalidArgumentName(name.to_string()),
location,
@@ -41,10 +50,12 @@ pub fn invalid_argument_name(location: Range, name: &str) -> Option<Check> {
None
}
/// N804
pub fn invalid_first_argument_name_for_class_method(
scope: &Scope,
decorator_list: &[Expr],
args: &Arguments,
settings: &Settings,
) -> Option<Check> {
if !matches!(scope.kind, ScopeKind::Class) {
return None;
@@ -52,7 +63,7 @@ pub fn invalid_first_argument_name_for_class_method(
if decorator_list.iter().any(|decorator| {
if let ExprKind::Name { id, .. } = &decorator.node {
id == "classmethod"
settings.classmethod_decorators.contains(id)
} else {
false
}
@@ -69,10 +80,12 @@ pub fn invalid_first_argument_name_for_class_method(
None
}
/// N805
pub fn invalid_first_argument_name_for_method(
scope: &Scope,
decorator_list: &[Expr],
args: &Arguments,
settings: &Settings,
) -> Option<Check> {
if !matches!(scope.kind, ScopeKind::Class) {
return None;
@@ -80,7 +93,8 @@ pub fn invalid_first_argument_name_for_method(
if decorator_list.iter().any(|decorator| {
if let ExprKind::Name { id, .. } = &decorator.node {
id == "classmethod" || id == "staticmethod"
settings.classmethod_decorators.contains(id)
|| settings.staticmethod_decorators.contains(id)
} else {
false
}
@@ -99,6 +113,21 @@ pub fn invalid_first_argument_name_for_method(
None
}
/// N806
pub fn non_lowercase_variable_in_function(scope: &Scope, expr: &Expr, name: &str) -> Option<Check> {
if !matches!(scope.kind, ScopeKind::Function(FunctionScope { .. })) {
return None;
}
if !is_lower(name) {
return Some(Check::new(
CheckKind::NonLowercaseVariableInFunction(name.to_string()),
Range::from_located(expr),
));
}
None
}
/// N807
pub fn dunder_function_name(func_def: &Stmt, scope: &Scope, name: &str) -> Option<Check> {
if matches!(scope.kind, ScopeKind::Class) {
return None;
@@ -114,6 +143,136 @@ pub fn dunder_function_name(func_def: &Stmt, scope: &Scope, name: &str) -> Optio
None
}
/// N811
pub fn constant_imported_as_non_constant(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_upper(name) && !is_upper(asname) {
return Some(Check::new(
CheckKind::ConstantImportedAsNonConstant(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
/// N812
pub fn lowercase_imported_as_non_lowercase(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_lower(name) && asname.to_lowercase() != asname {
return Some(Check::new(
CheckKind::LowercaseImportedAsNonLowercase(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
/// N813
pub fn camelcase_imported_as_lowercase(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_camelcase(name) && is_lower(asname) {
return Some(Check::new(
CheckKind::CamelcaseImportedAsLowercase(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
/// N814
pub fn camelcase_imported_as_constant(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_camelcase(name) && is_upper(asname) && !is_acronym(name, asname) {
return Some(Check::new(
CheckKind::CamelcaseImportedAsConstant(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
/// N815
pub fn mixed_case_variable_in_class_scope(scope: &Scope, expr: &Expr, name: &str) -> Option<Check> {
if !matches!(scope.kind, ScopeKind::Class) {
return None;
}
if is_mixed_case(name) {
return Some(Check::new(
CheckKind::MixedCaseVariableInClassScope(name.to_string()),
Range::from_located(expr),
));
}
None
}
/// N816
pub fn mixed_case_variable_in_global_scope(
scope: &Scope,
expr: &Expr,
name: &str,
) -> Option<Check> {
if !matches!(scope.kind, ScopeKind::Module) {
return None;
}
if is_mixed_case(name) {
return Some(Check::new(
CheckKind::MixedCaseVariableInGlobalScope(name.to_string()),
Range::from_located(expr),
));
}
None
}
/// N817
pub fn camelcase_imported_as_acronym(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_camelcase(name) && is_upper(asname) && is_acronym(name, asname) {
return Some(Check::new(
CheckKind::CamelcaseImportedAsAcronym(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
/// N818
pub fn error_suffix_on_exception_name(
class_def: &Stmt,
bases: &[Expr],
name: &str,
) -> Option<Check> {
if bases.iter().any(|base| {
if let ExprKind::Name { id, .. } = &base.node {
id == "Exception"
} else {
false
}
}) {
if !name.ends_with("Error") {
return Some(Check::new(
CheckKind::ErrorSuffixOnExceptionName(name.to_string()),
Range::from_located(class_def),
));
}
}
None
}
fn is_lower(s: &str) -> bool {
let mut cased = false;
for c in s.chars() {
@@ -138,86 +297,27 @@ fn is_upper(s: &str) -> bool {
cased
}
pub fn constant_imported_as_non_constant(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_upper(name) && !is_upper(asname) {
return Some(Check::new(
CheckKind::ConstantImportedAsNonConstant(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
pub fn lowercase_imported_as_non_lowercase(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_lower(name) && asname.to_lowercase() != asname {
return Some(Check::new(
CheckKind::LowercaseImportedAsNonLowercase(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
fn is_camelcase(name: &str) -> bool {
!is_lower(name) && !is_upper(name) && !name.contains('_')
}
fn is_mixed_case(name: &str) -> bool {
!is_lower(name)
&& name
.strip_prefix('_')
.unwrap_or(name)
.chars()
.next()
.map_or_else(|| false, |c| c.is_lowercase())
}
fn is_acronym(name: &str, asname: &str) -> bool {
name.chars().filter(|c| c.is_uppercase()).join("") == asname
}
pub fn camelcase_imported_as_lowercase(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_camelcase(name) && is_lower(asname) {
return Some(Check::new(
CheckKind::CamelcaseImportedAsLowercase(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
pub fn camelcase_imported_as_constant(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_camelcase(name) && is_upper(asname) && !is_acronym(name, asname) {
return Some(Check::new(
CheckKind::CamelcaseImportedAsConstant(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
pub fn camelcase_imported_as_acronym(
import_from: &Stmt,
name: &str,
asname: &str,
) -> Option<Check> {
if is_camelcase(name) && is_upper(asname) && is_acronym(name, asname) {
return Some(Check::new(
CheckKind::CamelcaseImportedAsAcronym(name.to_string(), asname.to_string()),
Range::from_located(import_from),
));
}
None
}
#[cfg(test)]
mod tests {
use super::{is_acronym, is_camelcase, is_lower, is_upper};
use super::{is_acronym, is_camelcase, is_lower, is_mixed_case, is_upper};
#[test]
fn test_is_lower() -> () {
@@ -251,6 +351,17 @@ mod tests {
assert!(!is_camelcase("CAMEL_CASE"));
}
#[test]
fn test_is_mixed_case() -> () {
assert!(is_mixed_case("mixedCase"));
assert!(is_mixed_case("mixed_Case"));
assert!(is_mixed_case("_mixed_Case"));
assert!(!is_mixed_case("mixed_case"));
assert!(!is_mixed_case("MIXED_CASE"));
assert!(!is_mixed_case(""));
assert!(!is_mixed_case("_"));
}
#[test]
fn test_is_acronym() -> () {
assert!(is_acronym("AB", "AB"));

View File

@@ -1 +1,2 @@
pub mod checks;
pub mod settings;

View File

@@ -0,0 +1,63 @@
//! Settings for the `pep8-naming` plugin.
use serde::Deserialize;
const IGNORE_NAMES: [&str; 12] = [
"setUp",
"tearDown",
"setUpClass",
"tearDownClass",
"setUpModule",
"tearDownModule",
"asyncSetUp",
"asyncTearDown",
"setUpTestData",
"failureException",
"longMessage",
"maxDiff",
];
const CLASSMETHOD_DECORATORS: [&str; 1] = ["classmethod"];
const STATICMETHOD_DECORATORS: [&str; 1] = ["staticmethod"];
#[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct Options {
pub ignore_names: Option<Vec<String>>,
pub classmethod_decorators: Option<Vec<String>>,
pub staticmethod_decorators: Option<Vec<String>>,
}
#[derive(Debug)]
pub struct Settings {
pub ignore_names: Vec<String>,
pub classmethod_decorators: Vec<String>,
pub staticmethod_decorators: Vec<String>,
}
impl Settings {
pub fn from_options(options: Options) -> Self {
Self {
ignore_names: options
.ignore_names
.unwrap_or_else(|| IGNORE_NAMES.map(String::from).to_vec()),
classmethod_decorators: options
.classmethod_decorators
.unwrap_or_else(|| CLASSMETHOD_DECORATORS.map(String::from).to_vec()),
staticmethod_decorators: options
.staticmethod_decorators
.unwrap_or_else(|| STATICMETHOD_DECORATORS.map(String::from).to_vec()),
}
}
}
impl Default for Settings {
fn default() -> Self {
Self {
ignore_names: IGNORE_NAMES.map(String::from).to_vec(),
classmethod_decorators: CLASSMETHOD_DECORATORS.map(String::from).to_vec(),
staticmethod_decorators: STATICMETHOD_DECORATORS.map(String::from).to_vec(),
}
}
}

View File

@@ -2,9 +2,9 @@ use itertools::izip;
use rustpython_ast::Location;
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Unaryop};
use crate::ast::operations::SourceCodeLocator;
use crate::ast::types::{CheckLocator, Range};
use crate::checks::{Check, CheckKind, RejectedCmpop};
use crate::source_code_locator::SourceCodeLocator;
fn is_ambiguous_name(name: &str) -> bool {
name == "l" || name == "I" || name == "O"

View File

@@ -35,8 +35,8 @@ pub fn not_missing(
checker.add_check(Check::new(
CheckKind::PublicModule,
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
location: Location::new(1, 0),
end_location: Location::new(1, 0),
},
));
}
@@ -47,8 +47,8 @@ pub fn not_missing(
checker.add_check(Check::new(
CheckKind::PublicPackage,
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
location: Location::new(1, 0),
end_location: Location::new(1, 0),
},
));
}
@@ -162,7 +162,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
} = &docstring.node
{
if checker.settings.enabled.contains(&CheckCode::D201) {
let (before, _, _) = checker.get_locator().partition_source_code_at(
let (before, _, _) = checker.locator.partition_source_code_at(
&Range::from_located(parent),
&Range::from_located(docstring),
);
@@ -181,8 +181,8 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
if checker.patch() {
// Delete the blank line before the docstring.
check.amend(Fix::deletion(
Location::new(docstring.location.row() - blank_lines_before, 1),
Location::new(docstring.location.row(), 1),
Location::new(docstring.location.row() - blank_lines_before, 0),
Location::new(docstring.location.row(), 0),
));
}
checker.add_check(check);
@@ -190,7 +190,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
}
if checker.settings.enabled.contains(&CheckCode::D202) {
let (_, _, after) = checker.get_locator().partition_source_code_at(
let (_, _, after) = checker.locator.partition_source_code_at(
&Range::from_located(parent),
&Range::from_located(docstring),
);
@@ -208,15 +208,13 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
.skip(1)
.take_while(|line| line.trim().is_empty())
.count();
// Report a D202 violation if the docstring is followed by a blank line and the
// blank line is not itself followed by an inner function or class.
let expected_blank_lines_after =
if INNER_FUNCTION_OR_CLASS_REGEX.is_match(after) {
1
} else {
0
};
if blank_lines_after != expected_blank_lines_after {
// Avoid D202 violations for blank lines followed by inner functions or classes.
if blank_lines_after == 1 && INNER_FUNCTION_OR_CLASS_REGEX.is_match(after) {
return;
}
if blank_lines_after != 0 {
let mut check = Check::new(
CheckKind::NoBlankLineAfterFunction(blank_lines_after),
Range::from_located(docstring),
@@ -224,11 +222,8 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
if checker.patch() {
// Delete the blank line after the docstring.
check.amend(Fix::deletion(
Location::new(
docstring.location.row() + 1 + expected_blank_lines_after,
1,
),
Location::new(docstring.location.row() + 1 + blank_lines_after, 1),
Location::new(docstring.location.row() + 1, 0),
Location::new(docstring.location.row() + 1 + blank_lines_after, 0),
));
}
checker.add_check(check);
@@ -253,7 +248,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
if checker.settings.enabled.contains(&CheckCode::D203)
|| checker.settings.enabled.contains(&CheckCode::D211)
{
let (before, _, _) = checker.get_locator().partition_source_code_at(
let (before, _, _) = checker.locator.partition_source_code_at(
&Range::from_located(parent),
&Range::from_located(docstring),
);
@@ -273,8 +268,8 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
if checker.patch() {
// Delete the blank line before the class.
check.amend(Fix::deletion(
Location::new(docstring.location.row() - blank_lines_before, 1),
Location::new(docstring.location.row(), 1),
Location::new(docstring.location.row() - blank_lines_before, 0),
Location::new(docstring.location.row(), 0),
));
}
checker.add_check(check);
@@ -290,8 +285,8 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
// Insert one blank line before the class.
check.amend(Fix::replacement(
"\n".to_string(),
Location::new(docstring.location.row() - blank_lines_before, 1),
Location::new(docstring.location.row(), 1),
Location::new(docstring.location.row() - blank_lines_before, 0),
Location::new(docstring.location.row(), 0),
));
}
checker.add_check(check);
@@ -300,7 +295,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
}
if checker.settings.enabled.contains(&CheckCode::D204) {
let (_, _, after) = checker.get_locator().partition_source_code_at(
let (_, _, after) = checker.locator.partition_source_code_at(
&Range::from_located(parent),
&Range::from_located(docstring),
);
@@ -327,10 +322,10 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
// Insert a blank line before the class (replacing any existing lines).
check.amend(Fix::replacement(
"\n".to_string(),
Location::new(docstring.end_location.unwrap().row() + 1, 1),
Location::new(docstring.end_location.unwrap().row() + 1, 0),
Location::new(
docstring.end_location.unwrap().row() + 1 + blank_lines_after,
1,
0,
),
));
}
@@ -369,8 +364,8 @@ pub fn blank_after_summary(checker: &mut Checker, definition: &Definition) {
// Insert one blank line after the summary (replacing any existing lines).
check.amend(Fix::replacement(
"\n".to_string(),
Location::new(docstring.location.row() + 1, 1),
Location::new(docstring.location.row() + 1 + blanks_count, 1),
Location::new(docstring.location.row() + 1, 0),
Location::new(docstring.location.row() + 1 + blanks_count, 0),
));
}
checker.add_check(check);
@@ -404,7 +399,8 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
// Omit empty lines, except for the last line, which is non-empty by way of
// containing the closing quotation marks.
if i < lines.len() - 1 && lines[i].trim().is_empty() {
let is_blank = lines[i].trim().is_empty();
if i < lines.len() - 1 && is_blank {
continue;
}
@@ -416,19 +412,19 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
if checker.settings.enabled.contains(&CheckCode::D207) {
// We report under-indentation on every line. This isn't great, but enables
// autofix.
if line_indent.len() < docstring_indent.len() {
if !is_blank && line_indent.len() < docstring_indent.len() {
let mut check = Check::new(
CheckKind::NoUnderIndentation,
Range {
location: Location::new(docstring.location.row() + i, 1),
end_location: Location::new(docstring.location.row() + i, 1),
location: Location::new(docstring.location.row() + i, 0),
end_location: Location::new(docstring.location.row() + i, 0),
},
);
if checker.patch() {
check.amend(Fix::replacement(
helpers::clean(&docstring_indent),
Location::new(docstring.location.row() + i, 1),
Location::new(docstring.location.row() + i, 1 + line_indent.len()),
Location::new(docstring.location.row() + i, 0),
Location::new(docstring.location.row() + i, line_indent.len()),
));
}
checker.add_check(check);
@@ -469,18 +465,15 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
let mut check = Check::new(
CheckKind::NoOverIndentation,
Range {
location: Location::new(docstring.location.row() + i, 1),
end_location: Location::new(docstring.location.row() + i, 1),
location: Location::new(docstring.location.row() + i, 0),
end_location: Location::new(docstring.location.row() + i, 0),
},
);
if checker.patch() {
check.amend(Fix::replacement(
helpers::clean(&docstring_indent),
Location::new(docstring.location.row() + i, 1),
Location::new(
docstring.location.row() + i,
1 + line_indent.len(),
),
Location::new(docstring.location.row() + i, 0),
Location::new(docstring.location.row() + i, line_indent.len()),
));
}
checker.add_check(check);
@@ -496,15 +489,15 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
let mut check = Check::new(
CheckKind::NoOverIndentation,
Range {
location: Location::new(docstring.location.row() + i, 1),
end_location: Location::new(docstring.location.row() + i, 1),
location: Location::new(docstring.location.row() + i, 0),
end_location: Location::new(docstring.location.row() + i, 0),
},
);
if checker.patch() {
check.amend(Fix::replacement(
helpers::clean(&docstring_indent),
Location::new(docstring.location.row() + i, 1),
Location::new(docstring.location.row() + i, 1 + line_indent.len()),
Location::new(docstring.location.row() + i, 0),
Location::new(docstring.location.row() + i, line_indent.len()),
));
}
checker.add_check(check);
@@ -530,7 +523,7 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, definition: &Definiti
}
if line_count > 1 {
let content = checker
.get_locator()
.locator
.slice_source_code_range(&Range::from_located(docstring));
if let Some(last_line) = content.lines().last().map(|line| line.trim()) {
if last_line != "\"\"\"" && last_line != "'''" {
@@ -583,7 +576,7 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, definition: &Definition)
);
if checker.patch() {
if let Some(first_line) = checker
.get_locator()
.locator
.slice_source_code_range(&Range::from_located(docstring))
.lines()
.next()
@@ -629,7 +622,7 @@ pub fn multi_line_summary_start(checker: &mut Checker, definition: &Definition)
{
if string.lines().nth(1).is_some() {
if let Some(first_line) = checker
.get_locator()
.locator
.slice_source_code_range(&Range::from_located(docstring))
.lines()
.next()
@@ -665,7 +658,7 @@ pub fn triple_quotes(checker: &mut Checker, definition: &Definition) {
} = &docstring.node
{
if let Some(first_line) = checker
.get_locator()
.locator
.slice_source_code_range(&Range::from_located(docstring))
.lines()
.next()
@@ -925,7 +918,7 @@ fn blanks_and_section_underline(
);
check.amend(Fix::insertion(
content,
Location::new(docstring.location.row() + context.original_index + 1, 1),
Location::new(docstring.location.row() + context.original_index + 1, 0),
));
}
checker.add_check(check);
@@ -959,7 +952,7 @@ fn blanks_and_section_underline(
);
check.amend(Fix::insertion(
content,
Location::new(docstring.location.row() + context.original_index + 1, 1),
Location::new(docstring.location.row() + context.original_index + 1, 0),
));
}
checker.add_check(check);
@@ -975,13 +968,13 @@ fn blanks_and_section_underline(
if checker.patch() {
// Delete any blank lines between the header and content.
check.amend(Fix::deletion(
Location::new(docstring.location.row() + context.original_index + 1, 1),
Location::new(docstring.location.row() + context.original_index + 1, 0),
Location::new(
docstring.location.row()
+ context.original_index
+ 1
+ blank_lines_after_header,
1,
0,
),
));
}
@@ -998,13 +991,13 @@ fn blanks_and_section_underline(
if checker.patch() {
// Delete any blank lines between the header and the underline.
check.amend(Fix::deletion(
Location::new(docstring.location.row() + context.original_index + 1, 1),
Location::new(docstring.location.row() + context.original_index + 1, 0),
Location::new(
docstring.location.row()
+ context.original_index
+ 1
+ blank_lines_after_header,
1,
0,
),
));
}
@@ -1040,7 +1033,7 @@ fn blanks_and_section_underline(
+ context.original_index
+ 1
+ blank_lines_after_header,
1,
0,
),
Location::new(
docstring.location.row()
@@ -1048,7 +1041,7 @@ fn blanks_and_section_underline(
+ 1
+ blank_lines_after_header
+ 1,
1,
0,
),
));
};
@@ -1073,7 +1066,7 @@ fn blanks_and_section_underline(
+ context.original_index
+ 1
+ blank_lines_after_header,
1,
0,
),
Location::new(
docstring.location.row()
@@ -1121,7 +1114,7 @@ fn blanks_and_section_underline(
+ context.original_index
+ 1
+ line_after_dashes_index,
1,
0,
),
Location::new(
docstring.location.row()
@@ -1129,7 +1122,7 @@ fn blanks_and_section_underline(
+ 1
+ line_after_dashes_index
+ blank_lines_after_dashes,
1,
0,
),
));
}
@@ -1183,11 +1176,11 @@ fn common_section(
capitalized_section_name,
Location::new(
docstring.location.row() + context.original_index,
1 + section_name_start,
*section_name_start,
),
Location::new(
docstring.location.row() + context.original_index,
1 + section_name_start + section_name_length,
section_name_start + section_name_length,
),
))
}
@@ -1209,10 +1202,10 @@ fn common_section(
// Replace the existing indentation with whitespace of the appropriate length.
check.amend(Fix::replacement(
helpers::clean(&indentation),
Location::new(docstring.location.row() + context.original_index, 1),
Location::new(docstring.location.row() + context.original_index, 0),
Location::new(
docstring.location.row() + context.original_index,
1 + leading_space.len(),
leading_space.len(),
),
));
};
@@ -1241,7 +1234,7 @@ fn common_section(
+ context.original_index
+ 1
+ context.following_lines.len(),
1,
0,
),
));
}
@@ -1262,7 +1255,7 @@ fn common_section(
+ context.original_index
+ 1
+ context.following_lines.len(),
1,
0,
),
));
}
@@ -1281,7 +1274,7 @@ fn common_section(
// Add a blank line before the section.
check.amend(Fix::insertion(
"\n".to_string(),
Location::new(docstring.location.row() + context.original_index, 1),
Location::new(docstring.location.row() + context.original_index, 0),
));
}
checker.add_check(check)
@@ -1448,11 +1441,11 @@ fn numpy_section(checker: &mut Checker, definition: &Definition, context: &Secti
check.amend(Fix::deletion(
Location::new(
docstring.location.row() + context.original_index,
1 + suffix_start,
*suffix_start,
),
Location::new(
docstring.location.row() + context.original_index,
1 + suffix_start + suffix_length,
suffix_start + suffix_length,
),
));
}
@@ -1498,11 +1491,11 @@ fn google_section(checker: &mut Checker, definition: &Definition, context: &Sect
":".to_string(),
Location::new(
docstring.location.row() + context.original_index,
1 + suffix_start,
*suffix_start,
),
Location::new(
docstring.location.row() + context.original_index,
1 + suffix_start + suffix_length,
suffix_start + suffix_length,
),
));
}

View File

@@ -2,10 +2,10 @@ use anyhow::Result;
use libcst_native::{Codegen, ImportNames, NameOrAttribute, SmallStatement, Statement};
use rustpython_ast::Stmt;
use crate::ast::operations::SourceCodeLocator;
use crate::ast::types::Range;
use crate::autofix::{helpers, Fix};
use crate::cst::helpers::compose_module_path;
use crate::source_code_locator::SourceCodeLocator;
/// Generate a Fix to remove any unused imports from an `import` statement.
pub fn remove_unused_imports(

View File

@@ -4,9 +4,9 @@ use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use crate::ast::helpers;
use crate::ast::operations::SourceCodeLocator;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::source_code_locator::SourceCodeLocator;
/// Generate a fix to remove a base from a ClassDef statement.
pub fn remove_class_def_base(

View File

@@ -17,9 +17,7 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp
.collect();
if let Some(mut check) = checks::super_args(scope, &parents, expr, func, args) {
if checker.patch() {
if let Some(fix) =
pyupgrade::fixes::remove_super_arguments(checker.get_locator(), expr)
{
if let Some(fix) = pyupgrade::fixes::remove_super_arguments(checker.locator, expr) {
check.amend(fix);
}
}

View File

@@ -15,7 +15,7 @@ pub fn useless_object_inheritance(
if let Some(mut check) = checks::useless_object_inheritance(name, bases, scope) {
if checker.patch() {
if let Some(fix) = pyupgrade::fixes::remove_class_def_base(
checker.get_locator(),
checker.locator,
&stmt.location,
check.location,
bases,

View File

@@ -8,9 +8,9 @@ use once_cell::sync::Lazy;
use regex::Regex;
use crate::checks_gen::CheckCodePrefix;
use crate::flake8_quotes;
use crate::settings::pyproject::load_options;
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
use crate::{flake8_quotes, pep8_naming};
#[derive(Debug)]
pub struct Configuration {
@@ -26,6 +26,7 @@ pub struct Configuration {
pub target_version: PythonVersion,
// Plugins
pub flake8_quotes: flake8_quotes::settings::Settings,
pub pep8_naming: pep8_naming::settings::Settings,
}
static DEFAULT_EXCLUDE: Lazy<Vec<FilePattern>> = Lazy::new(|| {
@@ -59,9 +60,8 @@ impl Configuration {
pub fn from_pyproject(
pyproject: &Option<PathBuf>,
project_root: &Option<PathBuf>,
quiet: bool,
) -> Result<Self> {
let options = load_options(pyproject, quiet)?;
let options = load_options(pyproject)?;
Ok(Configuration {
dummy_variable_rgx: match options.dummy_variable_rgx {
Some(pattern) => Regex::new(&pattern)
@@ -98,7 +98,11 @@ impl Configuration {
// Plugins
flake8_quotes: options
.flake8_quotes
.map(flake8_quotes::settings::Settings::from_config)
.map(flake8_quotes::settings::Settings::from_options)
.unwrap_or_default(),
pep8_naming: options
.pep8_naming
.map(pep8_naming::settings::Settings::from_options)
.unwrap_or_default(),
})
}

View File

@@ -8,9 +8,9 @@ use regex::Regex;
use crate::checks::CheckCode;
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
use crate::flake8_quotes;
use crate::settings::configuration::Configuration;
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
use crate::{flake8_quotes, pep8_naming};
pub mod configuration;
pub mod options;
@@ -29,6 +29,7 @@ pub struct Settings {
pub target_version: PythonVersion,
// Plugins
pub flake8_quotes: flake8_quotes::settings::Settings,
pub pep8_naming: pep8_naming::settings::Settings,
}
impl Settings {
@@ -45,6 +46,7 @@ impl Settings {
extend_exclude: config.extend_exclude,
flake8_quotes: config.flake8_quotes,
line_length: config.line_length,
pep8_naming: config.pep8_naming,
per_file_ignores: config.per_file_ignores,
target_version: config.target_version,
}
@@ -60,6 +62,7 @@ impl Settings {
per_file_ignores: vec![],
target_version: PythonVersion::Py310,
flake8_quotes: Default::default(),
pep8_naming: Default::default(),
}
}
@@ -73,6 +76,7 @@ impl Settings {
per_file_ignores: vec![],
target_version: PythonVersion::Py310,
flake8_quotes: Default::default(),
pep8_naming: Default::default(),
}
}
}

View File

@@ -3,8 +3,8 @@
use serde::Deserialize;
use crate::checks_gen::CheckCodePrefix;
use crate::flake8_quotes;
use crate::settings::types::{PythonVersion, StrCheckCodePair};
use crate::{flake8_quotes, pep8_naming};
#[derive(Debug, PartialEq, Eq, Deserialize, Default)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
@@ -24,5 +24,7 @@ pub struct Options {
pub per_file_ignores: Vec<StrCheckCodePair>,
pub dummy_variable_rgx: Option<String>,
pub target_version: Option<PythonVersion>,
// Plugins
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
pub pep8_naming: Option<pep8_naming::settings::Options>,
}

View File

@@ -4,6 +4,7 @@ use std::path::{Path, PathBuf};
use anyhow::Result;
use common_path::common_path_all;
use log::debug;
use path_absolutize::Absolutize;
use serde::Deserialize;
@@ -69,17 +70,15 @@ pub fn find_project_root(sources: &[PathBuf]) -> Option<PathBuf> {
None
}
pub fn load_options(pyproject: &Option<PathBuf>, quiet: bool) -> Result<Options> {
pub fn load_options(pyproject: &Option<PathBuf>) -> Result<Options> {
match pyproject {
Some(pyproject) => Ok(parse_pyproject_toml(pyproject)?
.tool
.and_then(|tool| tool.ruff)
.unwrap_or_default()),
None => {
if !quiet {
eprintln!("No pyproject.toml found.");
eprintln!("Falling back to default configuration...");
}
debug!("No pyproject.toml found.");
debug!("Falling back to default configuration...");
Ok(Default::default())
}
}
@@ -94,12 +93,12 @@ mod tests {
use anyhow::Result;
use crate::checks_gen::CheckCodePrefix;
use crate::flake8_quotes;
use crate::flake8_quotes::settings::Quote;
use crate::settings::pyproject::{
find_project_root, find_pyproject_toml, parse_pyproject_toml, Options, Pyproject, Tools,
};
use crate::settings::types::StrCheckCodePair;
use crate::{flake8_quotes, pep8_naming};
#[test]
fn deserialize() -> Result<()> {
@@ -133,7 +132,8 @@ mod tests {
per_file_ignores: vec![],
dummy_variable_rgx: None,
target_version: None,
flake8_quotes: None
flake8_quotes: None,
pep8_naming: None,
})
})
);
@@ -159,7 +159,8 @@ line-length = 79
per_file_ignores: vec![],
dummy_variable_rgx: None,
target_version: None,
flake8_quotes: None
flake8_quotes: None,
pep8_naming: None,
})
})
);
@@ -185,7 +186,8 @@ exclude = ["foo.py"]
per_file_ignores: vec![],
dummy_variable_rgx: None,
target_version: None,
flake8_quotes: None
flake8_quotes: None,
pep8_naming: None,
})
})
);
@@ -211,7 +213,8 @@ select = ["E501"]
per_file_ignores: vec![],
dummy_variable_rgx: None,
target_version: None,
flake8_quotes: None
flake8_quotes: None,
pep8_naming: None,
})
})
);
@@ -238,7 +241,8 @@ ignore = ["E501"]
per_file_ignores: vec![],
dummy_variable_rgx: None,
target_version: None,
flake8_quotes: None
flake8_quotes: None,
pep8_naming: None,
})
})
);
@@ -316,7 +320,25 @@ other-attribute = 1
multiline_quotes: Some(Quote::Double),
docstring_quotes: Some(Quote::Double),
avoid_escape: Some(true),
})
}),
pep8_naming: Some(pep8_naming::settings::Options {
ignore_names: Some(vec![
"setUp".to_string(),
"tearDown".to_string(),
"setUpClass".to_string(),
"tearDownClass".to_string(),
"setUpModule".to_string(),
"tearDownModule".to_string(),
"asyncSetUp".to_string(),
"asyncTearDown".to_string(),
"setUpTestData".to_string(),
"failureException".to_string(),
"longMessage".to_string(),
"maxDiff".to_string(),
]),
classmethod_decorators: Some(vec!["classmethod".to_string()]),
staticmethod_decorators: Some(vec!["staticmethod".to_string()]),
}),
}
);

View File

@@ -6,7 +6,7 @@ use regex::Regex;
use crate::checks_gen::CheckCodePrefix;
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
use crate::{flake8_quotes, Configuration};
use crate::{flake8_quotes, pep8_naming, Configuration};
/// Struct to render user-facing exclusion patterns.
#[derive(Debug)]
@@ -46,6 +46,7 @@ pub struct UserConfiguration {
pub target_version: PythonVersion,
// Plugins
pub flake8_quotes: flake8_quotes::settings::Settings,
pub pep8_naming: pep8_naming::settings::Settings,
// Non-settings exposed to the user
pub project_root: Option<PathBuf>,
pub pyproject: Option<PathBuf>,
@@ -53,30 +54,31 @@ pub struct UserConfiguration {
impl UserConfiguration {
pub fn from_configuration(
settings: Configuration,
configuration: Configuration,
project_root: Option<PathBuf>,
pyproject: Option<PathBuf>,
) -> Self {
Self {
dummy_variable_rgx: settings.dummy_variable_rgx,
exclude: settings
dummy_variable_rgx: configuration.dummy_variable_rgx,
exclude: configuration
.exclude
.into_iter()
.map(Exclusion::from_file_pattern)
.collect(),
extend_exclude: settings
extend_exclude: configuration
.extend_exclude
.into_iter()
.map(Exclusion::from_file_pattern)
.collect(),
extend_ignore: settings.extend_ignore,
extend_select: settings.extend_select,
ignore: settings.ignore,
line_length: settings.line_length,
per_file_ignores: settings.per_file_ignores,
select: settings.select,
target_version: settings.target_version,
flake8_quotes: settings.flake8_quotes,
extend_ignore: configuration.extend_ignore,
extend_select: configuration.extend_select,
ignore: configuration.ignore,
line_length: configuration.line_length,
per_file_ignores: configuration.per_file_ignores,
select: configuration.select,
target_version: configuration.target_version,
flake8_quotes: configuration.flake8_quotes,
pep8_naming: configuration.pep8_naming,
project_root,
pyproject,
}

View File

@@ -6,162 +6,162 @@ expression: checks
BuiltinVariableShadowing: sum
location:
row: 1
column: 1
column: 0
end_location:
row: 1
column: 19
column: 18
fix: ~
- kind:
BuiltinVariableShadowing: int
location:
row: 2
column: 1
column: 0
end_location:
row: 2
column: 30
column: 29
fix: ~
- kind:
BuiltinVariableShadowing: print
location:
row: 4
column: 1
column: 0
end_location:
row: 4
column: 6
column: 5
fix: ~
- kind:
BuiltinVariableShadowing: copyright
location:
row: 5
column: 1
column: 0
end_location:
row: 5
column: 10
column: 9
fix: ~
- kind:
BuiltinVariableShadowing: complex
location:
row: 6
column: 2
column: 1
end_location:
row: 6
column: 14
column: 13
fix: ~
- kind:
BuiltinVariableShadowing: float
location:
row: 7
column: 1
column: 0
end_location:
row: 7
column: 6
column: 5
fix: ~
- kind:
BuiltinVariableShadowing: object
location:
row: 7
column: 9
column: 8
end_location:
row: 7
column: 15
column: 14
fix: ~
- kind:
BuiltinVariableShadowing: min
location:
row: 8
column: 1
column: 0
end_location:
row: 8
column: 4
column: 3
fix: ~
- kind:
BuiltinVariableShadowing: max
location:
row: 8
column: 6
column: 5
end_location:
row: 8
column: 9
column: 8
fix: ~
- kind:
BuiltinVariableShadowing: bytes
location:
row: 10
column: 1
column: 0
end_location:
row: 13
column: 1
column: 0
fix: ~
- kind:
BuiltinVariableShadowing: slice
location:
row: 13
column: 1
column: 0
end_location:
row: 16
column: 1
column: 0
fix: ~
- kind:
BuiltinVariableShadowing: ValueError
location:
row: 18
column: 1
column: 0
end_location:
row: 21
column: 1
column: 0
fix: ~
- kind:
BuiltinVariableShadowing: memoryview
location:
row: 21
column: 5
column: 4
end_location:
row: 21
column: 15
column: 14
fix: ~
- kind:
BuiltinVariableShadowing: bytearray
location:
row: 21
column: 18
column: 17
end_location:
row: 21
column: 27
column: 26
fix: ~
- kind:
BuiltinVariableShadowing: str
location:
row: 24
column: 22
column: 21
end_location:
row: 24
column: 25
column: 24
fix: ~
- kind:
BuiltinVariableShadowing: all
location:
row: 24
column: 45
column: 44
end_location:
row: 24
column: 48
column: 47
fix: ~
- kind:
BuiltinVariableShadowing: any
location:
row: 24
column: 50
column: 49
end_location:
row: 24
column: 53
column: 52
fix: ~
- kind:
BuiltinVariableShadowing: sum
location:
row: 27
column: 8
column: 7
end_location:
row: 27
column: 11
column: 10
fix: ~

View File

@@ -6,63 +6,63 @@ expression: checks
BuiltinArgumentShadowing: str
location:
row: 1
column: 11
column: 10
end_location:
row: 1
column: 14
column: 13
fix: ~
- kind:
BuiltinArgumentShadowing: type
location:
row: 1
column: 19
column: 18
end_location:
row: 1
column: 23
column: 22
fix: ~
- kind:
BuiltinArgumentShadowing: complex
location:
row: 1
column: 26
column: 25
end_location:
row: 1
column: 33
column: 32
fix: ~
- kind:
BuiltinArgumentShadowing: Exception
location:
row: 1
column: 35
column: 34
end_location:
row: 1
column: 44
column: 43
fix: ~
- kind:
BuiltinArgumentShadowing: getattr
location:
row: 1
column: 48
column: 47
end_location:
row: 1
column: 55
column: 54
fix: ~
- kind:
BuiltinArgumentShadowing: bytes
location:
row: 5
column: 17
column: 16
end_location:
row: 5
column: 22
column: 21
fix: ~
- kind:
BuiltinArgumentShadowing: float
location:
row: 9
column: 16
column: 15
end_location:
row: 9
column: 21
column: 20
fix: ~

View File

@@ -6,18 +6,18 @@ expression: checks
BuiltinAttributeShadowing: ImportError
location:
row: 2
column: 5
column: 4
end_location:
row: 2
column: 16
column: 15
fix: ~
- kind:
BuiltinAttributeShadowing: str
location:
row: 7
column: 5
column: 4
end_location:
row: 9
column: 1
column: 0
fix: ~

View File

@@ -5,17 +5,17 @@ expression: checks
- kind: UnaryPrefixIncrement
location:
row: 15
column: 9
column: 8
end_location:
row: 15
column: 12
column: 11
fix: ~
- kind: UnaryPrefixIncrement
location:
row: 20
column: 12
column: 11
end_location:
row: 20
column: 15
column: 14
fix: ~

View File

@@ -0,0 +1,93 @@
---
source: src/linter.rs
expression: checks
---
- kind: MutableArgumentDefault
location:
row: 60
column: 24
end_location:
row: 60
column: 33
fix: ~
- kind: MutableArgumentDefault
location:
row: 64
column: 29
end_location:
row: 64
column: 31
fix: ~
- kind: MutableArgumentDefault
location:
row: 68
column: 19
end_location:
row: 68
column: 24
fix: ~
- kind: MutableArgumentDefault
location:
row: 72
column: 19
end_location:
row: 72
column: 44
fix: ~
- kind: MutableArgumentDefault
location:
row: 76
column: 31
end_location:
row: 76
column: 56
fix: ~
- kind: MutableArgumentDefault
location:
row: 80
column: 25
end_location:
row: 80
column: 44
fix: ~
- kind: MutableArgumentDefault
location:
row: 85
column: 45
end_location:
row: 85
column: 69
fix: ~
- kind: MutableArgumentDefault
location:
row: 89
column: 45
end_location:
row: 89
column: 72
fix: ~
- kind: MutableArgumentDefault
location:
row: 93
column: 44
end_location:
row: 93
column: 68
fix: ~
- kind: MutableArgumentDefault
location:
row: 97
column: 32
end_location:
row: 97
column: 34
fix: ~
- kind: MutableArgumentDefault
location:
row: 170
column: 19
end_location:
row: 170
column: 48
fix: ~

View File

@@ -6,72 +6,72 @@ expression: checks
UnusedLoopControlVariable: i
location:
row: 6
column: 5
column: 4
end_location:
row: 6
column: 6
column: 5
fix:
patch:
content: _i
location:
row: 6
column: 5
column: 4
end_location:
row: 6
column: 6
column: 5
applied: false
- kind:
UnusedLoopControlVariable: k
location:
row: 18
column: 13
column: 12
end_location:
row: 18
column: 14
column: 13
fix:
patch:
content: _k
location:
row: 18
column: 13
column: 12
end_location:
row: 18
column: 14
column: 13
applied: false
- kind:
UnusedLoopControlVariable: i
location:
row: 30
column: 5
column: 4
end_location:
row: 30
column: 6
column: 5
fix:
patch:
content: _i
location:
row: 30
column: 5
column: 4
end_location:
row: 30
column: 6
column: 5
applied: false
- kind:
UnusedLoopControlVariable: k
location:
row: 30
column: 13
column: 12
end_location:
row: 30
column: 14
column: 13
fix:
patch:
content: _k
location:
row: 30
column: 13
column: 12
end_location:
row: 30
column: 14
column: 13
applied: false

View File

@@ -5,35 +5,35 @@ expression: checks
- kind: DoNotAssertFalse
location:
row: 8
column: 8
column: 7
end_location:
row: 8
column: 13
column: 12
fix:
patch:
content: raise AssertionError()
location:
row: 8
column: 1
column: 0
end_location:
row: 8
column: 13
column: 12
applied: false
- kind: DoNotAssertFalse
location:
row: 10
column: 8
column: 7
end_location:
row: 10
column: 13
column: 12
fix:
patch:
content: "raise AssertionError('message')"
location:
row: 10
column: 1
column: 0
end_location:
row: 10
column: 24
column: 23
applied: false

View File

@@ -0,0 +1,14 @@
---
source: src/linter.rs
expression: checks
---
- kind:
RedundantTupleInExceptionHandler: ValueError
location:
row: 3
column: 8
end_location:
row: 3
column: 19
fix: ~

View File

@@ -7,56 +7,56 @@ expression: checks
- OSError
location:
row: 17
column: 9
column: 8
end_location:
row: 17
column: 25
column: 24
fix:
patch:
content: "OSError,"
location:
row: 17
column: 9
column: 8
end_location:
row: 17
column: 25
column: 24
applied: false
- kind:
DuplicateHandlerException:
- MyError
location:
row: 28
column: 9
column: 8
end_location:
row: 28
column: 25
column: 24
fix:
patch:
content: "MyError,"
location:
row: 28
column: 9
column: 8
end_location:
row: 28
column: 25
column: 24
applied: false
- kind:
DuplicateHandlerException:
- re.error
location:
row: 49
column: 9
column: 8
end_location:
row: 49
column: 27
column: 26
fix:
patch:
content: "re.error,"
location:
row: 49
column: 9
column: 8
end_location:
row: 49
column: 27
column: 26
applied: false

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: NoAssertRaisesException
location:
row: 22
column: 9
column: 8
end_location:
row: 25
column: 5
column: 4
fix: ~

View File

@@ -6,36 +6,36 @@ expression: checks
DuplicateTryBlockException: ValueError
location:
row: 15
column: 1
column: 0
end_location:
row: 22
column: 1
column: 0
fix: ~
- kind:
DuplicateTryBlockException: pickle.PickleError
location:
row: 22
column: 1
column: 0
end_location:
row: 31
column: 1
column: 0
fix: ~
- kind:
DuplicateTryBlockException: TypeError
location:
row: 31
column: 1
column: 0
end_location:
row: 39
column: 1
column: 0
fix: ~
- kind:
DuplicateTryBlockException: ValueError
location:
row: 31
column: 1
column: 0
end_location:
row: 39
column: 1
column: 0
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: UnnecessaryGeneratorList
location:
row: 1
column: 5
column: 4
end_location:
row: 1
column: 30
column: 29
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: UnnecessaryGeneratorSet
location:
row: 1
column: 5
column: 4
end_location:
row: 1
column: 29
column: 28
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: UnnecessaryGeneratorDict
location:
row: 1
column: 5
column: 0
end_location:
row: 1
column: 35
column: 30
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: UnnecessaryListComprehensionSet
location:
row: 1
column: 5
column: 4
end_location:
row: 1
column: 31
column: 30
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: UnnecessaryListComprehensionDict
location:
row: 1
column: 5
column: 0
end_location:
row: 1
column: 37
column: 32
fix: ~

View File

@@ -6,36 +6,36 @@ expression: checks
UnnecessaryLiteralSet: list
location:
row: 1
column: 6
column: 5
end_location:
row: 1
column: 17
column: 16
fix: ~
- kind:
UnnecessaryLiteralSet: tuple
location:
row: 2
column: 6
column: 5
end_location:
row: 2
column: 17
column: 16
fix: ~
- kind:
UnnecessaryLiteralSet: list
location:
row: 3
column: 6
column: 5
end_location:
row: 3
column: 13
column: 12
fix: ~
- kind:
UnnecessaryLiteralSet: tuple
location:
row: 4
column: 6
column: 5
end_location:
row: 4
column: 13
column: 12
fix: ~

View File

@@ -6,36 +6,36 @@ expression: checks
UnnecessaryLiteralDict: list
location:
row: 1
column: 6
column: 5
end_location:
row: 1
column: 20
column: 19
fix: ~
- kind:
UnnecessaryLiteralDict: tuple
location:
row: 2
column: 6
column: 5
end_location:
row: 2
column: 21
column: 20
fix: ~
- kind:
UnnecessaryLiteralDict: list
location:
row: 3
column: 6
column: 5
end_location:
row: 3
column: 14
column: 13
fix: ~
- kind:
UnnecessaryLiteralDict: tuple
location:
row: 4
column: 6
column: 5
end_location:
row: 4
column: 14
column: 13
fix: ~

View File

@@ -6,36 +6,36 @@ expression: checks
UnnecessaryCollectionCall: tuple
location:
row: 1
column: 5
column: 4
end_location:
row: 1
column: 12
column: 11
fix: ~
- kind:
UnnecessaryCollectionCall: list
location:
row: 2
column: 5
column: 4
end_location:
row: 2
column: 10
fix: ~
- kind:
UnnecessaryCollectionCall: dict
location:
row: 3
column: 5
end_location:
row: 3
column: 11
fix: ~
- kind:
UnnecessaryCollectionCall: dict
location:
row: 3
column: 6
end_location:
row: 3
column: 12
fix: ~
- kind:
UnnecessaryCollectionCall: dict
location:
row: 4
column: 6
column: 5
end_location:
row: 4
column: 15
column: 14
fix: ~

View File

@@ -6,27 +6,27 @@ expression: checks
UnnecessaryLiteralWithinTupleCall: list
location:
row: 1
column: 6
column: 5
end_location:
row: 1
column: 19
column: 18
fix: ~
- kind:
UnnecessaryLiteralWithinTupleCall: tuple
location:
row: 2
column: 6
column: 5
end_location:
row: 2
column: 19
column: 18
fix: ~
- kind:
UnnecessaryLiteralWithinTupleCall: list
location:
row: 3
column: 6
column: 5
end_location:
row: 3
column: 15
column: 14
fix: ~

View File

@@ -6,36 +6,36 @@ expression: checks
UnnecessaryLiteralWithinListCall: list
location:
row: 1
column: 6
column: 5
end_location:
row: 1
column: 18
column: 17
fix: ~
- kind:
UnnecessaryLiteralWithinListCall: tuple
location:
row: 2
column: 6
column: 5
end_location:
row: 2
column: 18
column: 17
fix: ~
- kind:
UnnecessaryLiteralWithinListCall: list
location:
row: 3
column: 6
column: 5
end_location:
row: 3
column: 14
column: 13
fix: ~
- kind:
UnnecessaryLiteralWithinListCall: tuple
location:
row: 4
column: 6
column: 5
end_location:
row: 4
column: 14
column: 13
fix: ~

View File

@@ -5,9 +5,9 @@ expression: checks
- kind: UnnecessaryListCall
location:
row: 2
column: 1
column: 0
end_location:
row: 2
column: 21
column: 20
fix: ~

View File

@@ -6,27 +6,27 @@ expression: checks
UnnecessaryCallAroundSorted: list
location:
row: 2
column: 1
column: 0
end_location:
row: 2
column: 16
column: 15
fix: ~
- kind:
UnnecessaryCallAroundSorted: reversed
location:
row: 3
column: 1
column: 0
end_location:
row: 3
column: 20
column: 19
fix: ~
- kind:
UnnecessaryCallAroundSorted: reversed
location:
row: 4
column: 1
column: 0
end_location:
row: 4
column: 34
column: 33
fix: ~

View File

@@ -8,76 +8,76 @@ expression: checks
- list
location:
row: 2
column: 1
column: 0
end_location:
row: 2
column: 14
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- tuple
- list
location:
row: 3
column: 1
end_location:
row: 3
column: 15
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- list
- tuple
location:
row: 4
column: 1
end_location:
row: 4
column: 15
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- tuple
- tuple
location:
row: 5
column: 1
end_location:
row: 5
column: 16
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- set
- set
location:
row: 6
column: 1
end_location:
row: 6
column: 12
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- list
- set
location:
row: 7
column: 1
end_location:
row: 7
column: 13
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- tuple
- list
location:
row: 3
column: 0
end_location:
row: 3
column: 14
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- list
- tuple
location:
row: 4
column: 0
end_location:
row: 4
column: 14
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- tuple
- tuple
location:
row: 5
column: 0
end_location:
row: 5
column: 15
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- set
- set
location:
row: 6
column: 0
end_location:
row: 6
column: 11
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- list
- set
location:
row: 7
column: 0
end_location:
row: 7
column: 12
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
- tuple
- set
location:
row: 8
column: 1
column: 0
end_location:
row: 8
column: 14
column: 13
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
@@ -85,10 +85,10 @@ expression: checks
- set
location:
row: 9
column: 1
column: 0
end_location:
row: 9
column: 15
column: 14
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
@@ -96,10 +96,10 @@ expression: checks
- set
location:
row: 10
column: 1
column: 0
end_location:
row: 10
column: 17
column: 16
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
@@ -107,10 +107,10 @@ expression: checks
- sorted
location:
row: 11
column: 1
column: 0
end_location:
row: 11
column: 16
column: 15
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
@@ -118,10 +118,10 @@ expression: checks
- sorted
location:
row: 12
column: 1
column: 0
end_location:
row: 12
column: 17
column: 16
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
@@ -129,10 +129,10 @@ expression: checks
- sorted
location:
row: 13
column: 1
column: 0
end_location:
row: 13
column: 18
column: 17
fix: ~
- kind:
UnnecessaryDoubleCastOrProcess:
@@ -140,9 +140,9 @@ expression: checks
- sorted
location:
row: 14
column: 1
column: 0
end_location:
row: 14
column: 20
column: 19
fix: ~

Some files were not shown because too many files have changed in this diff Show More