Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4be09b45ea | ||
|
|
13e8ed0a0a | ||
|
|
4161d4ae32 | ||
|
|
99f7854d8c | ||
|
|
a580d1a858 | ||
|
|
86806a9e39 | ||
|
|
89afc9db74 | ||
|
|
0f34cdb7a3 | ||
|
|
437b6f23b9 | ||
|
|
0fe2b15676 | ||
|
|
e81efa5a3d |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.127
|
||||
rev: v0.0.129
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -49,6 +49,16 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7021ce4924a3f25f802b2cccd1af585e39ea1a363a1aa2e72afe54b67a3a7a7"
|
||||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"yansi-term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.66"
|
||||
@@ -417,7 +427,7 @@ version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5b5db619f3556839cb2223ae86ff3f9a09da2c5013be42bc9af08c9589bf70c"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"annotate-snippets 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -930,7 +940,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.127-dev.0"
|
||||
version = "0.0.129-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -2238,8 +2248,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.127"
|
||||
version = "0.0.129"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
"atty",
|
||||
@@ -2287,7 +2298,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.127"
|
||||
version = "0.0.129"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -3312,3 +3323,12 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi-term"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@@ -6,13 +6,14 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.127"
|
||||
version = "0.0.129"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "ruff"
|
||||
|
||||
[dependencies]
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { version = "1.0.66" }
|
||||
atty = { version = "0.2.14" }
|
||||
bincode = { version = "1.3.3" }
|
||||
|
||||
34
README.md
34
README.md
@@ -122,7 +122,7 @@ default configuration is equivalent to:
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
# Enable Flake's "E" and "F" codes by default.
|
||||
# Enable Pyflakes `E` and `F` codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = []
|
||||
|
||||
@@ -157,18 +157,24 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
target-version = "py310"
|
||||
```
|
||||
|
||||
As an example, the following would configure Ruff to (1) avoid checking for line-length
|
||||
violations (`E501`) and (2) ignore unused import rules in `__init__.py` files:
|
||||
As an example, the following would configure Ruff to: (1) avoid checking for line-length
|
||||
violations (`E501`); (2), always autofix, but never remove unused imports (`F401`); and (3) ignore
|
||||
import-at-top-of-file errors (`E402`) in `__init__.py` files:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Enable Pyflakes and pycodestyle rules.
|
||||
select = ["E", "F"]
|
||||
|
||||
# Never enforce `E501`.
|
||||
# Never enforce `E501` (line length violations).
|
||||
ignore = ["E501"]
|
||||
|
||||
# Ignore `F401` violations in any `__init__.py` file, and in `path/to/file.py`.
|
||||
per-file-ignores = {"__init__.py" = ["F401"], "path/to/file.py" = ["F401"]}
|
||||
# Always autofix, but never try to fix `F401` (unused imports).
|
||||
fix = true
|
||||
unfixable = ["F401"]
|
||||
|
||||
# Ignore `E402` (import violations in any `__init__.py` file, and in `path/to/file.py`.
|
||||
per-file-ignores = {"__init__.py" = ["E402"], "path/to/file.py" = ["E402"]}
|
||||
```
|
||||
|
||||
Plugin configurations should be expressed as subsections, e.g.:
|
||||
@@ -191,7 +197,7 @@ ruff path/to/code/ --select F401 --select F403
|
||||
See `ruff --help` for more:
|
||||
|
||||
```shell
|
||||
ruff: An extremely fast Python linter.
|
||||
Ruff: An extremely fast Python linter.
|
||||
|
||||
Usage: ruff [OPTIONS] <FILES>...
|
||||
|
||||
@@ -227,14 +233,20 @@ Options:
|
||||
List of paths, used to exclude files and/or directories from checks
|
||||
--extend-exclude <EXTEND_EXCLUDE>
|
||||
Like --exclude, but adds additional files and directories on top of the excluded ones
|
||||
--fixable <FIXABLE>
|
||||
List of error codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
--unfixable <UNFIXABLE>
|
||||
List of error codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
|
||||
--per-file-ignores <PER_FILE_IGNORES>
|
||||
List of mappings from file pattern to code to exclude
|
||||
--format <FORMAT>
|
||||
Output serialization format for error messages [default: text] [possible values: text, json]
|
||||
--show-source
|
||||
Show violations with source code
|
||||
--show-files
|
||||
See the files ruff will be run against with the current settings
|
||||
See the files Ruff will be run against with the current settings
|
||||
--show-settings
|
||||
See ruff's settings
|
||||
See Ruff's settings
|
||||
--add-noqa
|
||||
Enable automatic additions of noqa directives to failing lines
|
||||
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
|
||||
@@ -244,7 +256,7 @@ Options:
|
||||
--line-length <LINE_LENGTH>
|
||||
Set the line-length for length-associated checks and automatic formatting
|
||||
--max-complexity <MAX_COMPLEXITY>
|
||||
Set the maximum cyclomatic complexity for complexity-associated checks
|
||||
Max McCabe complexity allowed for a function
|
||||
--stdin-filename <STDIN_FILENAME>
|
||||
The name of the file when passing it through stdin
|
||||
-h, --help
|
||||
@@ -368,7 +380,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
|
||||
| E714 | NotIsTest | Test for object identity should be `is not` | 🛠 |
|
||||
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
|
||||
| E722 | DoNotUseBareExcept | Do not use bare `except` | |
|
||||
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | |
|
||||
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | 🛠 |
|
||||
| E741 | AmbiguousVariableName | Ambiguous variable name: `...` | |
|
||||
| E742 | AmbiguousClassName | Ambiguous class name: `...` | |
|
||||
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.127"
|
||||
version = "0.0.129"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.127"
|
||||
version = "0.0.129"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.127-dev.0"
|
||||
version = "0.0.129-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -243,22 +243,25 @@ mod tests {
|
||||
fn it_converts_empty() -> Result<()> {
|
||||
let actual = convert(&HashMap::from([]), None)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -279,22 +282,25 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(100),
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: Some(100),
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -315,22 +321,25 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: Some(100),
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: Some(100),
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -351,22 +360,25 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -387,22 +399,25 @@ mod tests {
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
@@ -431,11 +446,16 @@ mod tests {
|
||||
Some(vec![Plugin::Flake8Docstrings]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::D100,
|
||||
CheckCodePrefix::D101,
|
||||
@@ -476,12 +496,10 @@ mod tests {
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -502,23 +520,26 @@ mod tests {
|
||||
None,
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
line_length: None,
|
||||
src: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
select: Some(vec![
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::Q,
|
||||
CheckCodePrefix::W,
|
||||
]),
|
||||
extend_select: None,
|
||||
ignore: Some(vec![]),
|
||||
extend_ignore: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
|
||||
20
resources/test/fixtures/B006_B008.py
vendored
20
resources/test/fixtures/B006_B008.py
vendored
@@ -185,3 +185,23 @@ def nested_b008(a=random.randint(0, dt.datetime.now().year)):
|
||||
# Ignore lambda contents since they are evaluated at call time.
|
||||
def foo(f=lambda x: print(x)):
|
||||
f(1)
|
||||
|
||||
|
||||
from collections import abc
|
||||
from typing import Annotated, Dict, Optional, Sequence, Union, Set
|
||||
|
||||
|
||||
def immutable_annotations(
|
||||
a: Sequence[int] | None = [],
|
||||
b: Optional[abc.Mapping[int, int]] = {},
|
||||
c: Annotated[Union[abc.Set[str], abc.Sized], "annotation"] = set(),
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def mutable_annotations(
|
||||
a: list[int] | None = [],
|
||||
b: Optional[Dict[int, int]] = {},
|
||||
c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
):
|
||||
pass
|
||||
|
||||
17
resources/test/fixtures/F841.py
vendored
17
resources/test/fixtures/F841.py
vendored
@@ -35,3 +35,20 @@ def f4():
|
||||
_ = 1
|
||||
__ = 1
|
||||
_discarded = 1
|
||||
|
||||
|
||||
a = 1
|
||||
|
||||
|
||||
def f5():
|
||||
global a
|
||||
|
||||
# Used in `f7` via `nonlocal`.
|
||||
b = 1
|
||||
|
||||
def f6():
|
||||
# F841
|
||||
b = 1
|
||||
|
||||
def f7():
|
||||
nonlocal b
|
||||
|
||||
8
resources/test/fixtures/M001.py
vendored
8
resources/test/fixtures/M001.py
vendored
@@ -18,6 +18,14 @@ def f() -> None:
|
||||
# Invalid (and unimplemented)
|
||||
d = 1 # noqa: F841, W191
|
||||
|
||||
# fmt: off
|
||||
# Invalid - no space before #
|
||||
d = 1# noqa: E501
|
||||
|
||||
# Invalid - many spaces before #
|
||||
d = 1 # noqa: E501
|
||||
# fmt: on
|
||||
|
||||
|
||||
# Valid
|
||||
_ = """Lorem ipsum dolor sit amet.
|
||||
|
||||
4
resources/test/fixtures/U013.py
vendored
4
resources/test/fixtures/U013.py
vendored
@@ -1,4 +1,5 @@
|
||||
from typing import TypedDict, NotRequired, Literal
|
||||
import typing
|
||||
|
||||
# dict literal
|
||||
MyType1 = TypedDict("MyType1", {"a": int, "b": str})
|
||||
@@ -27,3 +28,6 @@ MyType9 = TypedDict("MyType9", {"in": int, "x-y": int})
|
||||
|
||||
# using Literal type
|
||||
MyType10 = TypedDict("MyType10", {"key": Literal["value"]})
|
||||
|
||||
# using namespace TypedDict
|
||||
MyType11 = typing.TypedDict("MyType11", {"key": int})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.127"
|
||||
version = "0.0.129"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, StmtKind};
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::SourceCodeLocator;
|
||||
|
||||
#[inline(always)]
|
||||
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
|
||||
@@ -261,6 +264,34 @@ pub fn to_absolute(relative: &Location, base: &Location) -> Location {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if a `Stmt` has leading content.
|
||||
pub fn match_leading_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
|
||||
let range = Range {
|
||||
location: Location::new(stmt.location.row(), 0),
|
||||
end_location: stmt.location,
|
||||
};
|
||||
let prefix = locator.slice_source_code_range(&range);
|
||||
prefix.chars().any(|char| !char.is_whitespace())
|
||||
}
|
||||
|
||||
/// Return `true` if a `Stmt` has trailing content.
|
||||
pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
|
||||
let range = Range {
|
||||
location: stmt.end_location.unwrap(),
|
||||
end_location: Location::new(stmt.end_location.unwrap().row() + 1, 0),
|
||||
};
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
for char in suffix.chars() {
|
||||
if char == '#' {
|
||||
return false;
|
||||
}
|
||||
if !char.is_whitespace() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod operations;
|
||||
pub mod relocate;
|
||||
pub mod types;
|
||||
pub mod visitor;
|
||||
pub mod whitespace;
|
||||
|
||||
@@ -83,6 +83,7 @@ pub enum BindingKind {
|
||||
Binding,
|
||||
LoopVar,
|
||||
Global,
|
||||
Nonlocal,
|
||||
Builtin,
|
||||
ClassDefinition,
|
||||
Definition,
|
||||
|
||||
@@ -3,12 +3,6 @@ use rustpython_ast::{Located, Location};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
|
||||
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
|
||||
];
|
||||
|
||||
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];
|
||||
|
||||
/// Extract the leading words from a line of text.
|
||||
pub fn leading_words(line: &str) -> String {
|
||||
line.trim()
|
||||
109
src/check_ast.rs
109
src/check_ast.rs
@@ -152,10 +152,10 @@ impl<'a> Checker<'a> {
|
||||
|
||||
/// Return `true` if a patch should be generated under the given autofix
|
||||
/// `Mode`.
|
||||
pub fn patch(&self) -> bool {
|
||||
pub fn patch(&self, code: &CheckCode) -> bool {
|
||||
// TODO(charlie): We can't fix errors in f-strings until RustPython adds
|
||||
// location data.
|
||||
self.autofix.patch() && self.in_f_string.is_none()
|
||||
self.autofix.patch() && self.in_f_string.is_none() && self.settings.fixable.contains(code)
|
||||
}
|
||||
|
||||
/// Return `true` if the `Expr` is a reference to `typing.${target}`.
|
||||
@@ -213,21 +213,74 @@ where
|
||||
|
||||
// Pre-visit.
|
||||
match &stmt.node {
|
||||
StmtKind::Global { names } | StmtKind::Nonlocal { names } => {
|
||||
let global_scope_id = self.scopes[GLOBAL_SCOPE_INDEX].id;
|
||||
let scope =
|
||||
&mut self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
|
||||
if scope.id != global_scope_id {
|
||||
StmtKind::Global { names } => {
|
||||
let scope_index = *self.scope_stack.last().expect("No current scope found.");
|
||||
if scope_index != GLOBAL_SCOPE_INDEX {
|
||||
let scope = &mut self.scopes[scope_index];
|
||||
let usage = Some((scope.id, Range::from_located(stmt)));
|
||||
for name in names {
|
||||
// Add a binding to the current scope.
|
||||
scope.values.insert(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::Global,
|
||||
used: Some((global_scope_id, Range::from_located(stmt))),
|
||||
used: usage,
|
||||
range: Range::from_located(stmt),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Mark the binding in the global scope as used.
|
||||
for name in names {
|
||||
if let Some(mut existing) = self.scopes[GLOBAL_SCOPE_INDEX]
|
||||
.values
|
||||
.get_mut(&name.as_str())
|
||||
{
|
||||
existing.used = usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::E741) {
|
||||
let location = Range::from_located(stmt);
|
||||
self.add_checks(
|
||||
names
|
||||
.iter()
|
||||
.filter_map(|name| {
|
||||
pycodestyle::checks::ambiguous_variable_name(name, location)
|
||||
})
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
StmtKind::Nonlocal { names } => {
|
||||
let scope_index = *self.scope_stack.last().expect("No current scope found.");
|
||||
if scope_index != GLOBAL_SCOPE_INDEX {
|
||||
let scope = &mut self.scopes[scope_index];
|
||||
let usage = Some((scope.id, Range::from_located(stmt)));
|
||||
for name in names {
|
||||
// Add a binding to the current scope.
|
||||
scope.values.insert(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::Global,
|
||||
used: usage,
|
||||
range: Range::from_located(stmt),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Mark the binding in the defining scopes as used too. (Skip the global scope
|
||||
// and the current scope.)
|
||||
for name in names {
|
||||
for index in self.scope_stack.iter().skip(1).rev().skip(1) {
|
||||
if let Some(mut existing) =
|
||||
self.scopes[*index].values.get_mut(&name.as_str())
|
||||
{
|
||||
existing.used = usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::E741) {
|
||||
@@ -928,11 +981,7 @@ where
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::E731) {
|
||||
if let [target] = &targets[..] {
|
||||
if let Some(check) =
|
||||
pycodestyle::checks::do_not_assign_lambda(target, value, stmt)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
pycodestyle::plugins::do_not_assign_lambda(self, target, value, stmt)
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::U001) {
|
||||
@@ -957,11 +1006,7 @@ where
|
||||
StmtKind::AnnAssign { target, value, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::E731) {
|
||||
if let Some(value) = value {
|
||||
if let Some(check) =
|
||||
pycodestyle::checks::do_not_assign_lambda(target, value, stmt)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
pycodestyle::plugins::do_not_assign_lambda(self, target, value, stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1256,7 +1301,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C400),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1270,7 +1315,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C401),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1284,7 +1329,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C402),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1299,7 +1344,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C403),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1315,7 +1360,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C404),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1330,7 +1375,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C405),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1344,7 +1389,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C406),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1358,7 +1403,7 @@ where
|
||||
args,
|
||||
keywords,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C408),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1372,7 +1417,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C409),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1387,7 +1432,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C410),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1401,7 +1446,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C411),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -1415,7 +1460,7 @@ where
|
||||
func,
|
||||
args,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C413),
|
||||
Range::from_located(expr),
|
||||
)
|
||||
{
|
||||
@@ -1667,7 +1712,7 @@ where
|
||||
elt,
|
||||
generators,
|
||||
self.locator,
|
||||
self.patch(),
|
||||
self.patch(&CheckCode::C416),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
@@ -2628,7 +2673,7 @@ impl<'a> Checker<'a> {
|
||||
let child = self.parents[defined_by];
|
||||
let parent = defined_in.map(|defined_in| self.parents[defined_in]);
|
||||
|
||||
let fix = if self.patch() {
|
||||
let fix = if self.patch(&CheckCode::F401) {
|
||||
let deleted: Vec<&Stmt> = self
|
||||
.deletions
|
||||
.iter()
|
||||
|
||||
@@ -73,7 +73,7 @@ pub fn check_lines(
|
||||
end_location: Location::new(lineno + 1, line_length + 1),
|
||||
},
|
||||
);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(lineno + 1, 0),
|
||||
Location::new(lineno + 1, line_length + 1),
|
||||
@@ -103,7 +103,7 @@ pub fn check_lines(
|
||||
matches.push(check.kind.code().as_ref());
|
||||
ignored.push(index)
|
||||
}
|
||||
(Directive::Codes(_, _, codes), matches) => {
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
ignored.push(index);
|
||||
@@ -133,7 +133,7 @@ pub fn check_lines(
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
}
|
||||
(Directive::Codes(_, _, codes), matches) => {
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
} else {
|
||||
@@ -170,7 +170,7 @@ pub fn check_lines(
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
}
|
||||
(Directive::Codes(_, _, codes), matches) => {
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
} else {
|
||||
@@ -186,7 +186,7 @@ pub fn check_lines(
|
||||
if enforce_noqa {
|
||||
for (row, (directive, matches)) in noqa_directives {
|
||||
match directive {
|
||||
Directive::All(start, end) => {
|
||||
Directive::All(spaces, start, end) => {
|
||||
if matches.is_empty() {
|
||||
let mut check = Check::new(
|
||||
CheckKind::UnusedNOQA(None),
|
||||
@@ -195,16 +195,16 @@ pub fn check_lines(
|
||||
end_location: Location::new(row + 1, end),
|
||||
},
|
||||
);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, start - spaces),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
}
|
||||
line_checks.push(check);
|
||||
}
|
||||
}
|
||||
Directive::Codes(start, end, codes) => {
|
||||
Directive::Codes(spaces, start, end, codes) => {
|
||||
let mut invalid_codes = vec![];
|
||||
let mut valid_codes = vec![];
|
||||
for code in codes {
|
||||
@@ -223,15 +223,15 @@ pub fn check_lines(
|
||||
end_location: Location::new(row + 1, end),
|
||||
},
|
||||
);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
if valid_codes.is_empty() {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, start - spaces),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
} else {
|
||||
check.amend(Fix::replacement(
|
||||
format!(" # noqa: {}", valid_codes.join(", ")),
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
|
||||
@@ -36,7 +36,7 @@ pub fn check_tokens(
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
if matches!(tok, Tok::String { .. } | Tok::Comment) {
|
||||
for check in rules::checks::ambiguous_unicode_character(
|
||||
checks.extend(rules::checks::ambiguous_unicode_character(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
@@ -49,12 +49,9 @@ pub fn check_tokens(
|
||||
} else {
|
||||
Context::Comment
|
||||
},
|
||||
autofix.patch(),
|
||||
) {
|
||||
if settings.enabled.contains(check.kind.code()) {
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
settings,
|
||||
autofix,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2038,46 +2038,51 @@ impl CheckKind {
|
||||
pub fn fixable(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
CheckKind::AmbiguousUnicodeCharacterString(_, _)
|
||||
| CheckKind::AmbiguousUnicodeCharacterDocstring(_, _)
|
||||
| CheckKind::BlankLineAfterLastSection(_)
|
||||
| CheckKind::BlankLineAfterSection(_)
|
||||
CheckKind::AmbiguousUnicodeCharacterString(..)
|
||||
| CheckKind::AmbiguousUnicodeCharacterDocstring(..)
|
||||
| CheckKind::BlankLineAfterLastSection(..)
|
||||
| CheckKind::BlankLineAfterSection(..)
|
||||
| CheckKind::BlankLineAfterSummary
|
||||
| CheckKind::BlankLineBeforeSection(_)
|
||||
| CheckKind::CapitalizeSectionName(_)
|
||||
| CheckKind::DashedUnderlineAfterSection(_)
|
||||
| CheckKind::DeprecatedUnittestAlias(_, _)
|
||||
| CheckKind::BlankLineBeforeSection(..)
|
||||
| CheckKind::CapitalizeSectionName(..)
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass
|
||||
| CheckKind::DashedUnderlineAfterSection(..)
|
||||
| CheckKind::DeprecatedUnittestAlias(..)
|
||||
| CheckKind::DoNotAssertFalse
|
||||
| CheckKind::DuplicateHandlerException(_)
|
||||
| CheckKind::DoNotAssignLambda
|
||||
| CheckKind::DuplicateHandlerException(..)
|
||||
| CheckKind::GetAttrWithConstant
|
||||
| CheckKind::IsLiteral
|
||||
| CheckKind::NewLineAfterLastParagraph
|
||||
| CheckKind::NewLineAfterSectionName(_)
|
||||
| CheckKind::NoBlankLineAfterFunction(_)
|
||||
| CheckKind::NoBlankLineBeforeClass(_)
|
||||
| CheckKind::NoBlankLineBeforeFunction(_)
|
||||
| CheckKind::NoBlankLinesBetweenHeaderAndContent(_)
|
||||
| CheckKind::NewLineAfterSectionName(..)
|
||||
| CheckKind::NoBlankLineAfterFunction(..)
|
||||
| CheckKind::NoBlankLineBeforeClass(..)
|
||||
| CheckKind::NoBlankLineBeforeFunction(..)
|
||||
| CheckKind::NoBlankLinesBetweenHeaderAndContent(..)
|
||||
| CheckKind::NoOverIndentation
|
||||
| CheckKind::NoSurroundingWhitespace
|
||||
| CheckKind::NoUnderIndentation
|
||||
| CheckKind::OneBlankLineAfterClass(_)
|
||||
| CheckKind::OneBlankLineBeforeClass(_)
|
||||
| CheckKind::NoneComparison(..)
|
||||
| CheckKind::NotInTest
|
||||
| CheckKind::NotIsTest
|
||||
| CheckKind::OneBlankLineAfterClass(..)
|
||||
| CheckKind::OneBlankLineBeforeClass(..)
|
||||
| CheckKind::PEP3120UnnecessaryCodingComment
|
||||
| CheckKind::PPrintFound
|
||||
| CheckKind::PrintFound
|
||||
| CheckKind::RaiseNotImplemented
|
||||
| CheckKind::SectionNameEndsInColon(_)
|
||||
| CheckKind::SectionNotOverIndented(_)
|
||||
| CheckKind::SectionUnderlineAfterName(_)
|
||||
| CheckKind::SectionUnderlineMatchesSectionLength(_)
|
||||
| CheckKind::SectionUnderlineNotOverIndented(_)
|
||||
| CheckKind::SectionNameEndsInColon(..)
|
||||
| CheckKind::SectionNotOverIndented(..)
|
||||
| CheckKind::SectionUnderlineAfterName(..)
|
||||
| CheckKind::SectionUnderlineMatchesSectionLength(..)
|
||||
| CheckKind::SectionUnderlineNotOverIndented(..)
|
||||
| CheckKind::SuperCallWithParameters
|
||||
| CheckKind::TypeOfPrimitive(_)
|
||||
| CheckKind::UnnecessaryCollectionCall(_)
|
||||
| CheckKind::UnnecessaryComprehension(_)
|
||||
| CheckKind::TrueFalseComparison(..)
|
||||
| CheckKind::TypeOfPrimitive(..)
|
||||
| CheckKind::UnnecessaryCollectionCall(..)
|
||||
| CheckKind::UnnecessaryComprehension(..)
|
||||
| CheckKind::UnnecessaryEncodeUTF8
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass
|
||||
| CheckKind::UnnecessaryFutureImport(_)
|
||||
| CheckKind::UnnecessaryFutureImport(..)
|
||||
| CheckKind::UnnecessaryGeneratorDict
|
||||
| CheckKind::UnnecessaryGeneratorList
|
||||
| CheckKind::UnnecessaryGeneratorSet
|
||||
@@ -2085,18 +2090,18 @@ impl CheckKind {
|
||||
| CheckKind::UnnecessaryListCall
|
||||
| CheckKind::UnnecessaryListComprehensionDict
|
||||
| CheckKind::UnnecessaryListComprehensionSet
|
||||
| CheckKind::UnnecessaryLiteralDict(_)
|
||||
| CheckKind::UnnecessaryLiteralSet(_)
|
||||
| CheckKind::UnnecessaryLiteralWithinListCall(_)
|
||||
| CheckKind::UnnecessaryLiteralWithinTupleCall(_)
|
||||
| CheckKind::UnnecessaryLiteralDict(..)
|
||||
| CheckKind::UnnecessaryLiteralSet(..)
|
||||
| CheckKind::UnnecessaryLiteralWithinListCall(..)
|
||||
| CheckKind::UnnecessaryLiteralWithinTupleCall(..)
|
||||
| CheckKind::UnsortedImports
|
||||
| CheckKind::UnusedImport(_, false)
|
||||
| CheckKind::UnusedLoopControlVariable(_)
|
||||
| CheckKind::UnusedNOQA(_)
|
||||
| CheckKind::UsePEP585Annotation(_)
|
||||
| CheckKind::UnusedLoopControlVariable(..)
|
||||
| CheckKind::UnusedNOQA(..)
|
||||
| CheckKind::UsePEP585Annotation(..)
|
||||
| CheckKind::UsePEP604Annotation
|
||||
| CheckKind::UselessMetaclassType
|
||||
| CheckKind::UselessObjectInheritance(_)
|
||||
| CheckKind::UselessObjectInheritance(..)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
78
src/cli.rs
78
src/cli.rs
@@ -1,19 +1,16 @@
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{command, Parser};
|
||||
use fnv::FnvHashMap;
|
||||
use log::warn;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::logging::LogLevel;
|
||||
use crate::printer::SerializationFormat;
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::types::{PatternPrefixPair, PerFileIgnore, PythonVersion};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, about = "ruff: An extremely fast Python linter.")]
|
||||
#[command(author, about = "Ruff: An extremely fast Python linter.")]
|
||||
#[command(version)]
|
||||
pub struct Cli {
|
||||
#[arg(required = true)]
|
||||
@@ -66,16 +63,27 @@ pub struct Cli {
|
||||
/// excluded ones.
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub extend_exclude: Vec<String>,
|
||||
/// List of error codes to treat as eligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub fixable: Vec<CheckCodePrefix>,
|
||||
/// List of error codes to treat as ineligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub unfixable: Vec<CheckCodePrefix>,
|
||||
/// List of mappings from file pattern to code to exclude
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub per_file_ignores: Vec<PatternPrefixPair>,
|
||||
/// Output serialization format for error messages.
|
||||
#[arg(long, value_enum, default_value_t=SerializationFormat::Text)]
|
||||
pub format: SerializationFormat,
|
||||
/// See the files ruff will be run against with the current settings.
|
||||
/// Show violations with source code.
|
||||
#[arg(long)]
|
||||
pub show_source: bool,
|
||||
/// See the files Ruff will be run against with the current settings.
|
||||
#[arg(long)]
|
||||
pub show_files: bool,
|
||||
/// See ruff's settings.
|
||||
/// See Ruff's settings.
|
||||
#[arg(long)]
|
||||
pub show_settings: bool,
|
||||
/// Enable automatic additions of noqa directives to failing lines.
|
||||
@@ -134,64 +142,6 @@ pub fn extract_log_level(cli: &Cli) -> LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Warnable {
|
||||
Select,
|
||||
ExtendSelect,
|
||||
}
|
||||
|
||||
impl fmt::Display for Warnable {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Warnable::Select => fmt.write_str("--select"),
|
||||
Warnable::ExtendSelect => fmt.write_str("--extend-select"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Warn the user if they attempt to enable a code that won't be respected.
|
||||
pub fn warn_on(
|
||||
flag: Warnable,
|
||||
codes: &[CheckCodePrefix],
|
||||
cli_ignore: &[CheckCodePrefix],
|
||||
cli_extend_ignore: &[CheckCodePrefix],
|
||||
pyproject_configuration: &Configuration,
|
||||
pyproject_path: Option<&PathBuf>,
|
||||
) {
|
||||
for code in codes {
|
||||
if !cli_ignore.is_empty() {
|
||||
if cli_ignore.contains(code) {
|
||||
warn!("{code:?} was passed to {flag}, but ignored via --ignore")
|
||||
}
|
||||
} else if pyproject_configuration.ignore.contains(code) {
|
||||
if let Some(path) = pyproject_path {
|
||||
warn!(
|
||||
"{code:?} was passed to {flag}, but ignored by the `ignore` field in {}",
|
||||
path.to_string_lossy()
|
||||
)
|
||||
} else {
|
||||
warn!("{code:?} was passed to {flag}, but ignored by the default `ignore` field",)
|
||||
}
|
||||
}
|
||||
if !cli_extend_ignore.is_empty() {
|
||||
if cli_extend_ignore.contains(code) {
|
||||
warn!("{code:?} was passed to {flag}, but ignored via --extend-ignore")
|
||||
}
|
||||
} else if pyproject_configuration.extend_ignore.contains(code) {
|
||||
if let Some(path) = pyproject_path {
|
||||
warn!(
|
||||
"{code:?} was passed to {flag}, but ignored by the `extend_ignore` field in {}",
|
||||
path.to_string_lossy()
|
||||
)
|
||||
} else {
|
||||
warn!(
|
||||
"{code:?} was passed to {flag}, but ignored by the default `extend_ignore` \
|
||||
field"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
|
||||
pub fn collect_per_file_ignores(
|
||||
pairs: Vec<PatternPrefixPair>,
|
||||
|
||||
5
src/docstrings/constants.rs
Normal file
5
src/docstrings/constants.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
|
||||
];
|
||||
|
||||
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];
|
||||
@@ -1,7 +1,7 @@
|
||||
pub mod constants;
|
||||
pub mod definition;
|
||||
pub mod extraction;
|
||||
pub mod google;
|
||||
pub mod helpers;
|
||||
pub mod numpy;
|
||||
pub mod sections;
|
||||
pub mod styles;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::docstrings::helpers;
|
||||
use crate::ast::whitespace;
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -14,7 +14,7 @@ pub(crate) struct SectionContext<'a> {
|
||||
fn suspected_as_section(line: &str, style: &SectionStyle) -> bool {
|
||||
style
|
||||
.lowercase_section_names()
|
||||
.contains(&helpers::leading_words(line).to_lowercase().as_str())
|
||||
.contains(&whitespace::leading_words(line).to_lowercase().as_str())
|
||||
}
|
||||
|
||||
/// Check if the suspected context is really a section header.
|
||||
@@ -64,7 +64,7 @@ pub(crate) fn section_contexts<'a>(
|
||||
let mut contexts = vec![];
|
||||
for lineno in suspected_section_indices {
|
||||
let context = SectionContext {
|
||||
section_name: helpers::leading_words(lines[lineno]),
|
||||
section_name: whitespace::leading_words(lines[lineno]),
|
||||
previous_line: lines[lineno - 1],
|
||||
line: lines[lineno],
|
||||
following_lines: &lines[lineno + 1..],
|
||||
|
||||
@@ -36,6 +36,7 @@ fn assertion_error(msg: Option<&Expr>) -> Stmt {
|
||||
)
|
||||
}
|
||||
|
||||
/// B011
|
||||
pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option<&Expr>) {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(false),
|
||||
@@ -43,7 +44,7 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option
|
||||
} = &test.node
|
||||
{
|
||||
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_stmt(&assertion_error(msg)) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
|
||||
@@ -54,7 +54,7 @@ fn duplicate_handler_exceptions<'a>(
|
||||
),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// TODO(charlie): If we have a single element, remove the tuple.
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_expr(&type_pattern(unique_elts), 0) {
|
||||
|
||||
@@ -20,6 +20,7 @@ fn attribute(value: &Expr, attr: &str) -> Expr {
|
||||
)
|
||||
}
|
||||
|
||||
/// B009
|
||||
pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "getattr" {
|
||||
@@ -32,7 +33,7 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
if IDENTIFIER_REGEX.is_match(value) && !KWLIST.contains(&value.as_str()) {
|
||||
let mut check =
|
||||
Check::new(CheckKind::GetAttrWithConstant, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_expr(&attribute(obj, value), 0) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use rustpython_ast::{Arguments, Expr, ExprKind};
|
||||
use rustpython_ast::{Arguments, Constant, Expr, ExprKind, Operator};
|
||||
|
||||
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
const MUTABLE_FUNCS: [(&str, &str); 7] = [
|
||||
const MUTABLE_FUNCS: &[(&str, &str)] = &[
|
||||
("", "dict"),
|
||||
("", "list"),
|
||||
("", "set"),
|
||||
@@ -16,6 +16,47 @@ const MUTABLE_FUNCS: [(&str, &str); 7] = [
|
||||
("collections", "deque"),
|
||||
];
|
||||
|
||||
const IMMUTABLE_TYPES: &[(&str, &str)] = &[
|
||||
("", "bool"),
|
||||
("", "bytes"),
|
||||
("", "complex"),
|
||||
("", "float"),
|
||||
("", "frozenset"),
|
||||
("", "int"),
|
||||
("", "object"),
|
||||
("", "range"),
|
||||
("", "str"),
|
||||
("collections.abc", "Sized"),
|
||||
("typing", "LiteralString"),
|
||||
("typing", "Sized"),
|
||||
];
|
||||
|
||||
const IMMUTABLE_GENERIC_TYPES: &[(&str, &str)] = &[
|
||||
("", "tuple"),
|
||||
("collections.abc", "ByteString"),
|
||||
("collections.abc", "Collection"),
|
||||
("collections.abc", "Container"),
|
||||
("collections.abc", "Iterable"),
|
||||
("collections.abc", "Mapping"),
|
||||
("collections.abc", "Reversible"),
|
||||
("collections.abc", "Sequence"),
|
||||
("collections.abc", "Set"),
|
||||
("typing", "AbstractSet"),
|
||||
("typing", "ByteString"),
|
||||
("typing", "Callable"),
|
||||
("typing", "Collection"),
|
||||
("typing", "Container"),
|
||||
("typing", "FrozenSet"),
|
||||
("typing", "Iterable"),
|
||||
("typing", "Literal"),
|
||||
("typing", "Mapping"),
|
||||
("typing", "Never"),
|
||||
("typing", "NoReturn"),
|
||||
("typing", "Reversible"),
|
||||
("typing", "Sequence"),
|
||||
("typing", "Tuple"),
|
||||
];
|
||||
|
||||
pub fn is_mutable_func(
|
||||
expr: &Expr,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
@@ -27,34 +68,106 @@ pub fn is_mutable_func(
|
||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||
}
|
||||
|
||||
fn is_mutable_expr(
|
||||
expr: &Expr,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
import_aliases: &FnvHashMap<&str, &str>,
|
||||
) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::List { .. }
|
||||
| ExprKind::Dict { .. }
|
||||
| ExprKind::Set { .. }
|
||||
| ExprKind::ListComp { .. }
|
||||
| ExprKind::DictComp { .. }
|
||||
| ExprKind::SetComp { .. } => true,
|
||||
ExprKind::Call { func, .. } => is_mutable_func(func, from_imports, import_aliases),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_immutable_annotation(
|
||||
expr: &Expr,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
import_aliases: &FnvHashMap<&str, &str>,
|
||||
) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Name { .. } | ExprKind::Attribute { .. } => {
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
IMMUTABLE_TYPES
|
||||
.iter()
|
||||
.chain(IMMUTABLE_GENERIC_TYPES)
|
||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||
}
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
let call_path = dealias_call_path(collect_call_paths(value), import_aliases);
|
||||
if IMMUTABLE_GENERIC_TYPES
|
||||
.iter()
|
||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||
{
|
||||
true
|
||||
} else if match_call_path(&call_path, "typing", "Union", from_imports) {
|
||||
if let ExprKind::Tuple { elts, .. } = &slice.node {
|
||||
elts.iter()
|
||||
.all(|elt| is_immutable_annotation(elt, from_imports, import_aliases))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else if match_call_path(&call_path, "typing", "Optional", from_imports) {
|
||||
is_immutable_annotation(slice, from_imports, import_aliases)
|
||||
} else if match_call_path(&call_path, "typing", "Annotated", from_imports) {
|
||||
if let ExprKind::Tuple { elts, .. } = &slice.node {
|
||||
elts.first().map_or(false, |elt| {
|
||||
is_immutable_annotation(elt, from_imports, import_aliases)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
ExprKind::BinOp {
|
||||
left,
|
||||
op: Operator::BitOr,
|
||||
right,
|
||||
} => {
|
||||
is_immutable_annotation(left, from_imports, import_aliases)
|
||||
&& is_immutable_annotation(right, from_imports, import_aliases)
|
||||
}
|
||||
ExprKind::Constant {
|
||||
value: Constant::None,
|
||||
..
|
||||
} => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// B006
|
||||
pub fn mutable_argument_default(checker: &mut Checker, arguments: &Arguments) {
|
||||
for expr in arguments
|
||||
.defaults
|
||||
// Scan in reverse order to right-align zip()
|
||||
for (arg, default) in arguments
|
||||
.kwonlyargs
|
||||
.iter()
|
||||
.chain(arguments.kw_defaults.iter())
|
||||
.rev()
|
||||
.zip(arguments.kw_defaults.iter().rev())
|
||||
.chain(
|
||||
arguments
|
||||
.args
|
||||
.iter()
|
||||
.rev()
|
||||
.chain(arguments.posonlyargs.iter().rev())
|
||||
.zip(arguments.defaults.iter().rev()),
|
||||
)
|
||||
{
|
||||
match &expr.node {
|
||||
ExprKind::List { .. }
|
||||
| ExprKind::Dict { .. }
|
||||
| ExprKind::Set { .. }
|
||||
| ExprKind::ListComp { .. }
|
||||
| ExprKind::DictComp { .. }
|
||||
| ExprKind::SetComp { .. } => {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MutableArgumentDefault,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
ExprKind::Call { func, .. } => {
|
||||
if is_mutable_func(func, &checker.from_imports, &checker.import_aliases) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MutableArgumentDefault,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
if is_mutable_expr(default, &checker.from_imports, &checker.import_aliases)
|
||||
&& arg.node.annotation.as_ref().map_or(true, |expr| {
|
||||
!is_immutable_annotation(expr, &checker.from_imports, &checker.import_aliases)
|
||||
})
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MutableArgumentDefault,
|
||||
Range::from_located(default),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ pub fn unused_loop_control_variable(checker: &mut Checker, target: &Expr, body:
|
||||
CheckKind::UnusedLoopControlVariable(name.to_string()),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Prefix the variable name with an underscore.
|
||||
check.amend(Fix::replacement(
|
||||
format!("_{name}"),
|
||||
|
||||
@@ -16,7 +16,7 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
||||
checker.settings.enabled.contains(&CheckCode::T203),
|
||||
Range::from_located(expr),
|
||||
) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
if matches!(
|
||||
checker.parents[context.defined_by].node,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use rustpython_ast::{Location, Stmt};
|
||||
use textwrap::{dedent, indent};
|
||||
|
||||
use crate::ast::helpers::{match_leading_content, match_trailing_content};
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::whitespace::leading_space;
|
||||
use crate::autofix::{fixer, Fix};
|
||||
use crate::checks::CheckKind;
|
||||
use crate::docstrings::helpers::leading_space;
|
||||
use crate::isort::{comments, format_imports};
|
||||
use crate::{Check, Settings, SourceCodeLocator};
|
||||
|
||||
@@ -27,34 +28,6 @@ fn extract_indentation(body: &[&Stmt], locator: &SourceCodeLocator) -> String {
|
||||
leading_space(&existing)
|
||||
}
|
||||
|
||||
fn match_leading_content(body: &[&Stmt], locator: &SourceCodeLocator) -> bool {
|
||||
let location = body.first().unwrap().location;
|
||||
let range = Range {
|
||||
location: Location::new(location.row(), 0),
|
||||
end_location: location,
|
||||
};
|
||||
let prefix = locator.slice_source_code_range(&range);
|
||||
prefix.chars().any(|char| !char.is_whitespace())
|
||||
}
|
||||
|
||||
fn match_trailing_content(body: &[&Stmt], locator: &SourceCodeLocator) -> bool {
|
||||
let end_location = body.last().unwrap().end_location.unwrap();
|
||||
let range = Range {
|
||||
location: end_location,
|
||||
end_location: Location::new(end_location.row() + 1, 0),
|
||||
};
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
for char in suffix.chars() {
|
||||
if char == '#' {
|
||||
return false;
|
||||
}
|
||||
if !char.is_whitespace() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// I001
|
||||
pub fn check_imports(
|
||||
body: Vec<&Stmt>,
|
||||
@@ -75,8 +48,8 @@ pub fn check_imports(
|
||||
);
|
||||
|
||||
// Special-cases: there's leading or trailing content in the import block.
|
||||
let has_leading_content = match_leading_content(&body, locator);
|
||||
let has_trailing_content = match_trailing_content(&body, locator);
|
||||
let has_leading_content = match_leading_content(body.first().unwrap(), locator);
|
||||
let has_trailing_content = match_trailing_content(body.last().unwrap(), locator);
|
||||
|
||||
// Generate the sorted import block.
|
||||
let expected = format_imports(
|
||||
@@ -91,7 +64,7 @@ pub fn check_imports(
|
||||
|
||||
if has_leading_content || has_trailing_content {
|
||||
let mut check = Check::new(CheckKind::UnsortedImports, range);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
let mut content = String::new();
|
||||
if has_leading_content {
|
||||
content.push('\n');
|
||||
@@ -119,7 +92,7 @@ pub fn check_imports(
|
||||
let actual = dedent(&locator.slice_source_code_range(&range));
|
||||
if actual != expected {
|
||||
let mut check = Check::new(CheckKind::UnsortedImports, range);
|
||||
if autofix.patch() {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
indent(&expected, &indentation),
|
||||
range.location,
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::check_tokens::check_tokens;
|
||||
use crate::checks::{Check, CheckCode, CheckKind, LintSource};
|
||||
use crate::code_gen::SourceGenerator;
|
||||
use crate::directives::Directives;
|
||||
use crate::message::Message;
|
||||
use crate::message::{Message, Source};
|
||||
use crate::noqa::add_noqa;
|
||||
use crate::settings::Settings;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
@@ -176,7 +176,15 @@ pub fn lint_stdin(
|
||||
// Convert to messages.
|
||||
Ok(checks
|
||||
.into_iter()
|
||||
.map(|check| Message::from_check(path.to_string_lossy().to_string(), check))
|
||||
.map(|check| {
|
||||
let filename = path.to_string_lossy().to_string();
|
||||
let source = if settings.show_source {
|
||||
Some(Source::from_check(&check, &locator))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Message::from_check(check, filename, source)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -233,7 +241,15 @@ pub fn lint_path(
|
||||
// Convert to messages.
|
||||
let messages: Vec<Message> = checks
|
||||
.into_iter()
|
||||
.map(|check| Message::from_check(path.to_string_lossy().to_string(), check))
|
||||
.map(|check| {
|
||||
let filename = path.to_string_lossy().to_string();
|
||||
let source = if settings.show_source {
|
||||
Some(Source::from_check(&check, &locator))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Message::from_check(check, filename, source)
|
||||
})
|
||||
.collect();
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
cache::set(path, &metadata, settings, autofix, &messages, mode);
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@@ -7,7 +7,7 @@ use std::time::Instant;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use ::ruff::cache;
|
||||
use ::ruff::checks::{CheckCode, CheckKind};
|
||||
use ::ruff::cli::{collect_per_file_ignores, extract_log_level, warn_on, Cli, Warnable};
|
||||
use ::ruff::cli::{collect_per_file_ignores, extract_log_level, Cli};
|
||||
use ::ruff::fs::iter_python_files;
|
||||
use ::ruff::linter::{add_noqa_to_path, autoformat_path, lint_path, lint_stdin};
|
||||
use ::ruff::logging::{set_up_logging, LogLevel};
|
||||
@@ -117,6 +117,7 @@ fn run_once(
|
||||
location: Default::default(),
|
||||
end_location: Default::default(),
|
||||
filename: path.to_string_lossy().to_string(),
|
||||
source: None,
|
||||
}]
|
||||
} else {
|
||||
error!("Failed to check {}: {message}", path.to_string_lossy());
|
||||
@@ -239,25 +240,9 @@ fn inner_main() -> Result<ExitCode> {
|
||||
collect_per_file_ignores(cli.per_file_ignores, project_root.as_ref());
|
||||
}
|
||||
if !cli.select.is_empty() {
|
||||
warn_on(
|
||||
Warnable::Select,
|
||||
&cli.select,
|
||||
&cli.ignore,
|
||||
&cli.extend_ignore,
|
||||
&configuration,
|
||||
pyproject.as_ref(),
|
||||
);
|
||||
configuration.select = cli.select;
|
||||
}
|
||||
if !cli.extend_select.is_empty() {
|
||||
warn_on(
|
||||
Warnable::ExtendSelect,
|
||||
&cli.extend_select,
|
||||
&cli.ignore,
|
||||
&cli.extend_ignore,
|
||||
&configuration,
|
||||
pyproject.as_ref(),
|
||||
);
|
||||
configuration.extend_select = cli.extend_select;
|
||||
}
|
||||
if !cli.ignore.is_empty() {
|
||||
@@ -266,6 +251,12 @@ fn inner_main() -> Result<ExitCode> {
|
||||
if !cli.extend_ignore.is_empty() {
|
||||
configuration.extend_ignore = cli.extend_ignore;
|
||||
}
|
||||
if !cli.fixable.is_empty() {
|
||||
configuration.fixable = cli.fixable;
|
||||
}
|
||||
if !cli.unfixable.is_empty() {
|
||||
configuration.unfixable = cli.unfixable;
|
||||
}
|
||||
if let Some(line_length) = cli.line_length {
|
||||
configuration.line_length = line_length;
|
||||
}
|
||||
@@ -281,6 +272,9 @@ fn inner_main() -> Result<ExitCode> {
|
||||
if let Some(fix) = fix {
|
||||
configuration.fix = fix;
|
||||
}
|
||||
if cli.show_source {
|
||||
configuration.show_source = true;
|
||||
}
|
||||
|
||||
if cli.show_settings && cli.show_files {
|
||||
eprintln!("Error: specify --show-settings or show-files (not both).");
|
||||
|
||||
@@ -2,12 +2,16 @@ use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
use annotate_snippets::display_list::{DisplayList, FormatOptions};
|
||||
use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
|
||||
use colored::Colorize;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::fs::relativize_path;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Message {
|
||||
@@ -16,16 +20,18 @@ pub struct Message {
|
||||
pub location: Location,
|
||||
pub end_location: Location,
|
||||
pub filename: String,
|
||||
pub source: Option<Source>,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn from_check(filename: String, check: Check) -> Self {
|
||||
pub fn from_check(check: Check, filename: String, source: Option<Source>) -> Self {
|
||||
Self {
|
||||
kind: check.kind,
|
||||
fixed: check.fix.map(|fix| fix.applied).unwrap_or_default(),
|
||||
location: Location::new(check.location.row(), check.location.column() + 1),
|
||||
end_location: Location::new(check.end_location.row(), check.end_location.column() + 1),
|
||||
filename,
|
||||
source,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +54,7 @@ impl PartialOrd for Message {
|
||||
|
||||
impl fmt::Display for Message {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
let label = format!(
|
||||
"{}{}{}{}{}{} {} {}",
|
||||
relativize_path(Path::new(&self.filename)).white().bold(),
|
||||
":".cyan(),
|
||||
@@ -58,7 +63,71 @@ impl fmt::Display for Message {
|
||||
self.location.column(),
|
||||
":".cyan(),
|
||||
self.kind.code().as_ref().red().bold(),
|
||||
self.kind.body()
|
||||
)
|
||||
self.kind.body(),
|
||||
);
|
||||
match &self.source {
|
||||
None => write!(f, "{}", label),
|
||||
Some(source) => {
|
||||
let snippet = Snippet {
|
||||
title: Some(Annotation {
|
||||
label: Some(&label),
|
||||
annotation_type: AnnotationType::Error,
|
||||
// The ID (error number) is already encoded in the `label`.
|
||||
id: None,
|
||||
}),
|
||||
footer: vec![],
|
||||
slices: vec![Slice {
|
||||
source: &source.contents,
|
||||
line_start: self.location.row(),
|
||||
annotations: vec![SourceAnnotation {
|
||||
label: self.kind.code().as_ref(),
|
||||
annotation_type: AnnotationType::Error,
|
||||
range: source.range,
|
||||
}],
|
||||
// The origin (file name, line number, and column number) is already encoded
|
||||
// in the `label`.
|
||||
origin: None,
|
||||
fold: false,
|
||||
}],
|
||||
opt: FormatOptions {
|
||||
color: true,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
// `split_once(' ')` strips "error: " from `message`.
|
||||
let message = DisplayList::from(snippet).to_string();
|
||||
let (_, message) = message.split_once(' ').unwrap();
|
||||
write!(f, "{}", message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Source {
|
||||
pub contents: String,
|
||||
pub range: (usize, usize),
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub fn from_check(check: &Check, locator: &SourceCodeLocator) -> Self {
|
||||
let source = locator.slice_source_code_range(&Range {
|
||||
location: Location::new(check.location.row(), 0),
|
||||
end_location: Location::new(check.end_location.row() + 1, 0),
|
||||
});
|
||||
let num_chars_in_range = locator
|
||||
.slice_source_code_range(&Range {
|
||||
location: check.location,
|
||||
end_location: check.end_location,
|
||||
})
|
||||
.chars()
|
||||
.count();
|
||||
Source {
|
||||
contents: source.to_string(),
|
||||
range: (
|
||||
check.location.column(),
|
||||
check.location.column() + num_chars_in_range,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
src/noqa.rs
48
src/noqa.rs
@@ -10,32 +10,40 @@ use regex::Regex;
|
||||
use crate::checks::{Check, CheckCode};
|
||||
|
||||
static NO_QA_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(r"(?P<noqa>\s*(?i:# noqa)(?::\s?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?)")
|
||||
.expect("Invalid regex")
|
||||
Regex::new(
|
||||
r"(?P<spaces>\s*)(?P<noqa>(?i:# noqa)(?::\s?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?)",
|
||||
)
|
||||
.expect("Invalid regex")
|
||||
});
|
||||
static SPLIT_COMMA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").expect("Invalid regex"));
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Directive<'a> {
|
||||
None,
|
||||
All(usize, usize),
|
||||
Codes(usize, usize, Vec<&'a str>),
|
||||
All(usize, usize, usize),
|
||||
Codes(usize, usize, usize, Vec<&'a str>),
|
||||
}
|
||||
|
||||
pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
match NO_QA_REGEX.captures(line) {
|
||||
Some(caps) => match caps.name("noqa") {
|
||||
Some(noqa) => match caps.name("codes") {
|
||||
Some(codes) => Directive::Codes(
|
||||
noqa.start(),
|
||||
noqa.end(),
|
||||
SPLIT_COMMA_REGEX
|
||||
.split(codes.as_str())
|
||||
.map(|code| code.trim())
|
||||
.filter(|code| !code.is_empty())
|
||||
.collect(),
|
||||
),
|
||||
None => Directive::All(noqa.start(), noqa.end()),
|
||||
Some(caps) => match caps.name("spaces") {
|
||||
Some(spaces) => match caps.name("noqa") {
|
||||
Some(noqa) => match caps.name("codes") {
|
||||
Some(codes) => Directive::Codes(
|
||||
spaces.as_str().chars().count(),
|
||||
noqa.start(),
|
||||
noqa.end(),
|
||||
SPLIT_COMMA_REGEX
|
||||
.split(codes.as_str())
|
||||
.map(|code| code.trim())
|
||||
.filter(|code| !code.is_empty())
|
||||
.collect(),
|
||||
),
|
||||
None => {
|
||||
Directive::All(spaces.as_str().chars().count(), noqa.start(), noqa.end())
|
||||
}
|
||||
},
|
||||
None => Directive::None,
|
||||
},
|
||||
None => Directive::None,
|
||||
},
|
||||
@@ -92,12 +100,14 @@ fn add_noqa_inner(
|
||||
match extract_noqa_directive(line) {
|
||||
Directive::None => {
|
||||
output.push_str(line);
|
||||
output.push_str(" # noqa: ");
|
||||
}
|
||||
Directive::All(_, start, _) | Directive::Codes(_, start, ..) => {
|
||||
output.push_str(&line[..start]);
|
||||
output.push_str("# noqa: ");
|
||||
}
|
||||
Directive::All(start, _) => output.push_str(&line[..start]),
|
||||
Directive::Codes(start, ..) => output.push_str(&line[..start]),
|
||||
};
|
||||
let codes: Vec<&str> = codes.iter().map(|code| code.as_ref()).collect();
|
||||
output.push_str(" # noqa: ");
|
||||
output.push_str(&codes.join(", "));
|
||||
output.push('\n');
|
||||
count += 1;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use itertools::izip;
|
||||
use rustpython_ast::Location;
|
||||
use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Stmt};
|
||||
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
@@ -46,19 +46,6 @@ pub fn ambiguous_function_name(name: &str, location: Range) -> Option<Check> {
|
||||
}
|
||||
}
|
||||
|
||||
/// E731
|
||||
pub fn do_not_assign_lambda(target: &Expr, value: &Expr, stmt: &Stmt) -> Option<Check> {
|
||||
if let ExprKind::Name { .. } = &target.node {
|
||||
if let ExprKind::Lambda { .. } = &value.node {
|
||||
return Some(Check::new(
|
||||
CheckKind::DoNotAssignLambda,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// E721
|
||||
pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
use anyhow::Result;
|
||||
use fnv::FnvHashMap;
|
||||
use itertools::izip;
|
||||
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Unaryop};
|
||||
use log::error;
|
||||
use rustpython_ast::{Arguments, Location, StmtKind};
|
||||
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Stmt, Unaryop};
|
||||
|
||||
use crate::ast::helpers::{match_leading_content, match_trailing_content};
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::whitespace::leading_space;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind, RejectedCmpop};
|
||||
@@ -62,7 +67,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Dummy replacement
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::Is);
|
||||
@@ -74,7 +79,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::IsNot);
|
||||
}
|
||||
@@ -93,7 +98,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::Is);
|
||||
}
|
||||
@@ -104,7 +109,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(0, Cmpop::IsNot);
|
||||
}
|
||||
@@ -129,7 +134,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::Is);
|
||||
}
|
||||
@@ -140,7 +145,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::NoneComparison(RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::IsNot);
|
||||
}
|
||||
@@ -159,7 +164,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::Is);
|
||||
}
|
||||
@@ -170,7 +175,7 @@ pub fn literal_comparisons(
|
||||
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
|
||||
Range::from_located(comparator),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::dummy(expr.location));
|
||||
bad_ops.insert(idx, Cmpop::IsNot);
|
||||
}
|
||||
@@ -226,7 +231,7 @@ pub fn not_tests(
|
||||
if check_not_in {
|
||||
let mut check =
|
||||
Check::new(CheckKind::NotInTest, Range::from_located(operand));
|
||||
if checker.patch() && should_fix {
|
||||
if checker.patch(check.kind.code()) && should_fix {
|
||||
if let Some(content) = compare(left, &[Cmpop::NotIn], comparators) {
|
||||
check.amend(Fix::replacement(
|
||||
content,
|
||||
@@ -241,8 +246,8 @@ pub fn not_tests(
|
||||
Cmpop::Is => {
|
||||
if check_not_is {
|
||||
let mut check =
|
||||
Check::new(CheckKind::NotInTest, Range::from_located(operand));
|
||||
if checker.patch() && should_fix {
|
||||
Check::new(CheckKind::NotIsTest, Range::from_located(operand));
|
||||
if checker.patch(check.kind.code()) && should_fix {
|
||||
if let Some(content) = compare(left, &[Cmpop::IsNot], comparators) {
|
||||
check.amend(Fix::replacement(
|
||||
content,
|
||||
@@ -260,3 +265,69 @@ pub fn not_tests(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn function(name: &str, args: &Arguments, body: &Expr) -> Result<String> {
|
||||
let body = Stmt::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
StmtKind::Return {
|
||||
value: Some(Box::new(body.clone())),
|
||||
},
|
||||
);
|
||||
let func = Stmt::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
StmtKind::FunctionDef {
|
||||
name: name.to_string(),
|
||||
args: Box::new(args.clone()),
|
||||
body: vec![body],
|
||||
decorator_list: vec![],
|
||||
returns: None,
|
||||
type_comment: None,
|
||||
},
|
||||
);
|
||||
let mut generator = SourceGenerator::new();
|
||||
generator.unparse_stmt(&func)?;
|
||||
generator.generate().map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// E731
|
||||
pub fn do_not_assign_lambda(checker: &mut Checker, target: &Expr, value: &Expr, stmt: &Stmt) {
|
||||
if let ExprKind::Name { id, .. } = &target.node {
|
||||
if let ExprKind::Lambda { args, body } = &value.node {
|
||||
let mut check = Check::new(CheckKind::DoNotAssignLambda, Range::from_located(stmt));
|
||||
if checker.patch(check.kind.code()) {
|
||||
if !match_leading_content(stmt, checker.locator)
|
||||
&& !match_trailing_content(stmt, checker.locator)
|
||||
{
|
||||
match function(id, args, body) {
|
||||
Ok(content) => {
|
||||
let indentation =
|
||||
&leading_space(&checker.locator.slice_source_code_range(&Range {
|
||||
location: Location::new(stmt.location.row(), 0),
|
||||
end_location: Location::new(stmt.location.row() + 1, 0),
|
||||
}));
|
||||
let mut indented = String::new();
|
||||
for (idx, line) in content.lines().enumerate() {
|
||||
if idx == 0 {
|
||||
indented.push_str(line);
|
||||
} else {
|
||||
indented.push('\n');
|
||||
indented.push_str(indentation);
|
||||
indented.push_str(line);
|
||||
}
|
||||
}
|
||||
check.amend(Fix::replacement(
|
||||
indented,
|
||||
stmt.location,
|
||||
stmt.end_location.unwrap(),
|
||||
));
|
||||
}
|
||||
Err(e) => error!("Failed to generate fix: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ use regex::Regex;
|
||||
use rustpython_ast::{Arg, Constant, ExprKind, Location, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::whitespace;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::docstrings::constants;
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
use crate::docstrings::helpers;
|
||||
use crate::docstrings::sections::{section_contexts, SectionContext};
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
use crate::visibility::{is_init, is_magic, is_overload, is_staticmethod, Visibility};
|
||||
@@ -179,7 +180,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
CheckKind::NoBlankLineBeforeFunction(blank_lines_before),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the blank line before the docstring.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() - blank_lines_before, 0),
|
||||
@@ -220,7 +221,7 @@ pub fn blank_before_after_function(checker: &mut Checker, definition: &Definitio
|
||||
CheckKind::NoBlankLineAfterFunction(blank_lines_after),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the blank line after the docstring.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.end_location.unwrap().row() + 1, 0),
|
||||
@@ -269,7 +270,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::NoBlankLineBeforeClass(blank_lines_before),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the blank line before the class.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() - blank_lines_before, 0),
|
||||
@@ -285,7 +286,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::OneBlankLineBeforeClass(blank_lines_before),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert one blank line before the class.
|
||||
check.amend(Fix::replacement(
|
||||
"\n".to_string(),
|
||||
@@ -322,7 +323,7 @@ pub fn blank_before_after_class(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::OneBlankLineAfterClass(blank_lines_after),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert a blank line before the class (replacing any existing lines).
|
||||
check.amend(Fix::replacement(
|
||||
"\n".to_string(),
|
||||
@@ -364,7 +365,7 @@ pub fn blank_after_summary(checker: &mut Checker, definition: &Definition) {
|
||||
CheckKind::BlankLineAfterSummary,
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert one blank line after the summary (replacing any existing lines).
|
||||
check.amend(Fix::replacement(
|
||||
"\n".to_string(),
|
||||
@@ -391,7 +392,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
return;
|
||||
}
|
||||
|
||||
let docstring_indent = helpers::indentation(checker, docstring);
|
||||
let docstring_indent = whitespace::indentation(checker, docstring);
|
||||
let mut has_seen_tab = docstring_indent.contains('\t');
|
||||
let mut is_over_indented = true;
|
||||
let mut over_indented_lines = vec![];
|
||||
@@ -408,7 +409,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let line_indent = helpers::leading_space(lines[i]);
|
||||
let line_indent = whitespace::leading_space(lines[i]);
|
||||
|
||||
// We only report tab indentation once, so only check if we haven't seen a tab
|
||||
// yet.
|
||||
@@ -425,9 +426,9 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
end_location: Location::new(docstring.location.row() + i, 0),
|
||||
},
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&docstring_indent),
|
||||
whitespace::clean(&docstring_indent),
|
||||
Location::new(docstring.location.row() + i, 0),
|
||||
Location::new(docstring.location.row() + i, line_indent.len()),
|
||||
));
|
||||
@@ -464,7 +465,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
// If every line (except the last) is over-indented...
|
||||
if is_over_indented {
|
||||
for i in over_indented_lines {
|
||||
let line_indent = helpers::leading_space(lines[i]);
|
||||
let line_indent = whitespace::leading_space(lines[i]);
|
||||
if line_indent.len() > docstring_indent.len() {
|
||||
// We report over-indentation on every line. This isn't great, but
|
||||
// enables autofix.
|
||||
@@ -475,9 +476,9 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
end_location: Location::new(docstring.location.row() + i, 0),
|
||||
},
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&docstring_indent),
|
||||
whitespace::clean(&docstring_indent),
|
||||
Location::new(docstring.location.row() + i, 0),
|
||||
Location::new(docstring.location.row() + i, line_indent.len()),
|
||||
));
|
||||
@@ -490,7 +491,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
// If the last line is over-indented...
|
||||
if !lines.is_empty() {
|
||||
let i = lines.len() - 1;
|
||||
let line_indent = helpers::leading_space(lines[i]);
|
||||
let line_indent = whitespace::leading_space(lines[i]);
|
||||
if line_indent.len() > docstring_indent.len() {
|
||||
let mut check = Check::new(
|
||||
CheckKind::NoOverIndentation,
|
||||
@@ -499,9 +500,9 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||
end_location: Location::new(docstring.location.row() + i, 0),
|
||||
},
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&docstring_indent),
|
||||
whitespace::clean(&docstring_indent),
|
||||
Location::new(docstring.location.row() + i, 0),
|
||||
Location::new(docstring.location.row() + i, line_indent.len()),
|
||||
));
|
||||
@@ -537,11 +538,11 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, definition: &Definiti
|
||||
CheckKind::NewLineAfterLastParagraph,
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Insert a newline just before the end-quote(s).
|
||||
let content = format!(
|
||||
"\n{}",
|
||||
helpers::clean(&helpers::indentation(checker, docstring))
|
||||
whitespace::clean(&whitespace::indentation(checker, docstring))
|
||||
);
|
||||
check.amend(Fix::insertion(
|
||||
content,
|
||||
@@ -580,7 +581,7 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, definition: &Definition)
|
||||
CheckKind::NoSurroundingWhitespace,
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(first_line) = checker
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(docstring))
|
||||
@@ -588,9 +589,9 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, definition: &Definition)
|
||||
.next()
|
||||
.map(|line| line.to_lowercase())
|
||||
{
|
||||
for pattern in helpers::TRIPLE_QUOTE_PREFIXES
|
||||
for pattern in constants::TRIPLE_QUOTE_PREFIXES
|
||||
.iter()
|
||||
.chain(helpers::SINGLE_QUOTE_PREFIXES)
|
||||
.chain(constants::SINGLE_QUOTE_PREFIXES)
|
||||
{
|
||||
if first_line.starts_with(pattern) {
|
||||
check.amend(Fix::replacement(
|
||||
@@ -634,7 +635,7 @@ pub fn multi_line_summary_start(checker: &mut Checker, definition: &Definition)
|
||||
.next()
|
||||
.map(|line| line.to_lowercase())
|
||||
{
|
||||
if helpers::TRIPLE_QUOTE_PREFIXES.contains(&first_line.as_str()) {
|
||||
if constants::TRIPLE_QUOTE_PREFIXES.contains(&first_line.as_str()) {
|
||||
if checker.settings.enabled.contains(&CheckCode::D212) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MultiLineSummaryFirstLine,
|
||||
@@ -916,11 +917,11 @@ fn blanks_and_section_underline(
|
||||
CheckKind::DashedUnderlineAfterSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a dashed line (of the appropriate length) under the section header.
|
||||
let content = format!(
|
||||
"{}{}\n",
|
||||
helpers::clean(&helpers::indentation(checker, docstring)),
|
||||
whitespace::clean(&whitespace::indentation(checker, docstring)),
|
||||
"-".repeat(context.section_name.len())
|
||||
);
|
||||
check.amend(Fix::insertion(
|
||||
@@ -950,11 +951,11 @@ fn blanks_and_section_underline(
|
||||
CheckKind::DashedUnderlineAfterSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a dashed line (of the appropriate length) under the section header.
|
||||
let content = format!(
|
||||
"{}{}\n",
|
||||
helpers::clean(&helpers::indentation(checker, docstring)),
|
||||
whitespace::clean(&whitespace::indentation(checker, docstring)),
|
||||
"-".repeat(context.section_name.len())
|
||||
);
|
||||
check.amend(Fix::insertion(
|
||||
@@ -972,7 +973,7 @@ fn blanks_and_section_underline(
|
||||
),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete any blank lines between the header and content.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() + context.original_index + 1, 0),
|
||||
@@ -995,7 +996,7 @@ fn blanks_and_section_underline(
|
||||
CheckKind::SectionUnderlineAfterName(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete any blank lines between the header and the underline.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(docstring.location.row() + context.original_index + 1, 0),
|
||||
@@ -1026,11 +1027,11 @@ fn blanks_and_section_underline(
|
||||
),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the existing underline with a line of the appropriate length.
|
||||
let content = format!(
|
||||
"{}{}\n",
|
||||
helpers::clean(&helpers::indentation(checker, docstring)),
|
||||
whitespace::clean(&whitespace::indentation(checker, docstring)),
|
||||
"-".repeat(context.section_name.len())
|
||||
);
|
||||
check.amend(Fix::replacement(
|
||||
@@ -1057,17 +1058,17 @@ fn blanks_and_section_underline(
|
||||
}
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::D215) {
|
||||
let leading_space = helpers::leading_space(non_empty_line);
|
||||
let indentation = helpers::indentation(checker, docstring);
|
||||
let leading_space = whitespace::leading_space(non_empty_line);
|
||||
let indentation = whitespace::indentation(checker, docstring);
|
||||
if leading_space.len() > indentation.len() {
|
||||
let mut check = Check::new(
|
||||
CheckKind::SectionUnderlineNotOverIndented(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the existing indentation with whitespace of the appropriate length.
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&indentation),
|
||||
whitespace::clean(&indentation),
|
||||
Location::new(
|
||||
docstring.location.row()
|
||||
+ context.original_index
|
||||
@@ -1113,7 +1114,7 @@ fn blanks_and_section_underline(
|
||||
),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete any blank lines between the header and content.
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(
|
||||
@@ -1172,7 +1173,7 @@ fn common_section(
|
||||
CheckKind::CapitalizeSectionName(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the section title with the capitalized variant. This requires
|
||||
// locating the start and end of the section name.
|
||||
if let Some(index) = context.line.find(&context.section_name) {
|
||||
@@ -1198,17 +1199,17 @@ fn common_section(
|
||||
}
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::D214) {
|
||||
let leading_space = helpers::leading_space(context.line);
|
||||
let indentation = helpers::indentation(checker, docstring);
|
||||
let leading_space = whitespace::leading_space(context.line);
|
||||
let indentation = whitespace::indentation(checker, docstring);
|
||||
if leading_space.len() > indentation.len() {
|
||||
let mut check = Check::new(
|
||||
CheckKind::SectionNotOverIndented(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the existing indentation with whitespace of the appropriate length.
|
||||
check.amend(Fix::replacement(
|
||||
helpers::clean(&indentation),
|
||||
whitespace::clean(&indentation),
|
||||
Location::new(docstring.location.row() + context.original_index, 0),
|
||||
Location::new(
|
||||
docstring.location.row() + context.original_index,
|
||||
@@ -1232,7 +1233,7 @@ fn common_section(
|
||||
CheckKind::BlankLineAfterLastSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a newline after the section.
|
||||
check.amend(Fix::insertion(
|
||||
"\n".to_string(),
|
||||
@@ -1253,7 +1254,7 @@ fn common_section(
|
||||
CheckKind::BlankLineAfterSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a newline after the section.
|
||||
check.amend(Fix::insertion(
|
||||
"\n".to_string(),
|
||||
@@ -1277,7 +1278,7 @@ fn common_section(
|
||||
CheckKind::BlankLineBeforeSection(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Add a blank line before the section.
|
||||
check.amend(Fix::insertion(
|
||||
"\n".to_string(),
|
||||
@@ -1400,13 +1401,13 @@ fn args_section(checker: &mut Checker, definition: &Definition, context: &Sectio
|
||||
fn parameters_section(checker: &mut Checker, definition: &Definition, context: &SectionContext) {
|
||||
// Collect the list of arguments documented in the docstring.
|
||||
let mut docstring_args: FnvHashSet<&str> = FnvHashSet::default();
|
||||
let section_level_indent = helpers::leading_space(context.line);
|
||||
let section_level_indent = whitespace::leading_space(context.line);
|
||||
for i in 1..context.following_lines.len() {
|
||||
let current_line = context.following_lines[i - 1];
|
||||
let current_leading_space = helpers::leading_space(current_line);
|
||||
let current_leading_space = whitespace::leading_space(current_line);
|
||||
let next_line = context.following_lines[i];
|
||||
if current_leading_space == section_level_indent
|
||||
&& (helpers::leading_space(next_line).len() > current_leading_space.len())
|
||||
&& (whitespace::leading_space(next_line).len() > current_leading_space.len())
|
||||
&& !next_line.trim().is_empty()
|
||||
{
|
||||
let parameters = if let Some(semi_index) = current_line.find(':') {
|
||||
@@ -1444,7 +1445,7 @@ fn numpy_section(checker: &mut Checker, definition: &Definition, context: &Secti
|
||||
CheckKind::NewLineAfterSectionName(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Delete the suffix. This requires locating the end of the section name.
|
||||
if let Some(index) = context.line.find(&context.section_name) {
|
||||
// Map from bytes to characters.
|
||||
@@ -1493,7 +1494,7 @@ fn google_section(checker: &mut Checker, definition: &Definition, context: &Sect
|
||||
CheckKind::SectionNameEndsInColon(context.section_name.to_string()),
|
||||
Range::from_located(docstring),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
// Replace the suffix. This requires locating the end of the section name.
|
||||
if let Some(index) = context.line.find(&context.section_name) {
|
||||
// Map from bytes to characters.
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn invalid_literal_comparison(
|
||||
&& (is_constant_non_singleton(left) || is_constant_non_singleton(right))
|
||||
{
|
||||
let mut check = Check::new(CheckKind::IsLiteral, location);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
match fix_invalid_literal_comparison(
|
||||
checker.locator,
|
||||
Range {
|
||||
|
||||
@@ -28,7 +28,7 @@ fn match_not_implemented(expr: &Expr) -> Option<&Expr> {
|
||||
pub fn raise_not_implemented(checker: &mut Checker, expr: &Expr) {
|
||||
if let Some(expr) = match_not_implemented(expr) {
|
||||
let mut check = Check::new(CheckKind::RaiseNotImplemented, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
"NotImplementedError".to_string(),
|
||||
expr.location,
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::{bail, Result};
|
||||
use log::error;
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, KeywordData, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers::match_module_member;
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
@@ -10,11 +11,13 @@ use crate::code_gen::SourceGenerator;
|
||||
use crate::python::identifiers::IDENTIFIER_REGEX;
|
||||
use crate::python::keyword::KWLIST;
|
||||
|
||||
/// Return the class name, arguments, and keywords for a `TypedDict` assignment.
|
||||
/// Return the class name, arguments, keywords and base class for a `TypedDict`
|
||||
/// assignment.
|
||||
fn match_typed_dict_assign<'a>(
|
||||
checker: &Checker,
|
||||
targets: &'a [Expr],
|
||||
value: &'a Expr,
|
||||
) -> Option<(&'a str, &'a [Expr], &'a [Keyword])> {
|
||||
) -> Option<(&'a str, &'a [Expr], &'a [Keyword], &'a ExprKind)> {
|
||||
if let Some(target) = targets.get(0) {
|
||||
if let ExprKind::Name { id: class_name, .. } = &target.node {
|
||||
if let ExprKind::Call {
|
||||
@@ -23,10 +26,14 @@ fn match_typed_dict_assign<'a>(
|
||||
keywords,
|
||||
} = &value.node
|
||||
{
|
||||
if let ExprKind::Name { id: func_name, .. } = &func.node {
|
||||
if func_name == "TypedDict" {
|
||||
return Some((class_name, args, keywords));
|
||||
}
|
||||
if match_module_member(
|
||||
func,
|
||||
"typing",
|
||||
"TypedDict",
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
) {
|
||||
return Some((class_name, args, keywords, &func.node));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,12 +72,13 @@ fn create_pass_stmt() -> Stmt {
|
||||
Stmt::new(Default::default(), Default::default(), StmtKind::Pass)
|
||||
}
|
||||
|
||||
/// Generate a `StmtKind:ClassDef` statement bsaed on the provided body and
|
||||
/// keywords.
|
||||
/// Generate a `StmtKind:ClassDef` statement based on the provided body,
|
||||
/// keywords and base class.
|
||||
fn create_class_def_stmt(
|
||||
class_name: &str,
|
||||
body: Vec<Stmt>,
|
||||
total_keyword: Option<KeywordData>,
|
||||
base_class: &ExprKind,
|
||||
) -> Stmt {
|
||||
let keywords = match total_keyword {
|
||||
Some(keyword) => vec![Keyword::new(
|
||||
@@ -88,10 +96,7 @@ fn create_class_def_stmt(
|
||||
bases: vec![Expr::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExprKind::Name {
|
||||
id: "TypedDict".to_string(),
|
||||
ctx: ExprContext::Load,
|
||||
},
|
||||
base_class.clone(),
|
||||
)],
|
||||
keywords,
|
||||
body,
|
||||
@@ -150,11 +155,11 @@ fn get_properties_from_keywords(keywords: &[Keyword]) -> Result<Vec<Stmt>> {
|
||||
|
||||
// The only way to have the `total` keyword is to use the args version, like:
|
||||
// (`TypedDict('name', {'a': int}, total=True)`)
|
||||
fn get_total_from_only_keyword(keywords: &[Keyword]) -> Option<KeywordData> {
|
||||
fn get_total_from_only_keyword(keywords: &[Keyword]) -> Option<&KeywordData> {
|
||||
match keywords.get(0) {
|
||||
Some(keyword) => match &keyword.node.arg {
|
||||
Some(arg) => match arg.as_str() {
|
||||
"total" => Some(keyword.node.clone()),
|
||||
"total" => Some(&keyword.node),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
@@ -171,7 +176,7 @@ fn get_properties_and_total(
|
||||
// dict and keywords. For example, the following is illegal:
|
||||
// MyType = TypedDict('MyType', {'a': int, 'b': str}, a=int, b=str)
|
||||
if let Some(dict) = args.get(1) {
|
||||
let total = get_total_from_only_keyword(keywords);
|
||||
let total = get_total_from_only_keyword(keywords).cloned();
|
||||
match &dict.node {
|
||||
ExprKind::Dict { keys, values } => {
|
||||
Ok((get_properties_from_dict_literal(keys, values)?, total))
|
||||
@@ -188,15 +193,21 @@ fn get_properties_and_total(
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a `Fix` to convert a `TypedDict` to a functional class.
|
||||
fn convert_to_functional_class(
|
||||
/// Generate a `Fix` to convert a `TypedDict` from functional to class.
|
||||
fn convert_to_class(
|
||||
stmt: &Stmt,
|
||||
class_name: &str,
|
||||
body: Vec<Stmt>,
|
||||
total_keyword: Option<KeywordData>,
|
||||
base_class: &ExprKind,
|
||||
) -> Result<Fix> {
|
||||
let mut generator = SourceGenerator::new();
|
||||
generator.unparse_stmt(&create_class_def_stmt(class_name, body, total_keyword))?;
|
||||
generator.unparse_stmt(&create_class_def_stmt(
|
||||
class_name,
|
||||
body,
|
||||
total_keyword,
|
||||
base_class,
|
||||
))?;
|
||||
let content = generator.generate()?;
|
||||
Ok(Fix::replacement(
|
||||
content,
|
||||
@@ -212,7 +223,9 @@ pub fn convert_typed_dict_functional_to_class(
|
||||
targets: &[Expr],
|
||||
value: &Expr,
|
||||
) {
|
||||
if let Some((class_name, args, keywords)) = match_typed_dict_assign(targets, value) {
|
||||
if let Some((class_name, args, keywords, base_class)) =
|
||||
match_typed_dict_assign(checker, targets, value)
|
||||
{
|
||||
match get_properties_and_total(args, keywords) {
|
||||
Err(err) => error!("Failed to parse TypedDict: {}", err),
|
||||
Ok((body, total_keyword)) => {
|
||||
@@ -220,8 +233,8 @@ pub fn convert_typed_dict_functional_to_class(
|
||||
CheckKind::ConvertTypedDictFunctionalToClass,
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
if checker.patch() {
|
||||
match convert_to_functional_class(stmt, class_name, body, total_keyword) {
|
||||
if checker.patch(check.kind.code()) {
|
||||
match convert_to_class(stmt, class_name, body, total_keyword, base_class) {
|
||||
Ok(fix) => check.amend(fix),
|
||||
Err(err) => error!("Failed to convert TypedDict: {}", err),
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn deprecated_unittest_alias(checker: &mut Checker, expr: &Expr) {
|
||||
CheckKind::DeprecatedUnittestAlias(attr.to_string(), target.to_string()),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
format!("self.{}", target),
|
||||
expr.location,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
if let Some(mut check) = checks::super_args(scope, &parents, expr, func, args) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(fix) = pyupgrade::fixes::remove_super_arguments(checker.locator, expr) {
|
||||
check.amend(fix);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::pyupgrade::checks;
|
||||
/// U003
|
||||
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let Some(mut check) = checks::type_of_primitive(func, args, Range::from_located(expr)) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let CheckKind::TypeOfPrimitive(primitive) = &check.kind {
|
||||
check.amend(Fix::replacement(
|
||||
primitive.builtin(),
|
||||
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
const UTF8_LITERALS: &[&str] = &["utf-8", "utf8", "utf_8", "u8", "utf", "cp65001"];
|
||||
@@ -124,13 +124,16 @@ pub fn unnecessary_encode_utf8(
|
||||
expr,
|
||||
variable,
|
||||
checker.locator,
|
||||
checker.patch(),
|
||||
checker.patch(&CheckCode::U012),
|
||||
));
|
||||
} else {
|
||||
// "unicode text©".encode("utf-8")
|
||||
if let Some(check) =
|
||||
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
|
||||
{
|
||||
if let Some(check) = delete_default_encode_arg_or_kwarg(
|
||||
expr,
|
||||
args,
|
||||
kwargs,
|
||||
checker.patch(&CheckCode::U012),
|
||||
) {
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
@@ -139,9 +142,12 @@ pub fn unnecessary_encode_utf8(
|
||||
// f"foo{bar}".encode(*args, **kwargs)
|
||||
ExprKind::JoinedStr { .. } => {
|
||||
if is_default_encode(args, kwargs) {
|
||||
if let Some(check) =
|
||||
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
|
||||
{
|
||||
if let Some(check) = delete_default_encode_arg_or_kwarg(
|
||||
expr,
|
||||
args,
|
||||
kwargs,
|
||||
checker.patch(&CheckCode::U012),
|
||||
) {
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
|
||||
),
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn unnecessary_lru_cache_params(checker: &mut Checker, decorator_list: &[Exp
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(fix) =
|
||||
fixes::remove_unnecessary_lru_cache_params(checker.locator, &check.location)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr, id: &str) {
|
||||
CheckKind::UsePEP585Annotation(replacement.to_string()),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
replacement.to_lowercase(),
|
||||
expr.location,
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
|
||||
let call_path = dealias_call_path(collect_call_paths(value), &checker.import_aliases);
|
||||
if checker.match_typing_call_path(&call_path, "Optional") {
|
||||
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_expr(&optional(slice), 0) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
@@ -62,7 +62,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
|
||||
checker.add_check(check);
|
||||
} else if checker.match_typing_call_path(&call_path, "Union") {
|
||||
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
match &slice.node {
|
||||
ExprKind::Slice { .. } => {
|
||||
// Invalid type annotation.
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr,
|
||||
if let Some(mut check) =
|
||||
checks::useless_metaclass_type(targets, value, Range::from_located(stmt))
|
||||
{
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn useless_object_inheritance(
|
||||
) {
|
||||
let scope = checker.current_scope();
|
||||
if let Some(mut check) = checks::useless_object_inheritance(name, bases, scope) {
|
||||
if checker.patch() {
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(fix) = pyupgrade::fixes::remove_class_def_base(
|
||||
checker.locator,
|
||||
&stmt.location,
|
||||
|
||||
@@ -3,10 +3,10 @@ use once_cell::sync::Lazy;
|
||||
use rustpython_ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::autofix::{fixer, Fix};
|
||||
use crate::checks::CheckKind;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::Check;
|
||||
use crate::{Check, Settings};
|
||||
|
||||
/// See: https://github.com/microsoft/vscode/blob/095ddabc52b82498ee7f718a34f9dd11d59099a8/src/vs/base/common/strings.ts#L1094
|
||||
static CONFUSABLES: Lazy<FnvHashMap<u32, u32>> = Lazy::new(|| {
|
||||
@@ -1606,7 +1606,8 @@ pub fn ambiguous_unicode_character(
|
||||
start: &Location,
|
||||
end: &Location,
|
||||
context: Context,
|
||||
fix: bool,
|
||||
settings: &Settings,
|
||||
autofix: &fixer::Mode,
|
||||
) -> Vec<Check> {
|
||||
let mut checks = vec![];
|
||||
|
||||
@@ -1645,14 +1646,16 @@ pub fn ambiguous_unicode_character(
|
||||
end_location,
|
||||
},
|
||||
);
|
||||
if fix {
|
||||
check.amend(Fix::replacement(
|
||||
representant.to_string(),
|
||||
location,
|
||||
end_location,
|
||||
));
|
||||
if settings.enabled.contains(check.kind.code()) {
|
||||
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
representant.to_string(),
|
||||
location,
|
||||
end_location,
|
||||
));
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,15 @@ pub struct Configuration {
|
||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||
pub extend_select: Vec<CheckCodePrefix>,
|
||||
pub fix: bool,
|
||||
pub fixable: Vec<CheckCodePrefix>,
|
||||
pub ignore: Vec<CheckCodePrefix>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||
pub select: Vec<CheckCodePrefix>,
|
||||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
pub unfixable: Vec<CheckCodePrefix>,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
||||
@@ -121,6 +124,28 @@ impl Configuration {
|
||||
.unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]),
|
||||
extend_select: options.extend_select.unwrap_or_default(),
|
||||
fix: options.fix.unwrap_or_default(),
|
||||
fixable: options.fixable.unwrap_or_else(|| {
|
||||
// TODO(charlie): Autogenerate this list.
|
||||
vec![
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
CheckCodePrefix::D,
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::I,
|
||||
CheckCodePrefix::M,
|
||||
CheckCodePrefix::N,
|
||||
CheckCodePrefix::Q,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
]
|
||||
}),
|
||||
unfixable: options.unfixable.unwrap_or_default(),
|
||||
ignore: options.ignore.unwrap_or_default(),
|
||||
line_length: options.line_length.unwrap_or(88),
|
||||
per_file_ignores: options
|
||||
@@ -134,6 +159,7 @@ impl Configuration {
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
show_source: options.show_source.unwrap_or_default(),
|
||||
// Plugins
|
||||
flake8_annotations: options
|
||||
.flake8_annotations
|
||||
|
||||
@@ -30,8 +30,10 @@ pub struct Settings {
|
||||
pub enabled: FnvHashSet<CheckCode>,
|
||||
pub exclude: Vec<FilePattern>,
|
||||
pub extend_exclude: Vec<FilePattern>,
|
||||
pub fixable: FnvHashSet<CheckCode>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<PerFileIgnore>,
|
||||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
@@ -49,13 +51,20 @@ impl Settings {
|
||||
Self {
|
||||
dummy_variable_rgx: config.dummy_variable_rgx,
|
||||
enabled: resolve_codes(
|
||||
&config.select,
|
||||
&config.extend_select,
|
||||
&config.ignore,
|
||||
&config.extend_ignore,
|
||||
&config
|
||||
.select
|
||||
.into_iter()
|
||||
.chain(config.extend_select.into_iter())
|
||||
.collect::<Vec<_>>(),
|
||||
&config
|
||||
.ignore
|
||||
.into_iter()
|
||||
.chain(config.extend_ignore.into_iter())
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
exclude: config.exclude,
|
||||
extend_exclude: config.extend_exclude,
|
||||
fixable: resolve_codes(&config.fixable, &config.unfixable),
|
||||
flake8_annotations: config.flake8_annotations,
|
||||
flake8_bugbear: config.flake8_bugbear,
|
||||
flake8_quotes: config.flake8_quotes,
|
||||
@@ -67,13 +76,15 @@ impl Settings {
|
||||
per_file_ignores: config.per_file_ignores,
|
||||
src: config.src,
|
||||
target_version: config.target_version,
|
||||
show_source: config.show_source,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_rule(check_code: CheckCode) -> Self {
|
||||
Self {
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: FnvHashSet::from_iter([check_code]),
|
||||
enabled: FnvHashSet::from_iter([check_code.clone()]),
|
||||
fixable: FnvHashSet::from_iter([check_code]),
|
||||
exclude: Default::default(),
|
||||
extend_exclude: Default::default(),
|
||||
line_length: 88,
|
||||
@@ -87,13 +98,15 @@ impl Settings {
|
||||
isort: Default::default(),
|
||||
mccabe: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
show_source: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_rules(check_codes: Vec<CheckCode>) -> Self {
|
||||
Self {
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: FnvHashSet::from_iter(check_codes),
|
||||
enabled: FnvHashSet::from_iter(check_codes.clone()),
|
||||
fixable: FnvHashSet::from_iter(check_codes),
|
||||
exclude: Default::default(),
|
||||
extend_exclude: Default::default(),
|
||||
line_length: 88,
|
||||
@@ -107,6 +120,7 @@ impl Settings {
|
||||
isort: Default::default(),
|
||||
mccabe: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
show_source: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,10 +132,14 @@ impl Hash for Settings {
|
||||
for value in self.enabled.iter() {
|
||||
value.hash(state);
|
||||
}
|
||||
for value in self.fixable.iter() {
|
||||
value.hash(state);
|
||||
}
|
||||
self.line_length.hash(state);
|
||||
for value in self.per_file_ignores.iter() {
|
||||
value.hash(state);
|
||||
}
|
||||
self.show_source.hash(state);
|
||||
self.target_version.hash(state);
|
||||
// Add plugin properties in alphabetical order.
|
||||
self.flake8_annotations.hash(state);
|
||||
@@ -136,12 +154,7 @@ impl Hash for Settings {
|
||||
|
||||
/// Given a set of selected and ignored prefixes, resolve the set of enabled
|
||||
/// error codes.
|
||||
fn resolve_codes(
|
||||
select: &[CheckCodePrefix],
|
||||
extend_select: &[CheckCodePrefix],
|
||||
ignore: &[CheckCodePrefix],
|
||||
extend_ignore: &[CheckCodePrefix],
|
||||
) -> FnvHashSet<CheckCode> {
|
||||
fn resolve_codes(select: &[CheckCodePrefix], ignore: &[CheckCodePrefix]) -> FnvHashSet<CheckCode> {
|
||||
let mut codes: FnvHashSet<CheckCode> = FnvHashSet::default();
|
||||
for specificity in [
|
||||
PrefixSpecificity::Category,
|
||||
@@ -154,11 +167,6 @@ fn resolve_codes(
|
||||
codes.extend(prefix.codes());
|
||||
}
|
||||
}
|
||||
for prefix in extend_select {
|
||||
if prefix.specificity() == specificity {
|
||||
codes.extend(prefix.codes());
|
||||
}
|
||||
}
|
||||
for prefix in ignore {
|
||||
if prefix.specificity() == specificity {
|
||||
for code in prefix.codes() {
|
||||
@@ -166,13 +174,6 @@ fn resolve_codes(
|
||||
}
|
||||
}
|
||||
}
|
||||
for prefix in extend_ignore {
|
||||
if prefix.specificity() == specificity {
|
||||
for code in prefix.codes() {
|
||||
codes.remove(&code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
codes
|
||||
}
|
||||
@@ -187,19 +188,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resolver() {
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[], &[], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W292, CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W6], &[], &[], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W6], &[]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[], &[CheckCodePrefix::W292], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[CheckCodePrefix::W292]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W605], &[], &[CheckCodePrefix::W605], &[]);
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W605], &[CheckCodePrefix::W605]);
|
||||
let expected = FnvHashSet::from_iter([]);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@@ -19,11 +19,14 @@ pub struct Options {
|
||||
pub extend_ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub extend_select: Option<Vec<CheckCodePrefix>>,
|
||||
pub fix: Option<bool>,
|
||||
pub fixable: Option<Vec<CheckCodePrefix>>,
|
||||
pub ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub line_length: Option<usize>,
|
||||
pub select: Option<Vec<CheckCodePrefix>>,
|
||||
pub show_source: Option<bool>,
|
||||
pub src: Option<Vec<String>>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
pub unfixable: Option<Vec<CheckCodePrefix>>,
|
||||
// Plugins
|
||||
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
|
||||
|
||||
@@ -134,18 +134,21 @@ mod tests {
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -168,18 +171,21 @@ line-length = 79
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: Some(79),
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: None,
|
||||
line_length: Some(79),
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -210,10 +216,13 @@ exclude = ["foo.py"]
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
fixable: None,
|
||||
unfixable: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
show_source: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -236,18 +245,21 @@ select = ["E501"]
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: Some(vec![CheckCodePrefix::E501]),
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
extend_select: None,
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: None,
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: Some(vec![CheckCodePrefix::E501]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -271,18 +283,21 @@ ignore = ["E501"]
|
||||
pyproject.tool,
|
||||
Some(Tools {
|
||||
ruff: Some(Options {
|
||||
line_length: None,
|
||||
fix: None,
|
||||
dummy_variable_rgx: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
extend_select: Some(vec![CheckCodePrefix::M001]),
|
||||
ignore: Some(vec![CheckCodePrefix::E501]),
|
||||
extend_ignore: None,
|
||||
extend_select: Some(vec![CheckCodePrefix::M001]),
|
||||
fix: None,
|
||||
fixable: None,
|
||||
ignore: Some(vec![CheckCodePrefix::E501]),
|
||||
line_length: None,
|
||||
per_file_ignores: None,
|
||||
dummy_variable_rgx: None,
|
||||
select: None,
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
unfixable: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
@@ -357,6 +372,8 @@ other-attribute = 1
|
||||
extend_select: None,
|
||||
ignore: None,
|
||||
extend_ignore: None,
|
||||
fixable: None,
|
||||
unfixable: None,
|
||||
per_file_ignores: Some(FnvHashMap::from_iter([(
|
||||
"__init__.py".to_string(),
|
||||
vec![CheckCodePrefix::F401]
|
||||
@@ -364,6 +381,7 @@ other-attribute = 1
|
||||
dummy_variable_rgx: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
show_source: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: Some(flake8_bugbear::settings::Options {
|
||||
extend_immutable_calls: Some(vec![
|
||||
|
||||
@@ -43,12 +43,15 @@ pub struct UserConfiguration {
|
||||
pub extend_ignore: Vec<CheckCodePrefix>,
|
||||
pub extend_select: Vec<CheckCodePrefix>,
|
||||
pub fix: bool,
|
||||
pub fixable: Vec<CheckCodePrefix>,
|
||||
pub ignore: Vec<CheckCodePrefix>,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<(Exclusion, Vec<CheckCode>)>,
|
||||
pub select: Vec<CheckCodePrefix>,
|
||||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
pub unfixable: Vec<CheckCodePrefix>,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
@@ -81,6 +84,8 @@ impl UserConfiguration {
|
||||
extend_ignore: configuration.extend_ignore,
|
||||
extend_select: configuration.extend_select,
|
||||
fix: configuration.fix,
|
||||
fixable: configuration.fixable,
|
||||
unfixable: configuration.unfixable,
|
||||
ignore: configuration.ignore,
|
||||
line_length: configuration.line_length,
|
||||
per_file_ignores: configuration
|
||||
@@ -96,6 +101,7 @@ impl UserConfiguration {
|
||||
select: configuration.select,
|
||||
src: configuration.src,
|
||||
target_version: configuration.target_version,
|
||||
show_source: configuration.show_source,
|
||||
flake8_annotations: configuration.flake8_annotations,
|
||||
flake8_quotes: configuration.flake8_quotes,
|
||||
flake8_tidy_imports: configuration.flake8_tidy_imports,
|
||||
|
||||
@@ -90,4 +90,28 @@ expression: checks
|
||||
row: 170
|
||||
column: 48
|
||||
fix: ~
|
||||
- kind: MutableArgumentDefault
|
||||
location:
|
||||
row: 203
|
||||
column: 26
|
||||
end_location:
|
||||
row: 203
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: MutableArgumentDefault
|
||||
location:
|
||||
row: 204
|
||||
column: 34
|
||||
end_location:
|
||||
row: 204
|
||||
column: 36
|
||||
fix: ~
|
||||
- kind: MutableArgumentDefault
|
||||
location:
|
||||
row: 205
|
||||
column: 61
|
||||
end_location:
|
||||
row: 205
|
||||
column: 66
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: NotInTest
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 2
|
||||
column: 7
|
||||
@@ -19,7 +19,7 @@ expression: checks
|
||||
row: 2
|
||||
column: 13
|
||||
applied: false
|
||||
- kind: NotInTest
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 5
|
||||
column: 7
|
||||
@@ -36,7 +36,7 @@ expression: checks
|
||||
row: 5
|
||||
column: 15
|
||||
applied: false
|
||||
- kind: NotInTest
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 8
|
||||
column: 7
|
||||
|
||||
@@ -9,7 +9,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 2
|
||||
column: 19
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "def f(x):\n return (2 * x)"
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 19
|
||||
applied: false
|
||||
- kind: DoNotAssignLambda
|
||||
location:
|
||||
row: 4
|
||||
@@ -17,7 +26,16 @@ expression: checks
|
||||
end_location:
|
||||
row: 4
|
||||
column: 19
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "def f(x):\n return (2 * x)"
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 19
|
||||
applied: false
|
||||
- kind: DoNotAssignLambda
|
||||
location:
|
||||
row: 7
|
||||
@@ -25,5 +43,14 @@ expression: checks
|
||||
end_location:
|
||||
row: 7
|
||||
column: 29
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: "def this(y, z):\n return (2 * x)"
|
||||
location:
|
||||
row: 7
|
||||
column: 4
|
||||
end_location:
|
||||
row: 7
|
||||
column: 29
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -47,4 +47,13 @@ expression: checks
|
||||
row: 21
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: b
|
||||
location:
|
||||
row: 51
|
||||
column: 8
|
||||
end_location:
|
||||
row: 51
|
||||
column: 9
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,155 +4,172 @@ expression: checks
|
||||
---
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 4
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
row: 5
|
||||
column: 52
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType1(TypedDict):\n a: int\n b: str"
|
||||
location:
|
||||
row: 4
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
row: 5
|
||||
column: 52
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 50
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType2(TypedDict):\n a: int\n b: str"
|
||||
location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
row: 8
|
||||
column: 50
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 10
|
||||
row: 11
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
row: 11
|
||||
column: 44
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType3(TypedDict):\n a: int\n b: str"
|
||||
location:
|
||||
row: 10
|
||||
row: 11
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
row: 11
|
||||
column: 44
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 30
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType4(TypedDict):\n pass"
|
||||
location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 30
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 46
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType5(TypedDict):\n a: 'hello'"
|
||||
location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 46
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 17
|
||||
row: 18
|
||||
column: 0
|
||||
end_location:
|
||||
row: 17
|
||||
row: 18
|
||||
column: 41
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType6(TypedDict):\n a: 'hello'"
|
||||
location:
|
||||
row: 17
|
||||
row: 18
|
||||
column: 0
|
||||
end_location:
|
||||
row: 17
|
||||
row: 18
|
||||
column: 41
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 0
|
||||
end_location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 56
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType7(TypedDict):\n a: NotRequired[dict]"
|
||||
location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 0
|
||||
end_location:
|
||||
row: 20
|
||||
row: 21
|
||||
column: 56
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 23
|
||||
row: 24
|
||||
column: 0
|
||||
end_location:
|
||||
row: 23
|
||||
row: 24
|
||||
column: 65
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType8(TypedDict, total=False):\n x: int\n y: int"
|
||||
location:
|
||||
row: 23
|
||||
row: 24
|
||||
column: 0
|
||||
end_location:
|
||||
row: 23
|
||||
row: 24
|
||||
column: 65
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 29
|
||||
row: 30
|
||||
column: 0
|
||||
end_location:
|
||||
row: 29
|
||||
row: 30
|
||||
column: 59
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType10(TypedDict):\n key: Literal['value']"
|
||||
location:
|
||||
row: 29
|
||||
row: 30
|
||||
column: 0
|
||||
end_location:
|
||||
row: 29
|
||||
row: 30
|
||||
column: 59
|
||||
applied: false
|
||||
- kind: ConvertTypedDictFunctionalToClass
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 33
|
||||
column: 53
|
||||
fix:
|
||||
patch:
|
||||
content: "class MyType11(typing.TypedDict):\n key: int"
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 33
|
||||
column: 53
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -65,4 +65,13 @@ expression: checks
|
||||
row: 37
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: b
|
||||
location:
|
||||
row: 51
|
||||
column: 8
|
||||
end_location:
|
||||
row: 51
|
||||
column: 9
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ expression: checks
|
||||
UnusedNOQA: ~
|
||||
location:
|
||||
row: 9
|
||||
column: 9
|
||||
column: 11
|
||||
end_location:
|
||||
row: 9
|
||||
column: 17
|
||||
@@ -25,7 +25,7 @@ expression: checks
|
||||
- E501
|
||||
location:
|
||||
row: 13
|
||||
column: 9
|
||||
column: 11
|
||||
end_location:
|
||||
row: 13
|
||||
column: 23
|
||||
@@ -45,7 +45,7 @@ expression: checks
|
||||
- E501
|
||||
location:
|
||||
row: 16
|
||||
column: 9
|
||||
column: 11
|
||||
end_location:
|
||||
row: 16
|
||||
column: 29
|
||||
@@ -61,16 +61,17 @@ expression: checks
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA:
|
||||
- F841
|
||||
- W191
|
||||
location:
|
||||
row: 19
|
||||
column: 9
|
||||
column: 11
|
||||
end_location:
|
||||
row: 19
|
||||
column: 29
|
||||
fix:
|
||||
patch:
|
||||
content: " # noqa: F841"
|
||||
content: ""
|
||||
location:
|
||||
row: 19
|
||||
column: 9
|
||||
@@ -78,60 +79,107 @@ expression: checks
|
||||
row: 19
|
||||
column: 29
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA:
|
||||
- E501
|
||||
location:
|
||||
row: 23
|
||||
column: 9
|
||||
end_location:
|
||||
row: 23
|
||||
column: 21
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 23
|
||||
column: 9
|
||||
end_location:
|
||||
row: 23
|
||||
column: 21
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedVariable: d
|
||||
location:
|
||||
row: 26
|
||||
column: 4
|
||||
end_location:
|
||||
row: 26
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedNOQA:
|
||||
- E501
|
||||
location:
|
||||
row: 26
|
||||
column: 32
|
||||
end_location:
|
||||
row: 26
|
||||
column: 44
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 26
|
||||
column: 9
|
||||
end_location:
|
||||
row: 26
|
||||
column: 44
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA:
|
||||
- F841
|
||||
location:
|
||||
row: 44
|
||||
column: 3
|
||||
row: 52
|
||||
column: 5
|
||||
end_location:
|
||||
row: 44
|
||||
row: 52
|
||||
column: 23
|
||||
fix:
|
||||
patch:
|
||||
content: " # noqa: E501"
|
||||
content: "# noqa: E501"
|
||||
location:
|
||||
row: 44
|
||||
column: 3
|
||||
row: 52
|
||||
column: 5
|
||||
end_location:
|
||||
row: 44
|
||||
row: 52
|
||||
column: 23
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA:
|
||||
- E501
|
||||
location:
|
||||
row: 52
|
||||
column: 3
|
||||
row: 60
|
||||
column: 5
|
||||
end_location:
|
||||
row: 52
|
||||
row: 60
|
||||
column: 17
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 52
|
||||
row: 60
|
||||
column: 3
|
||||
end_location:
|
||||
row: 52
|
||||
row: 60
|
||||
column: 17
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA: ~
|
||||
location:
|
||||
row: 60
|
||||
column: 3
|
||||
row: 68
|
||||
column: 5
|
||||
end_location:
|
||||
row: 60
|
||||
row: 68
|
||||
column: 11
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 60
|
||||
row: 68
|
||||
column: 3
|
||||
end_location:
|
||||
row: 60
|
||||
row: 68
|
||||
column: 11
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -78,3 +78,15 @@ fn test_stdin_autofix_when_no_issues_should_still_print_contents() -> Result<()>
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_show_source() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(crate_name!())?;
|
||||
let output = cmd
|
||||
.args(["-", "--show-source"])
|
||||
.write_stdin("l = 1")
|
||||
.assert()
|
||||
.failure();
|
||||
assert!(str::from_utf8(&output.get_output().stdout)?.contains("l = 1"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user