Compare commits

..

18 Commits

Author SHA1 Message Date
Charlie Marsh
406491a3a2 Bump version to 0.0.53 2022-10-04 08:56:46 -04:00
Charlie Marsh
bfae262359 Simplify noqa extraction logic (#320) 2022-10-04 08:56:14 -04:00
Charlie Marsh
af894f290f Disable plugin-based rules by default (#318) 2022-10-04 08:28:46 -04:00
Charlie Marsh
c901742244 Add plugins mention to README (#309) 2022-10-03 17:23:53 -04:00
Charlie Marsh
7e4faf4b69 Implement flake8-print (#308) 2022-10-03 17:19:56 -04:00
Charlie Marsh
31a0b20271 Bump version to 0.0.52 2022-10-03 15:22:58 -04:00
Charlie Marsh
0966bf2c66 Handle multi-import lines (#307) 2022-10-03 15:22:46 -04:00
Charlie Marsh
64d8e25528 Bump version to 0.0.51 2022-10-03 14:08:39 -04:00
Charlie Marsh
b049cced04 Automatically remove unused imports (#298) 2022-10-03 14:08:16 -04:00
Charlie Marsh
bc335f839e Visit lambda arguments prior to deferral (#303) 2022-10-02 20:54:02 -04:00
Charlie Marsh
4819e19ba2 Bump version to 0.0.50 2022-10-02 20:43:30 -04:00
Charlie Marsh
622b8adb79 Avoid falling back to A003 when A001 is disabled (#302) 2022-10-02 20:43:12 -04:00
Charlie Marsh
558d9fcbe3 Enable LibCST-based autofixing for SPR001 (#297) 2022-10-02 19:58:13 -04:00
Charlie Marsh
83f18193c2 Add an end location to Check (#299) 2022-10-02 12:50:42 -04:00
Charlie Marsh
46e6a1b3be Add end locations to all nodes (#296) 2022-10-02 12:49:48 -04:00
Suguru Yamamoto
4d0d433af9 fix: Make assigns to dunder exception for E402. (#294) 2022-10-01 09:43:47 -04:00
Christian Clauss
11f7532e72 pre-commit: Validate pyproject.toml (#266) 2022-09-30 19:21:12 -04:00
Charlie Marsh
417764d309 Expose a public 'check' method (#289) 2022-09-30 11:30:37 -04:00
75 changed files with 2050 additions and 347 deletions

View File

@@ -3,3 +3,8 @@ repos:
rev: v0.0.40
hooks:
- id: lint
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.10.1
hooks:
- id: validate-pyproject

97
Cargo.lock generated
View File

@@ -37,6 +37,12 @@ dependencies = [
"libc",
]
[[package]]
name = "annotate-snippets"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7021ce4924a3f25f802b2cccd1af585e39ea1a363a1aa2e72afe54b67a3a7a7"
[[package]]
name = "anyhow"
version = "1.0.60"
@@ -359,6 +365,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chic"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b5db619f3556839cb2223ae86ff3f9a09da2c5013be42bc9af08c9589bf70c"
dependencies = [
"annotate-snippets",
]
[[package]]
name = "chrono"
version = "0.4.21"
@@ -1062,9 +1077,9 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.3"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
@@ -1153,6 +1168,30 @@ version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
[[package]]
name = "libcst"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=32a044c127668df44582f85699358e67803b0d73#32a044c127668df44582f85699358e67803b0d73"
dependencies = [
"chic",
"itertools",
"libcst_derive",
"once_cell",
"paste",
"peg",
"regex",
"thiserror",
]
[[package]]
name = "libcst_derive"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=32a044c127668df44582f85699358e67803b0d73#32a044c127668df44582f85699358e67803b0d73"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
@@ -1379,9 +1418,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.13.1"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "opaque-debug"
@@ -1430,6 +1469,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "paste"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
[[package]]
name = "path-absolutize"
version = "3.0.13"
@@ -1448,6 +1493,33 @@ dependencies = [
"once_cell",
]
[[package]]
name = "peg"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a07f2cafdc3babeebc087e499118343442b742cc7c31b4d054682cc598508554"
dependencies = [
"peg-macros",
"peg-runtime",
]
[[package]]
name = "peg-macros"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a90084dc05cf0428428e3d12399f39faad19b0909f64fb9170c9fdd6d9cd49b"
dependencies = [
"peg-runtime",
"proc-macro2",
"quote",
]
[[package]]
name = "peg-runtime"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739"
[[package]]
name = "percent-encoding"
version = "2.1.0"
@@ -1799,7 +1871,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.49"
version = "0.0.53"
dependencies = [
"anyhow",
"bincode",
@@ -1815,6 +1887,7 @@ dependencies = [
"glob",
"insta",
"itertools",
"libcst",
"log",
"notify",
"once_cell",
@@ -1844,7 +1917,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=966a80597d626a9a47eaec78471164422d341453#966a80597d626a9a47eaec78471164422d341453"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=4f457893efc381ad5c432576b24bcc7e4a08c641#4f457893efc381ad5c432576b24bcc7e4a08c641"
dependencies = [
"num-bigint",
"rustpython-compiler-core",
@@ -1853,7 +1926,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=966a80597d626a9a47eaec78471164422d341453#966a80597d626a9a47eaec78471164422d341453"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=4f457893efc381ad5c432576b24bcc7e4a08c641#4f457893efc381ad5c432576b24bcc7e4a08c641"
dependencies = [
"bincode",
"bitflags",
@@ -1870,7 +1943,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=966a80597d626a9a47eaec78471164422d341453#966a80597d626a9a47eaec78471164422d341453"
source = "git+https://github.com/charliermarsh/RustPython.git?rev=4f457893efc381ad5c432576b24bcc7e4a08c641#4f457893efc381ad5c432576b24bcc7e4a08c641"
dependencies = [
"ahash",
"anyhow",
@@ -2187,18 +2260,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.32"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.49"
version = "0.0.53"
edition = "2021"
[lib]
@@ -19,14 +19,15 @@ dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.17" }
glob = { version = "0.3.0" }
itertools = { version = "0.10.3" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "32a044c127668df44582f85699358e67803b0d73" }
log = { version = "0.4.17" }
notify = { version = "4.0.17" }
once_cell = { version = "1.13.1" }
path-absolutize = { version = "3.0.13", features = ["once_cell_cache"] }
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/charliermarsh/RustPython.git", rev = "966a80597d626a9a47eaec78471164422d341453" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/charliermarsh/RustPython.git", rev = "4f457893efc381ad5c432576b24bcc7e4a08c641" }
serde = { version = "1.0.143", features = ["derive"] }
serde_json = { version = "1.0.83" }
toml = { version = "0.5.9" }

View File

@@ -201,18 +201,24 @@ stylistic lint rules that are obviated by autoformatting.
### Parity with Flake8
ruff's goal is to achieve feature-parity with Flake8 when used (1) without plugins, (2) alongside
ruff's goal is to achieve feature parity with Flake8 when used (1) without plugins, (2) alongside
Black, and (3) on Python 3 code.
**Under those conditions, ruff implements 44 out of 60 rules.** (ruff is missing: 14 rules related
to string `.format` calls, 1 rule related to docstring parsing, and 1 rule related to redefined
variables.)
ruff also implements some of the most popular Flake8 plugins natively, including:
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis Flake8:
1. Flake8 has a plugin architecture and supports writing custom lint rules.
2. ruff does not yet support a few Python 3.9 and 3.10 language features, including structural
1. ruff does not yet support a few Python 3.9 and 3.10 language features, including structural
pattern matching and parenthesized context managers.
2. Flake8 has a plugin architecture and supports writing custom lint rules.
## Rules
@@ -264,6 +270,8 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
| A002 | BuiltinArgumentShadowing | Argument `...` is shadowing a python builtin |
| A003 | BuiltinAttributeShadowing | class attribute `...` is shadowing a python builtin |
| SPR001 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` |
| T201 | PrintFound | `print` found |
| T203 | PPrintFound | `pprint` found` |
| R001 | UselessObjectInheritance | Class `...` inherits from object |
| R002 | NoAssertEquals | `assertEquals` is deprecated, use `assertEqual` instead |
| M001 | UnusedNOQA | Unused `noqa` directive |

View File

@@ -1,4 +1,8 @@
"""Top-level docstring."""
__all__ = ["y"]
__version__: str = "0.1.0"
import a
try:

View File

@@ -1,6 +1,5 @@
from __future__ import all_feature_names
import os
import functools
import functools, os
from datetime import datetime
from collections import (
Counter,
@@ -11,14 +10,36 @@ import multiprocessing.pool
import multiprocessing.process
import logging.config
import logging.handlers
from typing import TYPING_CHECK, NamedTuple, Dict, Type, TypeVar, List, Set, Union, cast
from typing import (
TYPE_CHECKING,
NamedTuple,
Dict,
Type,
TypeVar,
List,
Set,
Union,
cast,
)
from dataclasses import MISSING, field
from blah import ClassA, ClassB, ClassC
if TYPING_CHECK:
if TYPE_CHECKING:
from models import Fruit, Nut, Vegetable
if TYPE_CHECKING:
import shelve
import importlib
if TYPE_CHECKING:
"""Hello, world!"""
import pathlib
z = 1
class X:
datetime: datetime
foo: Type["NamedTuple"]
@@ -28,6 +49,9 @@ class X:
y = Counter()
z = multiprocessing.pool.ThreadPool()
def b(self) -> None:
import pickle
__all__ = ["ClassA"] + ["ClassB"]
__all__ += ["ClassC"]
@@ -39,3 +63,5 @@ Z = TypeVar("Z", "List", "Set")
a = list["Fruit"]
b = Union["Nut", None]
c = cast("Vegetable", b)
Field = lambda default=MISSING: field(default=default)

1
resources/test/fixtures/T201.py vendored Normal file
View File

@@ -0,0 +1 @@
print("Hello, world!") # T201

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

@@ -0,0 +1,10 @@
from pprint import pprint
pprint("Hello, world!") # T203
import pprint
pprint.pprint("Hello, world!") # T203
pprint.pformat("Hello, world!")

View File

@@ -2,7 +2,10 @@ from __future__ import annotations
from dataclasses import dataclass
from models import Fruit, Nut
from models import (
Fruit,
Nut,
)
@dataclass

View File

@@ -8,13 +8,15 @@ use rustpython_parser::ast::{
};
use crate::ast::operations::SourceCodeLocator;
use crate::ast::types::{Binding, BindingKind, CheckLocator, FunctionScope, Scope, ScopeKind};
use crate::ast::types::{
Binding, BindingKind, CheckLocator, FunctionScope, Range, Scope, ScopeKind,
};
use crate::autofix::{fixer, fixes};
use crate::checks::{Check, CheckKind, Fix, RejectedCmpop};
use crate::python::builtins::BUILTINS;
/// Check IfTuple compliance.
pub fn check_if_tuple(test: &Expr, location: Location) -> Option<Check> {
pub fn check_if_tuple(test: &Expr, location: Range) -> Option<Check> {
if let ExprKind::Tuple { elts, .. } = &test.node {
if !elts.is_empty() {
return Some(Check::new(CheckKind::IfTuple, location));
@@ -24,7 +26,7 @@ pub fn check_if_tuple(test: &Expr, location: Location) -> Option<Check> {
}
/// Check AssertTuple compliance.
pub fn check_assert_tuple(test: &Expr, location: Location) -> Option<Check> {
pub fn check_assert_tuple(test: &Expr, location: Range) -> Option<Check> {
if let ExprKind::Tuple { elts, .. } = &test.node {
if !elts.is_empty() {
return Some(Check::new(CheckKind::AssertTuple, location));
@@ -51,7 +53,7 @@ pub fn check_not_tests(
if check_not_in {
checks.push(Check::new(
CheckKind::NotInTest,
locator.locate_check(operand.location),
locator.locate_check(Range::from_located(operand)),
));
}
}
@@ -59,7 +61,7 @@ pub fn check_not_tests(
if check_not_is {
checks.push(Check::new(
CheckKind::NotIsTest,
locator.locate_check(operand.location),
locator.locate_check(Range::from_located(operand)),
));
}
}
@@ -97,7 +99,7 @@ pub fn check_unused_variables(
{
checks.push(Check::new(
CheckKind::UnusedVariable(name.to_string()),
locator.locate_check(binding.location),
locator.locate_check(binding.range),
));
}
}
@@ -106,7 +108,7 @@ pub fn check_unused_variables(
}
/// Check DoNotAssignLambda compliance.
pub fn check_do_not_assign_lambda(value: &Expr, location: Location) -> Option<Check> {
pub fn check_do_not_assign_lambda(value: &Expr, location: Range) -> Option<Check> {
if let ExprKind::Lambda { .. } = &value.node {
Some(Check::new(CheckKind::DoNotAssignLambda, location))
} else {
@@ -119,7 +121,7 @@ fn is_ambiguous_name(name: &str) -> bool {
}
/// Check AmbiguousVariableName compliance.
pub fn check_ambiguous_variable_name(name: &str, location: Location) -> Option<Check> {
pub fn check_ambiguous_variable_name(name: &str, location: Range) -> Option<Check> {
if is_ambiguous_name(name) {
Some(Check::new(
CheckKind::AmbiguousVariableName(name.to_string()),
@@ -131,7 +133,7 @@ pub fn check_ambiguous_variable_name(name: &str, location: Location) -> Option<C
}
/// Check AmbiguousClassName compliance.
pub fn check_ambiguous_class_name(name: &str, location: Location) -> Option<Check> {
pub fn check_ambiguous_class_name(name: &str, location: Range) -> Option<Check> {
if is_ambiguous_name(name) {
Some(Check::new(
CheckKind::AmbiguousClassName(name.to_string()),
@@ -143,7 +145,7 @@ pub fn check_ambiguous_class_name(name: &str, location: Location) -> Option<Chec
}
/// Check AmbiguousFunctionName compliance.
pub fn check_ambiguous_function_name(name: &str, location: Location) -> Option<Check> {
pub fn check_ambiguous_function_name(name: &str, location: Range) -> Option<Check> {
if is_ambiguous_name(name) {
Some(Check::new(
CheckKind::AmbiguousFunctionName(name.to_string()),
@@ -175,7 +177,7 @@ pub fn check_useless_object_inheritance(
}) => {
let mut check = Check::new(
CheckKind::UselessObjectInheritance(name.to_string()),
expr.location,
Range::from_located(expr),
);
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
if let Some(fix) = fixes::remove_class_def_base(
@@ -206,7 +208,7 @@ pub fn check_default_except_not_last(handlers: &Vec<Excepthandler>) -> Option<Ch
if type_.is_none() && idx < handlers.len() - 1 {
return Some(Check::new(
CheckKind::DefaultExceptNotLast,
handler.location,
Range::from_located(handler),
));
}
}
@@ -220,13 +222,19 @@ pub fn check_raise_not_implemented(expr: &Expr) -> Option<Check> {
ExprKind::Call { func, .. } => {
if let ExprKind::Name { id, .. } = &func.node {
if id == "NotImplemented" {
return Some(Check::new(CheckKind::RaiseNotImplemented, expr.location));
return Some(Check::new(
CheckKind::RaiseNotImplemented,
Range::from_located(expr),
));
}
}
}
ExprKind::Name { id, .. } => {
if id == "NotImplemented" {
return Some(Check::new(CheckKind::RaiseNotImplemented, expr.location));
return Some(Check::new(
CheckKind::RaiseNotImplemented,
Range::from_located(expr),
));
}
}
_ => {}
@@ -258,7 +266,10 @@ pub fn check_duplicate_arguments(arguments: &Arguments) -> Vec<Check> {
for arg in all_arguments {
let ident = &arg.node.arg;
if idents.contains(ident.as_str()) {
checks.push(Check::new(CheckKind::DuplicateArgumentName, arg.location));
checks.push(Check::new(
CheckKind::DuplicateArgumentName,
Range::from_located(arg),
));
}
idents.insert(ident);
}
@@ -272,12 +283,16 @@ pub fn check_assert_equals(expr: &Expr, autofix: &fixer::Mode) -> Option<Check>
if attr == "assertEquals" {
if let ExprKind::Name { id, .. } = &value.node {
if id == "self" {
let mut check = Check::new(CheckKind::NoAssertEquals, expr.location);
let mut check =
Check::new(CheckKind::NoAssertEquals, Range::from_located(expr));
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
check.amend(Fix {
content: "assertEqual".to_string(),
start: Location::new(expr.location.row(), expr.location.column() + 1),
end: Location::new(
location: Location::new(
expr.location.row(),
expr.location.column() + 1,
),
end_location: Location::new(
expr.location.row(),
expr.location.column() + 1 + "assertEquals".len(),
),
@@ -326,7 +341,7 @@ pub fn check_repeated_keys(
if check_repeated_literals && v1 == v2 {
checks.push(Check::new(
CheckKind::MultiValueRepeatedKeyLiteral,
locator.locate_check(k2.location),
locator.locate_check(Range::from_located(k2)),
))
}
}
@@ -334,7 +349,7 @@ pub fn check_repeated_keys(
if check_repeated_variables && v1 == v2 {
checks.push(Check::new(
CheckKind::MultiValueRepeatedKeyVariable((*v2).to_string()),
locator.locate_check(k2.location),
locator.locate_check(Range::from_located(k2)),
))
}
}
@@ -373,13 +388,13 @@ pub fn check_literal_comparisons(
if matches!(op, Cmpop::Eq) {
checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::Eq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
if matches!(op, Cmpop::NotEq) {
checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::NotEq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
}
@@ -393,13 +408,13 @@ pub fn check_literal_comparisons(
if matches!(op, Cmpop::Eq) {
checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
if matches!(op, Cmpop::NotEq) {
checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
}
@@ -419,13 +434,13 @@ pub fn check_literal_comparisons(
if matches!(op, Cmpop::Eq) {
checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::Eq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
if matches!(op, Cmpop::NotEq) {
checks.push(Check::new(
CheckKind::NoneComparison(RejectedCmpop::NotEq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
}
@@ -439,13 +454,13 @@ pub fn check_literal_comparisons(
if matches!(op, Cmpop::Eq) {
checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::Eq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
if matches!(op, Cmpop::NotEq) {
checks.push(Check::new(
CheckKind::TrueFalseComparison(value, RejectedCmpop::NotEq),
locator.locate_check(comparator.location),
locator.locate_check(Range::from_located(comparator)),
));
}
}
@@ -482,7 +497,7 @@ pub fn check_is_literal(
left: &Expr,
ops: &Vec<Cmpop>,
comparators: &Vec<Expr>,
location: Location,
location: Range,
) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
@@ -503,7 +518,7 @@ pub fn check_is_literal(
pub fn check_type_comparison(
ops: &Vec<Cmpop>,
comparators: &Vec<Expr>,
location: Location,
location: Range,
) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
@@ -544,7 +559,7 @@ pub fn check_starred_expressions(
elts: &[Expr],
check_too_many_expressions: bool,
check_two_starred_expressions: bool,
location: Location,
location: Range,
) -> Option<Check> {
let mut has_starred: bool = false;
let mut starred_index: Option<usize> = None;
@@ -606,7 +621,7 @@ pub fn check_break_outside_loop(
if !allowed {
Some(Check::new(
CheckKind::BreakOutsideLoop,
locator.locate_check(stmt.location),
locator.locate_check(Range::from_located(stmt)),
))
} else {
None
@@ -647,7 +662,7 @@ pub fn check_continue_outside_loop(
if !allowed {
Some(Check::new(
CheckKind::ContinueOutsideLoop,
locator.locate_check(stmt.location),
locator.locate_check(Range::from_located(stmt)),
))
} else {
None
@@ -664,7 +679,7 @@ pub enum ShadowingType {
/// Check builtin name shadowing
pub fn check_builtin_shadowing(
name: &str,
location: Location,
location: Range,
node_type: ShadowingType,
) -> Option<Check> {
if BUILTINS.contains(&name) {
@@ -683,15 +698,54 @@ pub fn check_builtin_shadowing(
// flake8-super
/// Check that `super()` has no args
pub fn check_super_args(expr: &Expr, args: &Vec<Expr>) -> Option<Check> {
if let ExprKind::Name { id, .. } = &expr.node {
pub fn check_super_args(
expr: &Expr,
func: &Expr,
args: &Vec<Expr>,
locator: &mut SourceCodeLocator,
autofix: &fixer::Mode,
) -> Option<Check> {
if let ExprKind::Name { id, .. } = &func.node {
if id == "super" && !args.is_empty() {
return Some(Check::new(
let mut check = Check::new(
CheckKind::SuperCallWithParameters,
expr.location,
Range::from_located(expr),
);
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
if let Some(fix) = fixes::remove_super_arguments(locator, expr) {
check.amend(fix);
}
}
return Some(check);
}
}
None
}
// flake8-print
/// Check whether a function call is a `print` or `pprint` invocation
pub fn check_print_call(expr: &Expr, func: &Expr) -> Option<Check> {
if let ExprKind::Name { id, .. } = &func.node {
if id == "print" {
return Some(Check::new(CheckKind::PrintFound, Range::from_located(expr)));
} else if id == "pprint" {
return Some(Check::new(
CheckKind::PPrintFound,
Range::from_located(expr),
));
}
}
if let ExprKind::Attribute { value, attr, .. } = &func.node {
if let ExprKind::Name { id, .. } = &value.node {
if id == "pprint" && attr == "pprint" {
return Some(Check::new(
CheckKind::PPrintFound,
Range::from_located(expr),
));
}
}
}
None
}

View File

@@ -1,6 +1,6 @@
use rustpython_parser::ast::{Constant, Expr, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::{BindingKind, Scope};
use crate::ast::types::{BindingKind, Range, Scope};
/// Extract the names bound to a given __all__ assignment.
pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
@@ -134,7 +134,7 @@ impl<'a> SourceCodeLocator<'a> {
}
}
pub fn slice_source_code(&mut self, location: &Location) -> &'a str {
pub fn slice_source_code_at(&mut self, location: &Location) -> &'a str {
if !self.initialized {
let mut offset = 0;
for i in self.content.lines() {
@@ -147,4 +147,19 @@ impl<'a> SourceCodeLocator<'a> {
let offset = self.offsets[location.row() - 1] + location.column() - 1;
&self.content[offset..]
}
pub fn slice_source_code_range(&mut self, range: &Range) -> &'a str {
if !self.initialized {
let mut offset = 0;
for i in self.content.lines() {
self.offsets.push(offset);
offset += i.len();
offset += 1;
}
self.initialized = true;
}
let start = self.offsets[range.location.row() - 1] + range.location.column() - 1;
let end = self.offsets[range.end_location.row() - 1] + range.end_location.column() - 1;
&self.content[start..end]
}
}

View File

@@ -1,13 +1,17 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword, Location};
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
fn relocate_keyword(keyword: &mut Keyword, location: Location) {
keyword.location = location;
use crate::ast::types::Range;
fn relocate_keyword(keyword: &mut Keyword, location: Range) {
keyword.location = location.location;
keyword.end_location = location.end_location;
relocate_expr(&mut keyword.node.value, location);
}
/// Change an expression's location (recursively) to match a desired, fixed location.
pub fn relocate_expr(expr: &mut Expr, location: Location) {
expr.location = location;
pub fn relocate_expr(expr: &mut Expr, location: Range) {
expr.location = location.location;
expr.end_location = location.end_location;
match &mut expr.node {
ExprKind::BoolOp { values, .. } => {
for expr in values {

View File

@@ -1,13 +1,28 @@
use std::collections::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use rustpython_parser::ast::Location;
use rustpython_parser::ast::{Located, Location};
fn id() -> usize {
static COUNTER: AtomicUsize = AtomicUsize::new(1);
COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Range {
pub location: Location,
pub end_location: Location,
}
impl Range {
pub fn from_located<T>(located: &Located<T>) -> Self {
Range {
location: located.location,
end_location: located.end_location,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct FunctionScope {
pub uses_locals: bool,
@@ -40,6 +55,12 @@ impl Scope {
}
}
#[derive(Clone, Debug)]
pub struct BindingContext {
pub defined_by: usize,
pub defined_in: Option<usize>,
}
#[derive(Clone, Debug)]
pub enum BindingKind {
Annotation,
@@ -52,20 +73,27 @@ pub enum BindingKind {
Definition,
Export(Vec<String>),
FutureImportation,
Importation(String),
StarImportation,
SubmoduleImportation(String),
Importation(String, BindingContext),
FromImportation(String, BindingContext),
SubmoduleImportation(String, BindingContext),
}
#[derive(Clone, Debug)]
pub struct Binding {
pub kind: BindingKind,
pub location: Location,
/// Tuple of (scope index, location) indicating the scope and location at which the binding was
pub range: Range,
/// Tuple of (scope index, range) indicating the scope and range at which the binding was
/// last used.
pub used: Option<(usize, Location)>,
pub used: Option<(usize, Range)>,
}
pub trait CheckLocator {
fn locate_check(&self, default: Location) -> Location;
fn locate_check(&self, default: Range) -> Range;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum ImportKind {
Import,
ImportFrom,
}

View File

@@ -2,6 +2,7 @@ use std::fs;
use std::path::Path;
use anyhow::Result;
use itertools::Itertools;
use rustpython_parser::ast::Location;
use crate::checks::{Check, Fix};
@@ -43,41 +44,46 @@ fn apply_fixes<'a>(fixes: impl Iterator<Item = &'a mut Fix>, contents: &str) ->
let mut output = "".to_string();
let mut last_pos: Location = Location::new(0, 0);
for fix in fixes {
for fix in fixes.sorted_by_key(|fix| fix.location) {
// Best-effort approach: if this fix overlaps with a fix we've already applied, skip it.
if last_pos > fix.start {
if last_pos > fix.location {
continue;
}
if fix.start.row() > last_pos.row() {
if fix.location.row() > last_pos.row() {
if last_pos.row() > 0 || last_pos.column() > 0 {
output.push_str(&lines[last_pos.row() - 1][last_pos.column() - 1..]);
output.push('\n');
}
for line in &lines[last_pos.row()..fix.start.row() - 1] {
for line in &lines[last_pos.row()..fix.location.row() - 1] {
output.push_str(line);
output.push('\n');
}
output.push_str(&lines[fix.start.row() - 1][..fix.start.column() - 1]);
output.push_str(&lines[fix.location.row() - 1][..fix.location.column() - 1]);
output.push_str(&fix.content);
} else {
output.push_str(
&lines[last_pos.row() - 1][last_pos.column() - 1..fix.start.column() - 1],
&lines[last_pos.row() - 1][last_pos.column() - 1..fix.location.column() - 1],
);
output.push_str(&fix.content);
}
last_pos = fix.end;
last_pos = fix.end_location;
fix.applied = true;
}
if last_pos.row() > 0 || last_pos.column() > 0 {
if last_pos.row() > 0
&& (last_pos.row() - 1) < lines.len()
&& (last_pos.row() > 0 || last_pos.column() > 0)
{
output.push_str(&lines[last_pos.row() - 1][last_pos.column() - 1..]);
output.push('\n');
}
for line in &lines[last_pos.row()..] {
output.push_str(line);
output.push('\n');
if last_pos.row() < lines.len() {
for line in &lines[last_pos.row()..] {
output.push_str(line);
output.push('\n');
}
}
output
@@ -106,8 +112,8 @@ mod tests {
fn apply_single_replacement() -> Result<()> {
let mut fixes = vec![Fix {
content: "Bar".to_string(),
start: Location::new(1, 9),
end: Location::new(1, 15),
location: Location::new(1, 9),
end_location: Location::new(1, 15),
applied: false,
}];
let actual = apply_fixes(
@@ -130,8 +136,8 @@ mod tests {
fn apply_single_removal() -> Result<()> {
let mut fixes = vec![Fix {
content: "".to_string(),
start: Location::new(1, 8),
end: Location::new(1, 16),
location: Location::new(1, 8),
end_location: Location::new(1, 16),
applied: false,
}];
let actual = apply_fixes(
@@ -155,14 +161,14 @@ mod tests {
let mut fixes = vec![
Fix {
content: "".to_string(),
start: Location::new(1, 8),
end: Location::new(1, 17),
location: Location::new(1, 8),
end_location: Location::new(1, 17),
applied: false,
},
Fix {
content: "".to_string(),
start: Location::new(1, 17),
end: Location::new(1, 24),
location: Location::new(1, 17),
end_location: Location::new(1, 24),
applied: false,
},
];
@@ -187,14 +193,14 @@ mod tests {
let mut fixes = vec![
Fix {
content: "".to_string(),
start: Location::new(1, 8),
end: Location::new(1, 16),
location: Location::new(1, 8),
end_location: Location::new(1, 16),
applied: false,
},
Fix {
content: "ignored".to_string(),
start: Location::new(1, 10),
end: Location::new(1, 12),
location: Location::new(1, 10),
end_location: Location::new(1, 12),
applied: false,
},
];

View File

@@ -1,8 +1,14 @@
use rustpython_parser::ast::{Expr, Keyword, Location};
use anyhow::Result;
use itertools::Itertools;
use libcst_native::ImportNames::Aliases;
use libcst_native::NameOrAttribute::N;
use libcst_native::{Codegen, Expression, SmallStatement, Statement};
use rustpython_parser::ast::{ExcepthandlerKind, Expr, Keyword, Location, Stmt, StmtKind};
use rustpython_parser::lexer;
use rustpython_parser::token::Tok;
use crate::ast::operations::SourceCodeLocator;
use crate::ast::types::Range;
use crate::checks::Fix;
/// Convert a location within a file (relative to `base`) to an absolute position.
@@ -25,7 +31,7 @@ pub fn remove_class_def_base(
bases: &[Expr],
keywords: &[Keyword],
) -> Option<Fix> {
let content = locator.slice_source_code(stmt_at);
let content = locator.slice_source_code_at(stmt_at);
// Case 1: `object` is the only base.
if bases.len() == 1 && keywords.is_empty() {
@@ -52,8 +58,8 @@ pub fn remove_class_def_base(
return match (fix_start, fix_end) {
(Some(start), Some(end)) => Some(Fix {
content: "".to_string(),
start,
end,
location: start,
end_location: end,
applied: false,
}),
_ => None,
@@ -91,8 +97,8 @@ pub fn remove_class_def_base(
match (fix_start, fix_end) {
(Some(start), Some(end)) => Some(Fix {
content: "".to_string(),
start,
end,
location: start,
end_location: end,
applied: false,
}),
_ => None,
@@ -116,11 +122,271 @@ pub fn remove_class_def_base(
match (fix_start, fix_end) {
(Some(start), Some(end)) => Some(Fix {
content: "".to_string(),
start,
end,
location: start,
end_location: end,
applied: false,
}),
_ => None,
}
}
}
pub fn remove_super_arguments(locator: &mut SourceCodeLocator, expr: &Expr) -> Option<Fix> {
let range = Range::from_located(expr);
let contents = locator.slice_source_code_range(&range);
let mut tree = match libcst_native::parse_module(contents, None) {
Ok(m) => m,
Err(_) => return None,
};
if let Some(Statement::Simple(body)) = tree.body.first_mut() {
if let Some(SmallStatement::Expr(body)) = body.body.first_mut() {
if let Expression::Call(body) = &mut body.value {
body.args = vec![];
body.whitespace_before_args = Default::default();
body.whitespace_after_func = Default::default();
let mut state = Default::default();
tree.codegen(&mut state);
return Some(Fix {
content: state.to_string(),
location: range.location,
end_location: range.end_location,
applied: false,
});
}
}
}
None
}
/// Determine if a body contains only a single statement, taking into account deleted.
fn has_single_child(body: &[Stmt], deleted: &[&Stmt]) -> bool {
body.iter().filter(|child| !deleted.contains(child)).count() == 1
}
/// Determine if a child is the only statement in its body.
fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool> {
match &parent.node {
StmtKind::FunctionDef { body, .. }
| StmtKind::AsyncFunctionDef { body, .. }
| StmtKind::ClassDef { body, .. }
| StmtKind::With { body, .. }
| StmtKind::AsyncWith { body, .. } => {
if body.iter().contains(child) {
Ok(has_single_child(body, deleted))
} else {
Err(anyhow::anyhow!("Unable to find child in parent body."))
}
}
StmtKind::For { body, orelse, .. }
| StmtKind::AsyncFor { body, orelse, .. }
| StmtKind::While { body, orelse, .. }
| StmtKind::If { body, orelse, .. } => {
if body.iter().contains(child) {
Ok(has_single_child(body, deleted))
} else if orelse.iter().contains(child) {
Ok(has_single_child(orelse, deleted))
} else {
Err(anyhow::anyhow!("Unable to find child in parent body."))
}
}
StmtKind::Try {
body,
handlers,
orelse,
finalbody,
} => {
if body.iter().contains(child) {
Ok(has_single_child(body, deleted))
} else if orelse.iter().contains(child) {
Ok(has_single_child(orelse, deleted))
} else if finalbody.iter().contains(child) {
Ok(has_single_child(finalbody, deleted))
} else if let Some(body) = handlers.iter().find_map(|handler| match &handler.node {
ExcepthandlerKind::ExceptHandler { body, .. } => {
if body.iter().contains(child) {
Some(body)
} else {
None
}
}
}) {
Ok(has_single_child(body, deleted))
} else {
Err(anyhow::anyhow!("Unable to find child in parent body."))
}
}
_ => Err(anyhow::anyhow!("Unable to find child in parent body.")),
}
}
pub fn remove_stmt(stmt: &Stmt, parent: Option<&Stmt>, deleted: &[&Stmt]) -> Result<Fix> {
if parent
.map(|parent| is_lone_child(stmt, parent, deleted))
.map_or(Ok(None), |v| v.map(Some))?
.unwrap_or_default()
{
// If removing this node would lead to an invalid syntax tree, replace
// it with a `pass`.
Ok(Fix {
location: stmt.location,
end_location: stmt.end_location,
content: "pass".to_string(),
applied: false,
})
} else {
// Otherwise, nuke the entire line.
// TODO(charlie): This logic assumes that there are no multi-statement physical lines.
Ok(Fix {
location: Location::new(stmt.location.row(), 1),
end_location: Location::new(stmt.end_location.row() + 1, 1),
content: "".to_string(),
applied: false,
})
}
}
/// Generate a Fix to remove any unused imports from an `import` statement.
pub fn remove_unused_imports(
locator: &mut SourceCodeLocator,
full_names: &[&str],
stmt: &Stmt,
parent: Option<&Stmt>,
deleted: &[&Stmt],
) -> Result<Fix> {
let mut tree = match libcst_native::parse_module(
locator.slice_source_code_range(&Range::from_located(stmt)),
None,
) {
Ok(m) => m,
Err(_) => return Err(anyhow::anyhow!("Failed to extract CST from source.")),
};
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
body
} else {
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple."));
};
let body = if let Some(SmallStatement::Import(body)) = body.body.first_mut() {
body
} else {
return Err(anyhow::anyhow!(
"Expected node to be: SmallStatement::ImportFrom."
));
};
let aliases = &mut body.names;
// Preserve the trailing comma (or not) from the last entry.
let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone());
// Identify unused imports from within the `import from`.
let mut removable = vec![];
for (index, alias) in aliases.iter().enumerate() {
if let N(import_name) = &alias.name {
if full_names.contains(&import_name.value) {
removable.push(index);
}
}
}
// TODO(charlie): This is quadratic.
for index in removable.iter().rev() {
aliases.remove(*index);
}
if let Some(alias) = aliases.last_mut() {
alias.comma = trailing_comma;
}
if aliases.is_empty() {
remove_stmt(stmt, parent, deleted)
} else {
let mut state = Default::default();
tree.codegen(&mut state);
Ok(Fix {
content: state.to_string(),
location: stmt.location,
end_location: stmt.end_location,
applied: false,
})
}
}
/// Generate a Fix to remove any unused imports from an `import from` statement.
pub fn remove_unused_import_froms(
locator: &mut SourceCodeLocator,
full_names: &[&str],
stmt: &Stmt,
parent: Option<&Stmt>,
deleted: &[&Stmt],
) -> Result<Fix> {
let mut tree = match libcst_native::parse_module(
locator.slice_source_code_range(&Range::from_located(stmt)),
None,
) {
Ok(m) => m,
Err(_) => return Err(anyhow::anyhow!("Failed to extract CST from source.")),
};
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
body
} else {
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple."));
};
let body = if let Some(SmallStatement::ImportFrom(body)) = body.body.first_mut() {
body
} else {
return Err(anyhow::anyhow!(
"Expected node to be: SmallStatement::ImportFrom."
));
};
let aliases = if let Aliases(aliases) = &mut body.names {
aliases
} else {
return Err(anyhow::anyhow!("Expected node to be: Aliases."));
};
// Preserve the trailing comma (or not) from the last entry.
let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone());
// Identify unused imports from within the `import from`.
let mut removable = vec![];
for (index, alias) in aliases.iter().enumerate() {
if let N(name) = &alias.name {
let import_name = if let Some(N(module_name)) = &body.module {
format!("{}.{}", module_name.value, name.value)
} else {
name.value.to_string()
};
if full_names.contains(&import_name.as_str()) {
removable.push(index);
}
}
}
// TODO(charlie): This is quadratic.
for index in removable.iter().rev() {
aliases.remove(*index);
}
if let Some(alias) = aliases.last_mut() {
alias.comma = trailing_comma;
}
if aliases.is_empty() {
remove_stmt(stmt, parent, deleted)
} else {
let mut state = Default::default();
tree.codegen(&mut state);
Ok(Fix {
content: state.to_string(),
location: stmt.location,
end_location: stmt.end_location,
applied: false,
})
}
}

View File

@@ -1,18 +1,26 @@
use std::collections::{BTreeMap, BTreeSet};
use std::ops::Deref;
use std::path::Path;
use log::error;
use once_cell::sync::Lazy;
use regex::Regex;
use rustpython_parser::ast::{
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
KeywordData, Location, Operator, Stmt, StmtKind, Suite,
KeywordData, Operator, Stmt, StmtKind, Suite,
};
use rustpython_parser::parser;
use crate::ast::operations::{extract_all_names, SourceCodeLocator};
use crate::ast::relocate::relocate_expr;
use crate::ast::types::{Binding, BindingKind, CheckLocator, FunctionScope, Scope, ScopeKind};
use crate::ast::types::{
Binding, BindingContext, BindingKind, CheckLocator, FunctionScope, ImportKind, Range, Scope,
ScopeKind,
};
use crate::ast::visitor::{walk_excepthandler, Visitor};
use crate::ast::{checks, operations, visitor};
use crate::autofix::fixer;
use crate::autofix::fixes::remove_stmt;
use crate::autofix::{fixer, fixes};
use crate::checks::{Check, CheckCode, CheckKind};
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::python::future::ALL_FEATURE_NAMES;
@@ -21,6 +29,8 @@ use crate::settings::Settings;
pub const GLOBAL_SCOPE_INDEX: usize = 0;
static DUNDER_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"__[^\s]+__").unwrap());
struct Checker<'a> {
// Input data.
locator: SourceCodeLocator<'a>,
@@ -36,19 +46,21 @@ struct Checker<'a> {
scopes: Vec<Scope>,
scope_stack: Vec<usize>,
dead_scopes: Vec<usize>,
deferred_string_annotations: Vec<(Location, &'a str)>,
deferred_string_annotations: Vec<(Range, &'a str)>,
deferred_annotations: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
deferred_functions: Vec<(&'a Stmt, Vec<usize>, Vec<usize>)>,
deferred_lambdas: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
deferred_assignments: Vec<usize>,
// Derivative state.
in_f_string: Option<Location>,
in_f_string: Option<Range>,
in_annotation: bool,
in_literal: bool,
seen_non_import: bool,
seen_docstring: bool,
futures_allowed: bool,
annotations_future_enabled: bool,
// Edit tracking.
deletions: BTreeSet<usize>,
}
impl<'a> Checker<'a> {
@@ -81,6 +93,7 @@ impl<'a> Checker<'a> {
seen_docstring: false,
futures_allowed: true,
annotations_future_enabled: false,
deletions: Default::default(),
}
}
}
@@ -101,6 +114,36 @@ fn is_annotated_subscript(expr: &Expr) -> bool {
}
}
fn is_assignment_to_a_dunder(node: &StmtKind) -> bool {
// Check whether it's an assignment to a dunder, with or without a type annotation.
// This is what pycodestyle (as of 2.9.1) does.
match node {
StmtKind::Assign {
targets,
value: _,
type_comment: _,
} => {
if targets.len() != 1 {
return false;
}
match &targets[0].node {
ExprKind::Name { id, ctx: _ } => DUNDER_REGEX.is_match(id),
_ => false,
}
}
StmtKind::AnnAssign {
target,
annotation: _,
value: _,
simple: _,
} => match &target.node {
ExprKind::Name { id, ctx: _ } => DUNDER_REGEX.is_match(id),
_ => false,
},
_ => false,
}
}
impl<'a, 'b> Visitor<'b> for Checker<'a>
where
'b: 'a,
@@ -157,10 +200,11 @@ where
self.futures_allowed = false;
}
}
_ => {
node => {
self.futures_allowed = false;
if !self.seen_non_import
&& !is_assignment_to_a_dunder(node)
&& !operations::in_nested_block(&self.parent_stack, &self.parents)
{
self.seen_non_import = true;
@@ -183,8 +227,8 @@ where
name.to_string(),
Binding {
kind: BindingKind::Assignment,
used: Some((global_scope_id, stmt.location)),
location: stmt.location,
used: Some((global_scope_id, Range::from_located(stmt))),
range: Range::from_located(stmt),
},
);
}
@@ -192,7 +236,7 @@ where
}
if self.settings.select.contains(&CheckCode::E741) {
let location = self.locate_check(stmt.location);
let location = self.locate_check(Range::from_located(stmt));
self.checks.extend(
names.iter().filter_map(|name| {
checks::check_ambiguous_variable_name(name, location)
@@ -241,14 +285,15 @@ where
if self.settings.select.contains(&CheckCode::E743) {
if let Some(check) = checks::check_ambiguous_function_name(
name,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
) {
self.checks.push(check);
}
}
self.check_builtin_shadowing(name, stmt.location, true);
self.check_builtin_shadowing(name, Range::from_located(stmt), true);
// Visit the decorators and arguments, but avoid the body, which will be deferred.
for expr in decorator_list {
self.visit_expr(expr);
}
@@ -291,7 +336,7 @@ where
Binding {
kind: BindingKind::Definition,
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
);
}
@@ -306,7 +351,7 @@ where
ScopeKind::Class | ScopeKind::Module => {
self.checks.push(Check::new(
CheckKind::ReturnOutsideFunction,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
_ => {}
@@ -338,14 +383,19 @@ where
}
if self.settings.select.contains(&CheckCode::E742) {
if let Some(check) =
checks::check_ambiguous_class_name(name, self.locate_check(stmt.location))
{
if let Some(check) = checks::check_ambiguous_class_name(
name,
self.locate_check(Range::from_located(stmt)),
) {
self.checks.push(check);
}
}
self.check_builtin_shadowing(name, self.locate_check(stmt.location), false);
self.check_builtin_shadowing(
name,
self.locate_check(Range::from_located(stmt)),
false,
);
for expr in bases {
self.visit_expr(expr)
@@ -368,7 +418,7 @@ where
{
self.checks.push(Check::new(
CheckKind::ModuleImportNotAtTopOfFile,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
@@ -381,14 +431,15 @@ where
Binding {
kind: BindingKind::SubmoduleImportation(
alias.node.name.to_string(),
self.binding_context(),
),
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
)
} else {
if let Some(asname) = &alias.node.asname {
self.check_builtin_shadowing(asname, stmt.location, false);
self.check_builtin_shadowing(asname, Range::from_located(stmt), false);
}
self.add_binding(
@@ -404,9 +455,10 @@ where
.asname
.clone()
.unwrap_or_else(|| alias.node.name.clone()),
self.binding_context(),
),
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
)
}
@@ -426,7 +478,7 @@ where
{
self.checks.push(Check::new(
CheckKind::ModuleImportNotAtTopOfFile,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
@@ -447,9 +499,9 @@ where
.last()
.expect("No current scope found."))]
.id,
stmt.location,
Range::from_located(stmt),
)),
location: stmt.location,
range: Range::from_located(stmt),
},
);
@@ -462,7 +514,7 @@ where
{
self.checks.push(Check::new(
CheckKind::FutureFeatureNotDefined(alias.node.name.to_string()),
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
@@ -470,7 +522,7 @@ where
{
self.checks.push(Check::new(
CheckKind::LateFutureImport,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
} else if alias.node.name == "*" {
@@ -485,7 +537,7 @@ where
Binding {
kind: BindingKind::StarImportation,
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
);
@@ -495,7 +547,7 @@ where
if !matches!(scope.kind, ScopeKind::Module) {
self.checks.push(Check::new(
CheckKind::ImportStarNotPermitted(module_name.to_string()),
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
}
@@ -503,7 +555,7 @@ where
if self.settings.select.contains(&CheckCode::F403) {
self.checks.push(Check::new(
CheckKind::ImportStarUsed(module_name.to_string()),
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
@@ -514,16 +566,19 @@ where
scope.import_starred = true;
} else {
if let Some(asname) = &alias.node.asname {
self.check_builtin_shadowing(asname, stmt.location, false);
self.check_builtin_shadowing(asname, Range::from_located(stmt), false);
}
let binding = Binding {
kind: BindingKind::Importation(match module {
None => name.clone(),
Some(parent) => format!("{}.{}", parent, name),
}),
kind: BindingKind::FromImportation(
match module {
None => name.clone(),
Some(parent) => format!("{}.{}", parent, name),
},
self.binding_context(),
),
used: None,
location: stmt.location,
range: Range::from_located(stmt),
};
self.add_binding(name, binding)
}
@@ -544,7 +599,7 @@ where
StmtKind::If { test, .. } => {
if self.settings.select.contains(&CheckCode::F634) {
if let Some(check) =
checks::check_if_tuple(test, self.locate_check(stmt.location))
checks::check_if_tuple(test, self.locate_check(Range::from_located(stmt)))
{
self.checks.push(check);
}
@@ -552,9 +607,10 @@ where
}
StmtKind::Assert { test, .. } => {
if self.settings.select.contains(CheckKind::AssertTuple.code()) {
if let Some(check) =
checks::check_assert_tuple(test, self.locate_check(stmt.location))
{
if let Some(check) = checks::check_assert_tuple(
test,
self.locate_check(Range::from_located(stmt)),
) {
self.checks.push(check);
}
}
@@ -568,9 +624,10 @@ where
}
StmtKind::Assign { value, .. } => {
if self.settings.select.contains(&CheckCode::E731) {
if let Some(check) =
checks::check_do_not_assign_lambda(value, self.locate_check(stmt.location))
{
if let Some(check) = checks::check_do_not_assign_lambda(
value,
self.locate_check(Range::from_located(stmt)),
) {
self.checks.push(check);
}
}
@@ -580,7 +637,7 @@ where
if let Some(value) = value {
if let Some(check) = checks::check_do_not_assign_lambda(
value,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
) {
self.checks.push(check);
}
@@ -616,7 +673,7 @@ where
Binding {
kind: BindingKind::ClassDefinition,
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
);
};
@@ -663,7 +720,7 @@ where
elts,
check_too_many_expressions,
check_two_starred_expressions,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
) {
self.checks.push(check);
}
@@ -675,13 +732,13 @@ where
if self.settings.select.contains(&CheckCode::E741) {
if let Some(check) = checks::check_ambiguous_variable_name(
id,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
) {
self.checks.push(check);
}
}
self.check_builtin_shadowing(id, expr.location, true);
self.check_builtin_shadowing(id, Range::from_located(expr), true);
let parent =
self.parents[*(self.parent_stack.last().expect("No parent found."))];
@@ -698,7 +755,46 @@ where
// flake8-super
if self.settings.select.contains(&CheckCode::SPR001) {
if let Some(check) = checks::check_super_args(func, args) {
if let Some(check) =
checks::check_super_args(expr, func, args, &mut self.locator, self.autofix)
{
self.checks.push(check)
}
}
// flake8-print
if self.settings.select.contains(&CheckCode::T201)
|| self.settings.select.contains(&CheckCode::T203)
{
if let Some(mut check) = checks::check_print_call(expr, func) {
if matches!(self.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
let context = self.binding_context();
if matches!(
self.parents[context.defined_by].node,
StmtKind::Expr { .. }
) {
let deleted: Vec<&Stmt> = self
.deletions
.iter()
.map(|index| self.parents[*index])
.collect();
match remove_stmt(
self.parents[context.defined_by],
context.defined_in.map(|index| self.parents[index]),
&deleted,
) {
Ok(fix) => {
if fix.content.is_empty() || fix.content == "pass" {
self.deletions.insert(context.defined_by);
}
check.amend(fix)
}
Err(e) => error!("Failed to fix unused imports: {}", e),
}
}
}
self.checks.push(check)
}
}
@@ -741,7 +837,7 @@ where
{
self.checks.push(Check::new(
CheckKind::YieldOutsideFunction,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
));
}
}
@@ -757,10 +853,10 @@ where
{
self.checks.push(Check::new(
CheckKind::FStringMissingPlaceholders,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
));
}
self.in_f_string = Some(expr.location);
self.in_f_string = Some(Range::from_located(expr));
}
ExprKind::BinOp {
left,
@@ -777,8 +873,10 @@ where
..
}) = scope.values.get("print")
{
self.checks
.push(Check::new(CheckKind::InvalidPrintSyntax, left.location));
self.checks.push(Check::new(
CheckKind::InvalidPrintSyntax,
Range::from_located(left),
));
}
}
}
@@ -820,7 +918,7 @@ where
left,
ops,
comparators,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
));
}
@@ -828,7 +926,7 @@ where
self.checks.extend(checks::check_type_comparison(
ops,
comparators,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
));
}
}
@@ -837,7 +935,41 @@ where
..
} if self.in_annotation && !self.in_literal => {
self.deferred_string_annotations
.push((expr.location, value));
.push((Range::from_located(expr), value));
}
ExprKind::Lambda { args, .. } => {
// Visit the arguments, but avoid the body, which will be deferred.
for arg in &args.posonlyargs {
if let Some(expr) = &arg.node.annotation {
self.visit_annotation(expr);
}
}
for arg in &args.args {
if let Some(expr) = &arg.node.annotation {
self.visit_annotation(expr);
}
}
if let Some(arg) = &args.vararg {
if let Some(expr) = &arg.node.annotation {
self.visit_annotation(expr);
}
}
for arg in &args.kwonlyargs {
if let Some(expr) = &arg.node.annotation {
self.visit_annotation(expr);
}
}
if let Some(arg) = &args.kwarg {
if let Some(expr) = &arg.node.annotation {
self.visit_annotation(expr);
}
}
for expr in &args.kw_defaults {
self.visit_expr(expr);
}
for expr in &args.defaults {
self.visit_expr(expr);
}
}
ExprKind::GeneratorExp { .. }
| ExprKind::ListComp { .. }
@@ -987,7 +1119,7 @@ where
if self.settings.select.contains(&CheckCode::E722) && type_.is_none() {
self.checks.push(Check::new(
CheckKind::DoNotUseBareExcept,
excepthandler.location,
Range::from_located(excepthandler),
));
}
match name {
@@ -995,13 +1127,17 @@ where
if self.settings.select.contains(&CheckCode::E741) {
if let Some(check) = checks::check_ambiguous_variable_name(
name,
self.locate_check(excepthandler.location),
self.locate_check(Range::from_located(excepthandler)),
) {
self.checks.push(check);
}
}
self.check_builtin_shadowing(name, excepthandler.location, false);
self.check_builtin_shadowing(
name,
Range::from_located(excepthandler),
false,
);
let scope = &self.scopes
[*(self.scope_stack.last().expect("No current scope found."))];
@@ -1011,6 +1147,7 @@ where
self.handle_node_store(
&Expr::new(
excepthandler.location,
excepthandler.end_location,
ExprKind::Name {
id: name.to_string(),
ctx: ExprContext::Store,
@@ -1018,7 +1155,6 @@ where
),
parent,
);
self.parents.push(parent);
}
let parent =
@@ -1029,6 +1165,7 @@ where
self.handle_node_store(
&Expr::new(
excepthandler.location,
excepthandler.end_location,
ExprKind::Name {
id: name.to_string(),
ctx: ExprContext::Store,
@@ -1036,7 +1173,6 @@ where
),
parent,
);
self.parents.push(parent);
walk_excepthandler(self, excepthandler);
@@ -1048,7 +1184,7 @@ where
{
self.checks.push(Check::new(
CheckKind::UnusedVariable(name.to_string()),
excepthandler.location,
Range::from_located(excepthandler),
));
}
}
@@ -1094,25 +1230,25 @@ where
Binding {
kind: BindingKind::Argument,
used: None,
location: arg.location,
range: Range::from_located(arg),
},
);
if self.settings.select.contains(&CheckCode::E741) {
if let Some(check) = checks::check_ambiguous_variable_name(
&arg.node.arg,
self.locate_check(arg.location),
self.locate_check(Range::from_located(arg)),
) {
self.checks.push(check);
}
}
self.check_builtin_arg_shadowing(&arg.node.arg, arg.location);
self.check_builtin_arg_shadowing(&arg.node.arg, Range::from_located(arg));
}
}
impl CheckLocator for Checker<'_> {
fn locate_check(&self, default: Location) -> Location {
fn locate_check(&self, default: Range) -> Range {
self.in_f_string.unwrap_or(default)
}
}
@@ -1150,7 +1286,7 @@ impl<'a> Checker<'a> {
(*builtin).to_string(),
Binding {
kind: BindingKind::Builtin,
location: Default::default(),
range: Default::default(),
used: None,
},
);
@@ -1160,13 +1296,23 @@ impl<'a> Checker<'a> {
(*builtin).to_string(),
Binding {
kind: BindingKind::Builtin,
location: Default::default(),
range: Default::default(),
used: None,
},
);
}
}
fn binding_context(&self) -> BindingContext {
let mut rev = self.parent_stack.iter().rev().fuse();
let defined_by = *rev.next().expect("Expected to bind within a statement.");
let defined_in = rev.next().cloned();
BindingContext {
defined_by,
defined_in,
}
}
fn add_binding(&mut self, name: String, binding: Binding) {
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
@@ -1175,17 +1321,23 @@ impl<'a> Checker<'a> {
None => binding,
Some(existing) => {
if self.settings.select.contains(&CheckCode::F402)
&& matches!(existing.kind, BindingKind::Importation(_))
&& matches!(binding.kind, BindingKind::LoopVar)
&& matches!(
existing.kind,
BindingKind::Importation(_, _) | BindingKind::FromImportation(_, _)
)
{
self.checks.push(Check::new(
CheckKind::ImportShadowedByLoopVar(name.clone(), existing.location.row()),
binding.location,
CheckKind::ImportShadowedByLoopVar(
name.clone(),
existing.range.location.row(),
),
binding.range,
));
}
Binding {
kind: binding.kind,
location: binding.location,
range: binding.range,
used: existing.used,
}
}
@@ -1212,7 +1364,7 @@ impl<'a> Checker<'a> {
}
}
if let Some(binding) = scope.values.get_mut(id) {
binding.used = Some((scope_id, expr.location));
binding.used = Some((scope_id, Range::from_located(expr)));
return;
}
@@ -1236,7 +1388,7 @@ impl<'a> Checker<'a> {
self.checks.push(Check::new(
CheckKind::ImportStarUsage(id.clone(), from_list.join(", ")),
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
));
}
return;
@@ -1249,7 +1401,7 @@ impl<'a> Checker<'a> {
}
self.checks.push(Check::new(
CheckKind::UndefinedName(id.clone()),
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
))
}
}
@@ -1286,7 +1438,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::Annotation,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
return;
@@ -1302,7 +1454,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::LoopVar,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
return;
@@ -1314,7 +1466,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::Binding,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
return;
@@ -1334,7 +1486,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::Export(extract_all_names(parent, current)),
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
return;
@@ -1345,7 +1497,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::Assignment,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
}
@@ -1364,7 +1516,7 @@ impl<'a> Checker<'a> {
{
self.checks.push(Check::new(
CheckKind::UndefinedName(id.clone()),
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
))
}
}
@@ -1479,7 +1631,7 @@ impl<'a> Checker<'a> {
if !scope.values.contains_key(name) {
self.checks.push(Check::new(
CheckKind::UndefinedExport(name.to_string()),
self.locate_check(all_binding.location),
self.locate_check(all_binding.range),
));
}
}
@@ -1502,7 +1654,7 @@ impl<'a> Checker<'a> {
if !scope.values.contains_key(name) {
self.checks.push(Check::new(
CheckKind::ImportStarUsage(name.clone(), from_list.join(", ")),
self.locate_check(all_binding.location),
self.locate_check(all_binding.range),
));
}
}
@@ -1511,6 +1663,11 @@ impl<'a> Checker<'a> {
}
if self.settings.select.contains(&CheckCode::F401) {
// Collect all unused imports by location. (Multiple unused imports at the same
// location indicates an `import from`.)
let mut unused: BTreeMap<(ImportKind, usize, Option<usize>), Vec<&str>> =
BTreeMap::new();
for (name, binding) in scope.values.iter().rev() {
let used = binding.used.is_some()
|| all_names
@@ -1519,35 +1676,83 @@ impl<'a> Checker<'a> {
if !used {
match &binding.kind {
BindingKind::Importation(full_name)
| BindingKind::SubmoduleImportation(full_name) => {
self.checks.push(Check::new(
CheckKind::UnusedImport(full_name.to_string()),
self.locate_check(binding.location),
));
BindingKind::FromImportation(full_name, context) => {
let full_names = unused
.entry((
ImportKind::ImportFrom,
context.defined_by,
context.defined_in,
))
.or_insert(vec![]);
full_names.push(full_name);
}
BindingKind::Importation(full_name, context)
| BindingKind::SubmoduleImportation(full_name, context) => {
let full_names = unused
.entry((
ImportKind::Import,
context.defined_by,
context.defined_in,
))
.or_insert(vec![]);
full_names.push(full_name);
}
_ => {}
}
}
}
for ((kind, defined_by, defined_in), full_names) in unused {
let child = self.parents[defined_by];
let parent = defined_in.map(|defined_in| self.parents[defined_in]);
let mut check = Check::new(
CheckKind::UnusedImport(full_names.join(", ")),
self.locate_check(Range::from_located(child)),
);
if matches!(self.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
let deleted: Vec<&Stmt> = self
.deletions
.iter()
.map(|index| self.parents[*index])
.collect();
let removal_fn = match kind {
ImportKind::Import => fixes::remove_unused_imports,
ImportKind::ImportFrom => fixes::remove_unused_import_froms,
};
match removal_fn(&mut self.locator, &full_names, child, parent, &deleted) {
Ok(fix) => {
if fix.content.is_empty() || fix.content == "pass" {
self.deletions.insert(defined_by);
}
check.amend(fix)
}
Err(e) => error!("Failed to fix unused imports: {}", e),
}
}
self.checks.push(check);
}
}
}
}
fn check_builtin_shadowing(&mut self, name: &str, location: Location, is_attribute: bool) {
fn check_builtin_shadowing(&mut self, name: &str, location: Range, is_attribute: bool) {
let scope = &self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
// flake8-builtins
if is_attribute
&& matches!(scope.kind, ScopeKind::Class)
&& self.settings.select.contains(&CheckCode::A003)
{
if let Some(check) = checks::check_builtin_shadowing(
name,
self.locate_check(location),
checks::ShadowingType::Attribute,
) {
self.checks.push(check);
if is_attribute && matches!(scope.kind, ScopeKind::Class) {
if self.settings.select.contains(&CheckCode::A003) {
if let Some(check) = checks::check_builtin_shadowing(
name,
self.locate_check(location),
checks::ShadowingType::Attribute,
) {
self.checks.push(check);
}
}
} else if self.settings.select.contains(&CheckCode::A001) {
if let Some(check) = checks::check_builtin_shadowing(
@@ -1560,7 +1765,7 @@ impl<'a> Checker<'a> {
}
}
fn check_builtin_arg_shadowing(&mut self, name: &str, location: Location) {
fn check_builtin_arg_shadowing(&mut self, name: &str, location: Range) {
if self.settings.select.contains(&CheckCode::A002) {
if let Some(check) = checks::check_builtin_shadowing(
name,
@@ -1575,12 +1780,12 @@ impl<'a> Checker<'a> {
pub fn check_ast(
python_ast: &Suite,
content: &str,
contents: &str,
settings: &Settings,
autofix: &fixer::Mode,
path: &Path,
) -> Vec<Check> {
let mut checker = Checker::new(settings, autofix, path, content);
let mut checker = Checker::new(settings, autofix, path, contents);
checker.push_scope(Scope::new(ScopeKind::Module));
checker.bind_builtins();

View File

@@ -1,5 +1,6 @@
use std::collections::BTreeMap;
use crate::ast::types::Range;
use rustpython_parser::ast::Location;
use crate::autofix::fixer;
@@ -64,11 +65,11 @@ pub fn check_lines(
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
match noqa {
(Directive::All(_), matches) => {
(Directive::All(_, _), matches) => {
matches.push(check.kind.code().as_str());
ignored.push(index)
}
(Directive::Codes(_, codes), matches) => {
(Directive::Codes(_, _, codes), matches) => {
if codes.contains(&check.kind.code().as_str()) {
matches.push(check.kind.code().as_str());
ignored.push(index);
@@ -89,14 +90,17 @@ pub fn check_lines(
let check = Check::new(
CheckKind::LineTooLong(line_length, settings.line_length),
Location::new(lineno + 1, settings.line_length + 1),
Range {
location: Location::new(lineno + 1, 1),
end_location: Location::new(lineno + 1, line_length + 1),
},
);
match noqa {
(Directive::All(_), matches) => {
(Directive::All(_, _), matches) => {
matches.push(check.kind.code().as_str());
}
(Directive::Codes(_, codes), matches) => {
(Directive::Codes(_, _, codes), matches) => {
if codes.contains(&check.kind.code().as_str()) {
matches.push(check.kind.code().as_str());
} else {
@@ -113,24 +117,30 @@ pub fn check_lines(
if enforce_noqa {
for (row, (directive, matches)) in noqa_directives {
match directive {
Directive::All(column) => {
Directive::All(start, end) => {
if matches.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(None),
Location::new(row + 1, column + 1),
Range {
location: Location::new(row + 1, start + 1),
end_location: Location::new(row + 1, end + 1),
},
);
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
check.amend(Fix {
content: "".to_string(),
start: Location::new(row + 1, column + 1),
end: Location::new(row + 1, lines[row].chars().count() + 1),
location: Location::new(row + 1, start + 1),
end_location: Location::new(
row + 1,
lines[row].chars().count() + 1,
),
applied: false,
});
}
line_checks.push(check);
}
}
Directive::Codes(column, codes) => {
Directive::Codes(start, end, codes) => {
let mut invalid_codes = vec![];
let mut valid_codes = vec![];
for code in codes {
@@ -144,21 +154,30 @@ pub fn check_lines(
if !invalid_codes.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(Some(invalid_codes.join(", "))),
Location::new(row + 1, column + 1),
Range {
location: Location::new(row + 1, start + 1),
end_location: Location::new(row + 1, end + 1),
},
);
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
if valid_codes.is_empty() {
check.amend(Fix {
content: "".to_string(),
start: Location::new(row + 1, column + 1),
end: Location::new(row + 1, lines[row].chars().count() + 1),
location: Location::new(row + 1, start + 1),
end_location: Location::new(
row + 1,
lines[row].chars().count() + 1,
),
applied: false,
});
} else {
check.amend(Fix {
content: format!(" # noqa: {}", valid_codes.join(", ")),
start: Location::new(row + 1, column + 1),
end: Location::new(row + 1, lines[row].chars().count() + 1),
location: Location::new(row + 1, start + 1),
end_location: Location::new(
row + 1,
lines[row].chars().count() + 1,
),
applied: false,
});
}

View File

@@ -1,10 +1,11 @@
use std::str::FromStr;
use crate::ast::types::Range;
use anyhow::Result;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
pub const DEFAULT_CHECK_CODES: [CheckCode; 46] = [
pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [
// pycodestyle
CheckCode::E402,
CheckCode::E501,
@@ -49,15 +50,9 @@ pub const DEFAULT_CHECK_CODES: [CheckCode; 46] = [
CheckCode::F831,
CheckCode::F841,
CheckCode::F901,
// flake8-builtins
CheckCode::A001,
CheckCode::A002,
CheckCode::A003,
// flake8-super
CheckCode::SPR001,
];
pub const ALL_CHECK_CODES: [CheckCode; 49] = [
pub const ALL_CHECK_CODES: [CheckCode; 51] = [
// pycodestyle
CheckCode::E402,
CheckCode::E501,
@@ -108,6 +103,9 @@ pub const ALL_CHECK_CODES: [CheckCode; 49] = [
CheckCode::A003,
// flake8-super
CheckCode::SPR001,
// flake8-print
CheckCode::T201,
CheckCode::T203,
// Meta
CheckCode::M001,
// Refactor
@@ -167,6 +165,9 @@ pub enum CheckCode {
A003,
// flake8-super
SPR001,
// flake8-print
T201,
T203,
// Refactor
R001,
R002,
@@ -292,6 +293,9 @@ impl CheckCode {
CheckCode::A003 => "A003",
// flake8-super
CheckCode::SPR001 => "SPR001",
// flake8-print
CheckCode::T201 => "T201",
CheckCode::T203 => "T203",
// Refactor
CheckCode::R001 => "R001",
CheckCode::R002 => "R002",
@@ -362,6 +366,9 @@ impl CheckCode {
CheckCode::A003 => CheckKind::BuiltinAttributeShadowing("...".to_string()),
// flake8-super
CheckCode::SPR001 => CheckKind::SuperCallWithParameters,
// flake8-print
CheckCode::T201 => CheckKind::PrintFound,
CheckCode::T203 => CheckKind::PPrintFound,
// Refactor
CheckCode::R001 => CheckKind::UselessObjectInheritance("...".to_string()),
CheckCode::R002 => CheckKind::NoAssertEquals,
@@ -437,6 +444,9 @@ pub enum CheckKind {
BuiltinAttributeShadowing(String),
// flake8-super
SuperCallWithParameters,
// flake8-print
PrintFound,
PPrintFound,
}
impl CheckKind {
@@ -496,6 +506,9 @@ impl CheckKind {
CheckKind::BuiltinAttributeShadowing(_) => "BuiltinAttributeShadowing",
// flake8-super
CheckKind::SuperCallWithParameters => "SuperCallWithParameters",
// flake8-print
CheckKind::PrintFound => "PrintFound",
CheckKind::PPrintFound => "PPrintFound",
}
}
@@ -553,6 +566,9 @@ impl CheckKind {
CheckKind::BuiltinAttributeShadowing(_) => &CheckCode::A003,
// flake8-super
CheckKind::SuperCallWithParameters => &CheckCode::SPR001,
// flake8-print
CheckKind::PrintFound => &CheckCode::T201,
CheckKind::PPrintFound => &CheckCode::T203,
}
}
@@ -701,6 +717,9 @@ impl CheckKind {
CheckKind::SuperCallWithParameters => {
"Use `super()` instead of `super(__class__, self)`".to_string()
}
// flake8-print
CheckKind::PrintFound => "`print` found".to_string(),
CheckKind::PPrintFound => "`pprint` found`".to_string(),
}
}
@@ -711,6 +730,10 @@ impl CheckKind {
CheckKind::NoAssertEquals
| CheckKind::UselessObjectInheritance(_)
| CheckKind::UnusedNOQA(_)
| CheckKind::SuperCallWithParameters
| CheckKind::UnusedImport(_)
| CheckKind::PrintFound
| CheckKind::PPrintFound
)
}
}
@@ -718,8 +741,8 @@ impl CheckKind {
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Fix {
pub content: String,
pub start: Location,
pub end: Location,
pub location: Location,
pub end_location: Location,
pub applied: bool,
}
@@ -727,14 +750,16 @@ pub struct Fix {
pub struct Check {
pub kind: CheckKind,
pub location: Location,
pub end_location: Location,
pub fix: Option<Fix>,
}
impl Check {
pub fn new(kind: CheckKind, location: Location) -> Self {
pub fn new(kind: CheckKind, span: Range) -> Self {
Self {
kind,
location,
location: span.location,
end_location: span.end_location,
fix: None,
}
}

View File

@@ -1,3 +1,14 @@
use std::path::Path;
use anyhow::Result;
use log::debug;
use rustpython_parser::lexer::LexResult;
use crate::autofix::fixer::Mode;
use crate::linter::{check_path, tokenize};
use crate::message::Message;
use crate::settings::Settings;
mod ast;
mod autofix;
pub mod cache;
@@ -13,3 +24,50 @@ pub mod printer;
pub mod pyproject;
mod python;
pub mod settings;
/// Run ruff over Python source code directly.
pub fn check(path: &Path, contents: &str) -> Result<Vec<Message>> {
// Find the project root and pyproject.toml.
let project_root = pyproject::find_project_root(&[path.to_path_buf()]);
match &project_root {
Some(path) => debug!("Found project root at: {:?}", path),
None => debug!("Unable to identify project root; assuming current directory..."),
};
let pyproject = pyproject::find_pyproject_toml(&project_root);
match &pyproject {
Some(path) => debug!("Found pyproject.toml at: {:?}", path),
None => debug!("Unable to find pyproject.toml; using default settings..."),
};
let settings = Settings::from_pyproject(pyproject, project_root)?;
// Tokenize once.
let tokens: Vec<LexResult> = tokenize(contents);
// Determine the noqa line for every line in the source.
let noqa_line_for = noqa::extract_noqa_line_for(&tokens);
// Generate checks.
let checks = check_path(
path,
contents,
tokens,
&noqa_line_for,
&settings,
&Mode::None,
)?;
// Convert to messages.
let messages: Vec<Message> = checks
.into_iter()
.map(|check| Message {
kind: check.kind,
fixed: check.fix.map(|fix| fix.applied).unwrap_or_default(),
location: check.location,
end_location: check.end_location,
filename: path.to_string_lossy().to_string(),
})
.collect();
Ok(messages)
}

View File

@@ -5,6 +5,7 @@ use log::debug;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::{lexer, parser};
use crate::ast::types::Range;
use crate::autofix::fixer;
use crate::autofix::fixer::fix_file;
use crate::check_ast::check_ast;
@@ -16,7 +17,7 @@ use crate::settings::Settings;
use crate::{cache, fs, noqa};
/// Collect tokens up to and including the first error.
fn tokenize(contents: &str) -> Vec<LexResult> {
pub(crate) fn tokenize(contents: &str) -> Vec<LexResult> {
let mut tokens: Vec<LexResult> = vec![];
for tok in lexer::make_tokenizer(contents) {
let is_err = tok.is_err();
@@ -28,7 +29,7 @@ fn tokenize(contents: &str) -> Vec<LexResult> {
tokens
}
fn check_path(
pub(crate) fn check_path(
path: &Path,
contents: &str,
tokens: Vec<LexResult>,
@@ -53,7 +54,10 @@ fn check_path(
if settings.select.contains(&CheckCode::E999) {
checks.push(Check::new(
CheckKind::SyntaxError(parse_error.error.to_string()),
parse_error.location,
Range {
location: parse_error.location,
end_location: parse_error.location,
},
))
}
}
@@ -115,6 +119,7 @@ pub fn lint_path(
kind: check.kind,
fixed: check.fix.map(|fix| fix.applied).unwrap_or_default(),
location: check.location,
end_location: check.end_location,
filename: path.to_string_lossy().to_string(),
})
.collect();
@@ -774,4 +779,28 @@ mod tests {
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn t201() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/T201.py"),
&settings::Settings::for_rule(CheckCode::T201),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn t203() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/T203.py"),
&settings::Settings::for_rule(CheckCode::T203),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
}

View File

@@ -23,9 +23,9 @@ use ::ruff::logging::set_up_logging;
use ::ruff::message::Message;
use ::ruff::printer::{Printer, SerializationFormat};
use ::ruff::pyproject::{self, StrCheckCodePair};
use ::ruff::settings::CurrentSettings;
use ::ruff::settings::{FilePattern, PerFileIgnore, Settings};
use ::ruff::tell_user;
use ruff::settings::CurrentSettings;
const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME");
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -170,6 +170,7 @@ fn run_once(
kind: CheckKind::IOError(message),
fixed: false,
location: Default::default(),
end_location: Default::default(),
filename: path.to_string_lossy().to_string(),
}]
} else {

View File

@@ -14,6 +14,7 @@ pub struct Message {
pub kind: CheckKind,
pub fixed: bool,
pub location: Location,
pub end_location: Location,
pub filename: String,
}

View File

@@ -19,8 +19,8 @@ static SPLIT_COMMA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").expect
#[derive(Debug)]
pub enum Directive<'a> {
None,
All(usize),
Codes(usize, Vec<&'a str>),
All(usize, usize),
Codes(usize, usize, Vec<&'a str>),
}
pub fn extract_noqa_directive(line: &str) -> Directive {
@@ -29,13 +29,14 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
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()),
None => Directive::All(noqa.start(), noqa.end()),
},
None => Directive::None,
},
@@ -46,10 +47,9 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
pub fn extract_noqa_line_for(lxr: &[LexResult]) -> Vec<usize> {
let mut noqa_line_for: Vec<usize> = vec![];
let mut last_is_string = false;
let mut last_seen = usize::MIN;
let mut min_line = usize::MAX;
let mut max_line = usize::MIN;
let mut in_string = false;
for (start, tok, end) in lxr.iter().flatten() {
if matches!(tok, Tok::EndOfFile) {
@@ -61,29 +61,20 @@ pub fn extract_noqa_line_for(lxr: &[LexResult]) -> Vec<usize> {
max_line = max(max_line, start.row());
// For now, we only care about preserving noqa directives across multi-line strings.
if last_is_string {
noqa_line_for.extend(vec![max_line; (max_line + 1) - min_line]);
} else {
for i in (min_line - 1)..(max_line) {
if in_string {
for i in (noqa_line_for.len())..(min_line - 1) {
noqa_line_for.push(i + 1);
}
noqa_line_for.extend(vec![max_line; (max_line + 1) - min_line]);
}
min_line = usize::MAX;
max_line = usize::MIN;
} else {
// Handle empty lines.
if start.row() > last_seen {
for i in last_seen..(start.row() - 1) {
noqa_line_for.push(i + 1);
}
}
min_line = min(min_line, start.row());
max_line = max(max_line, end.row());
}
last_seen = start.row();
last_is_string = matches!(tok, Tok::String { .. });
in_string = matches!(tok, Tok::String { .. });
}
noqa_line_for
@@ -133,8 +124,8 @@ fn add_noqa_inner(
Directive::None => {
output.push_str(line);
}
Directive::All(start) => output.push_str(&line[..start]),
Directive::Codes(start, _) => output.push_str(&line[..start]),
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_str()).collect();
output.push_str(" # noqa: ");
@@ -161,6 +152,7 @@ pub fn add_noqa(
#[cfg(test)]
mod tests {
use crate::ast::types::Range;
use anyhow::Result;
use rustpython_parser::ast::Location;
use rustpython_parser::lexer;
@@ -171,6 +163,8 @@ mod tests {
#[test]
fn extraction() -> Result<()> {
let empty: Vec<usize> = Default::default();
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"x = 1
y = 2
@@ -178,7 +172,7 @@ z = x + 1",
)
.collect();
println!("{:?}", extract_noqa_line_for(&lxr));
assert_eq!(extract_noqa_line_for(&lxr), vec![1, 2, 3]);
assert_eq!(extract_noqa_line_for(&lxr), empty);
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"
@@ -188,7 +182,7 @@ z = x + 1",
)
.collect();
println!("{:?}", extract_noqa_line_for(&lxr));
assert_eq!(extract_noqa_line_for(&lxr), vec![1, 2, 3, 4]);
assert_eq!(extract_noqa_line_for(&lxr), empty);
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"x = 1
@@ -198,7 +192,7 @@ z = x + 1
)
.collect();
println!("{:?}", extract_noqa_line_for(&lxr));
assert_eq!(extract_noqa_line_for(&lxr), vec![1, 2, 3]);
assert_eq!(extract_noqa_line_for(&lxr), empty);
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"x = 1
@@ -209,7 +203,7 @@ z = x + 1
)
.collect();
println!("{:?}", extract_noqa_line_for(&lxr));
assert_eq!(extract_noqa_line_for(&lxr), vec![1, 2, 3, 4]);
assert_eq!(extract_noqa_line_for(&lxr), empty);
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"x = '''abc
@@ -220,7 +214,28 @@ y = 2
z = x + 1",
)
.collect();
assert_eq!(extract_noqa_line_for(&lxr), vec![4, 4, 4, 4, 5, 6]);
assert_eq!(extract_noqa_line_for(&lxr), vec![4, 4, 4, 4]);
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"x = 1
y = '''abc
def
ghi
'''
z = 2",
)
.collect();
assert_eq!(extract_noqa_line_for(&lxr), vec![1, 5, 5, 5, 5]);
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"x = 1
y = '''abc
def
ghi
'''",
)
.collect();
assert_eq!(extract_noqa_line_for(&lxr), vec![1, 5, 5, 5, 5]);
Ok(())
}
@@ -236,7 +251,10 @@ z = x + 1",
let checks = vec![Check::new(
CheckKind::UnusedVariable("x".to_string()),
Location::new(1, 1),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
},
)];
let contents = "x = 1";
let noqa_line_for = vec![1];
@@ -247,11 +265,17 @@ z = x + 1",
let checks = vec![
Check::new(
CheckKind::AmbiguousVariableName("x".to_string()),
Location::new(1, 1),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
},
),
Check::new(
CheckKind::UnusedVariable("x".to_string()),
Location::new(1, 1),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
},
),
];
let contents = "x = 1 # noqa: E741";
@@ -263,11 +287,17 @@ z = x + 1",
let checks = vec![
Check::new(
CheckKind::AmbiguousVariableName("x".to_string()),
Location::new(1, 1),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
},
),
Check::new(
CheckKind::UnusedVariable("x".to_string()),
Location::new(1, 1),
Range {
location: Location::new(1, 1),
end_location: Location::new(1, 1),
},
),
];
let contents = "x = 1 # noqa";

View File

@@ -21,6 +21,7 @@ struct ExpandedMessage<'a> {
message: String,
fixed: bool,
location: Location,
end_location: Location,
filename: &'a String,
}
@@ -55,6 +56,7 @@ impl Printer {
message: m.kind.body(),
fixed: m.fixed,
location: m.location,
end_location: m.end_location,
filename: &m.filename,
})
.collect::<Vec<_>>()

View File

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

View File

@@ -7,40 +7,62 @@ expression: checks
location:
row: 1
column: 11
end_location:
row: 1
column: 14
fix: ~
- kind:
BuiltinArgumentShadowing: type
location:
row: 1
column: 19
end_location:
row: 1
column: 23
fix: ~
- kind:
BuiltinArgumentShadowing: complex
location:
row: 1
column: 26
end_location:
row: 1
column: 33
fix: ~
- kind:
BuiltinArgumentShadowing: Exception
location:
row: 1
column: 35
end_location:
row: 1
column: 44
fix: ~
- kind:
BuiltinArgumentShadowing: getattr
location:
row: 1
column: 48
end_location:
row: 1
column: 55
fix: ~
- kind:
BuiltinArgumentShadowing: bytes
location:
row: 5
column: 17
end_location:
row: 5
column: 22
fix: ~
- kind:
BuiltinArgumentShadowing: float
location:
row: 9
column: 16
end_location:
row: 9
column: 21
fix: ~

View File

@@ -1,6 +1,5 @@
---
source: src/linter.rs
assertion_line: 762
expression: checks
---
- kind:
@@ -8,11 +7,17 @@ expression: checks
location:
row: 2
column: 5
end_location:
row: 2
column: 16
fix: ~
- kind:
BuiltinAttributeShadowing: str
location:
row: 7
column: 5
end_location:
row: 9
column: 1
fix: ~

View File

@@ -4,7 +4,10 @@ expression: checks
---
- kind: ModuleImportNotAtTopOfFile
location:
row: 20
row: 24
column: 1
end_location:
row: 24
column: 9
fix: ~

View File

@@ -8,6 +8,9 @@ expression: checks
- 88
location:
row: 5
column: 89
column: 1
end_location:
row: 5
column: 124
fix: ~

View File

@@ -7,47 +7,71 @@ expression: checks
location:
row: 2
column: 11
end_location:
row: 2
column: 15
fix: ~
- kind:
NoneComparison: NotEq
location:
row: 5
column: 11
end_location:
row: 5
column: 15
fix: ~
- kind:
NoneComparison: Eq
location:
row: 8
column: 4
end_location:
row: 8
column: 8
fix: ~
- kind:
NoneComparison: NotEq
location:
row: 11
column: 4
end_location:
row: 11
column: 8
fix: ~
- kind:
NoneComparison: Eq
location:
row: 14
column: 14
end_location:
row: 14
column: 18
fix: ~
- kind:
NoneComparison: NotEq
location:
row: 17
column: 14
end_location:
row: 17
column: 18
fix: ~
- kind:
NoneComparison: NotEq
location:
row: 20
column: 4
end_location:
row: 20
column: 8
fix: ~
- kind:
NoneComparison: Eq
location:
row: 23
column: 4
end_location:
row: 23
column: 8
fix: ~

View File

@@ -9,6 +9,9 @@ expression: checks
location:
row: 2
column: 11
end_location:
row: 2
column: 15
fix: ~
- kind:
TrueFalseComparison:
@@ -17,6 +20,9 @@ expression: checks
location:
row: 5
column: 11
end_location:
row: 5
column: 16
fix: ~
- kind:
TrueFalseComparison:
@@ -25,6 +31,9 @@ expression: checks
location:
row: 8
column: 4
end_location:
row: 8
column: 8
fix: ~
- kind:
TrueFalseComparison:
@@ -33,6 +42,9 @@ expression: checks
location:
row: 11
column: 4
end_location:
row: 11
column: 9
fix: ~
- kind:
TrueFalseComparison:
@@ -41,6 +53,9 @@ expression: checks
location:
row: 14
column: 14
end_location:
row: 14
column: 18
fix: ~
- kind:
TrueFalseComparison:
@@ -49,6 +64,9 @@ expression: checks
location:
row: 17
column: 14
end_location:
row: 17
column: 19
fix: ~
- kind:
TrueFalseComparison:
@@ -57,6 +75,9 @@ expression: checks
location:
row: 20
column: 20
end_location:
row: 20
column: 24
fix: ~
- kind:
TrueFalseComparison:
@@ -65,6 +86,9 @@ expression: checks
location:
row: 20
column: 44
end_location:
row: 20
column: 49
fix: ~
- kind:
TrueFalseComparison:
@@ -73,5 +97,8 @@ expression: checks
location:
row: 22
column: 5
end_location:
row: 22
column: 9
fix: ~

View File

@@ -6,25 +6,40 @@ expression: checks
location:
row: 2
column: 10
end_location:
row: 2
column: 14
fix: ~
- kind: NotInTest
location:
row: 5
column: 12
end_location:
row: 5
column: 16
fix: ~
- kind: NotInTest
location:
row: 8
column: 10
end_location:
row: 8
column: 14
fix: ~
- kind: NotInTest
location:
row: 11
column: 25
end_location:
row: 11
column: 29
fix: ~
- kind: NotInTest
location:
row: 14
column: 11
end_location:
row: 14
column: 15
fix: ~

View File

@@ -6,15 +6,24 @@ expression: checks
location:
row: 2
column: 10
end_location:
row: 2
column: 14
fix: ~
- kind: NotIsTest
location:
row: 5
column: 12
end_location:
row: 5
column: 16
fix: ~
- kind: NotIsTest
location:
row: 8
column: 10
end_location:
row: 8
column: 23
fix: ~

View File

@@ -6,80 +6,128 @@ expression: checks
location:
row: 2
column: 14
end_location:
row: 2
column: 25
fix: ~
- kind: TypeComparison
location:
row: 5
column: 14
end_location:
row: 5
column: 25
fix: ~
- kind: TypeComparison
location:
row: 10
column: 8
end_location:
row: 10
column: 24
fix: ~
- kind: TypeComparison
location:
row: 15
column: 14
end_location:
row: 15
column: 35
fix: ~
- kind: TypeComparison
location:
row: 18
column: 18
end_location:
row: 18
column: 32
fix: ~
- kind: TypeComparison
location:
row: 18
column: 46
end_location:
row: 18
column: 59
fix: ~
- kind: TypeComparison
location:
row: 20
column: 18
end_location:
row: 20
column: 29
fix: ~
- kind: TypeComparison
location:
row: 22
column: 18
end_location:
row: 22
column: 29
fix: ~
- kind: TypeComparison
location:
row: 24
column: 18
end_location:
row: 24
column: 31
fix: ~
- kind: TypeComparison
location:
row: 26
column: 18
end_location:
row: 26
column: 30
fix: ~
- kind: TypeComparison
location:
row: 28
column: 18
end_location:
row: 28
column: 31
fix: ~
- kind: TypeComparison
location:
row: 30
column: 18
end_location:
row: 30
column: 31
fix: ~
- kind: TypeComparison
location:
row: 32
column: 18
end_location:
row: 32
column: 35
fix: ~
- kind: TypeComparison
location:
row: 34
column: 18
end_location:
row: 38
column: 2
fix: ~
- kind: TypeComparison
location:
row: 40
column: 18
end_location:
row: 40
column: 29
fix: ~
- kind: TypeComparison
location:
row: 42
column: 18
end_location:
row: 42
column: 31
fix: ~

View File

@@ -6,15 +6,24 @@ expression: checks
location:
row: 4
column: 1
end_location:
row: 7
column: 1
fix: ~
- kind: DoNotUseBareExcept
location:
row: 11
column: 1
end_location:
row: 14
column: 1
fix: ~
- kind: DoNotUseBareExcept
location:
row: 16
column: 1
end_location:
row: 19
column: 1
fix: ~

View File

@@ -6,25 +6,40 @@ expression: checks
location:
row: 2
column: 1
end_location:
row: 2
column: 20
fix: ~
- kind: DoNotAssignLambda
location:
row: 4
column: 1
end_location:
row: 4
column: 20
fix: ~
- kind: DoNotAssignLambda
location:
row: 7
column: 5
end_location:
row: 7
column: 30
fix: ~
- kind: DoNotAssignLambda
location:
row: 12
column: 1
end_location:
row: 12
column: 28
fix: ~
- kind: DoNotAssignLambda
location:
row: 16
column: 1
end_location:
row: 16
column: 26
fix: ~

View File

@@ -7,149 +7,224 @@ expression: checks
location:
row: 3
column: 1
end_location:
row: 3
column: 2
fix: ~
- kind:
AmbiguousVariableName: I
location:
row: 4
column: 1
end_location:
row: 4
column: 2
fix: ~
- kind:
AmbiguousVariableName: O
location:
row: 5
column: 1
end_location:
row: 5
column: 2
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 6
column: 1
end_location:
row: 6
column: 2
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 8
column: 4
end_location:
row: 8
column: 5
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 9
column: 5
end_location:
row: 9
column: 6
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 10
column: 5
end_location:
row: 10
column: 6
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 11
column: 5
end_location:
row: 11
column: 6
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 16
column: 5
end_location:
row: 16
column: 6
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 20
column: 8
end_location:
row: 20
column: 9
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 25
column: 5
end_location:
row: 25
column: 13
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 26
column: 5
end_location:
row: 26
column: 6
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 30
column: 5
end_location:
row: 30
column: 6
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 33
column: 9
end_location:
row: 33
column: 19
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 34
column: 9
end_location:
row: 34
column: 10
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 40
column: 8
end_location:
row: 40
column: 9
fix: ~
- kind:
AmbiguousVariableName: I
location:
row: 40
column: 14
end_location:
row: 40
column: 15
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 44
column: 8
end_location:
row: 44
column: 9
fix: ~
- kind:
AmbiguousVariableName: I
location:
row: 44
column: 16
end_location:
row: 44
column: 17
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 48
column: 9
end_location:
row: 48
column: 10
fix: ~
- kind:
AmbiguousVariableName: I
location:
row: 48
column: 14
end_location:
row: 48
column: 15
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 57
column: 16
end_location:
row: 57
column: 17
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 66
column: 20
end_location:
row: 66
column: 21
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 71
column: 1
end_location:
row: 74
column: 1
fix: ~
- kind:
AmbiguousVariableName: l
location:
row: 74
column: 5
end_location:
row: 74
column: 11
fix: ~

View File

@@ -7,17 +7,26 @@ expression: checks
location:
row: 1
column: 1
end_location:
row: 5
column: 1
fix: ~
- kind:
AmbiguousClassName: I
location:
row: 5
column: 1
end_location:
row: 9
column: 1
fix: ~
- kind:
AmbiguousClassName: O
location:
row: 9
column: 1
end_location:
row: 13
column: 1
fix: ~

View File

@@ -7,17 +7,26 @@ expression: checks
location:
row: 1
column: 1
end_location:
row: 5
column: 1
fix: ~
- kind:
AmbiguousFunctionName: I
location:
row: 5
column: 1
end_location:
row: 9
column: 1
fix: ~
- kind:
AmbiguousFunctionName: O
location:
row: 10
column: 5
end_location:
row: 14
column: 1
fix: ~

View File

@@ -7,5 +7,8 @@ expression: checks
location:
row: 2
column: 1
end_location:
row: 2
column: 1
fix: ~

View File

@@ -5,19 +5,120 @@ expression: checks
- kind:
UnusedImport: functools
location:
row: 3
row: 2
column: 1
fix: ~
end_location:
row: 2
column: 21
fix:
content: import os
location:
row: 2
column: 1
end_location:
row: 2
column: 21
applied: false
- kind:
UnusedImport: collections.OrderedDict
location:
row: 5
row: 4
column: 1
fix: ~
end_location:
row: 8
column: 2
fix:
content: "from collections import (\n Counter,\n namedtuple,\n)"
location:
row: 4
column: 1
end_location:
row: 8
column: 2
applied: false
- kind:
UnusedImport: logging.handlers
location:
row: 13
row: 12
column: 1
fix: ~
end_location:
row: 12
column: 24
fix:
content: import logging.handlers
location:
row: 12
column: 1
end_location:
row: 12
column: 24
applied: false
- kind:
UnusedImport: shelve
location:
row: 33
column: 5
end_location:
row: 33
column: 18
fix:
content: ""
location:
row: 33
column: 1
end_location:
row: 34
column: 1
applied: false
- kind:
UnusedImport: importlib
location:
row: 34
column: 5
end_location:
row: 34
column: 21
fix:
content: pass
location:
row: 34
column: 5
end_location:
row: 34
column: 21
applied: false
- kind:
UnusedImport: pathlib
location:
row: 38
column: 5
end_location:
row: 38
column: 19
fix:
content: ""
location:
row: 38
column: 1
end_location:
row: 39
column: 1
applied: false
- kind:
UnusedImport: pickle
location:
row: 53
column: 9
end_location:
row: 53
column: 22
fix:
content: pass
location:
row: 53
column: 9
end_location:
row: 53
column: 22
applied: false

View File

@@ -9,6 +9,9 @@ expression: checks
location:
row: 5
column: 5
end_location:
row: 5
column: 7
fix: ~
- kind:
ImportShadowedByLoopVar:
@@ -17,5 +20,8 @@ expression: checks
location:
row: 8
column: 5
end_location:
row: 8
column: 9
fix: ~

View File

@@ -7,11 +7,17 @@ expression: checks
location:
row: 1
column: 1
end_location:
row: 1
column: 19
fix: ~
- kind:
ImportStarUsed: F634
location:
row: 2
column: 1
end_location:
row: 2
column: 19
fix: ~

View File

@@ -6,5 +6,8 @@ expression: checks
location:
row: 7
column: 1
end_location:
row: 7
column: 38
fix: ~

View File

@@ -9,6 +9,9 @@ expression: checks
location:
row: 5
column: 11
end_location:
row: 5
column: 15
fix: ~
- kind:
ImportStarUsage:
@@ -17,5 +20,8 @@ expression: checks
location:
row: 11
column: 1
end_location:
row: 11
column: 8
fix: ~

View File

@@ -7,11 +7,17 @@ expression: checks
location:
row: 5
column: 5
end_location:
row: 5
column: 23
fix: ~
- kind:
ImportStarNotPermitted: F634
location:
row: 9
column: 5
end_location:
row: 9
column: 23
fix: ~

View File

@@ -7,5 +7,8 @@ expression: checks
location:
row: 2
column: 1
end_location:
row: 2
column: 44
fix: ~

View File

@@ -6,15 +6,24 @@ expression: checks
location:
row: 4
column: 7
end_location:
row: 4
column: 11
fix: ~
- kind: FStringMissingPlaceholders
location:
row: 5
column: 7
end_location:
row: 5
column: 11
fix: ~
- kind: FStringMissingPlaceholders
location:
row: 7
column: 7
end_location:
row: 7
column: 11
fix: ~

View File

@@ -6,15 +6,24 @@ expression: checks
location:
row: 3
column: 6
end_location:
row: 3
column: 8
fix: ~
- kind: MultiValueRepeatedKeyLiteral
location:
row: 9
column: 5
end_location:
row: 9
column: 6
fix: ~
- kind: MultiValueRepeatedKeyLiteral
location:
row: 11
column: 7
end_location:
row: 11
column: 11
fix: ~

View File

@@ -7,5 +7,8 @@ expression: checks
location:
row: 5
column: 5
end_location:
row: 5
column: 6
fix: ~

View File

@@ -6,5 +6,8 @@ expression: checks
location:
row: 1
column: 1
end_location:
row: 1
column: 10
fix: ~

View File

@@ -6,10 +6,16 @@ expression: checks
location:
row: 1
column: 1
end_location:
row: 1
column: 20
fix: ~
- kind: AssertTuple
location:
row: 2
column: 1
end_location:
row: 2
column: 16
fix: ~

View File

@@ -6,10 +6,16 @@ expression: checks
location:
row: 1
column: 6
end_location:
row: 1
column: 14
fix: ~
- kind: IsLiteral
location:
row: 4
column: 8
end_location:
row: 4
column: 16
fix: ~

View File

@@ -6,5 +6,8 @@ expression: checks
location:
row: 4
column: 1
end_location:
row: 4
column: 6
fix: ~

View File

@@ -6,10 +6,16 @@ expression: checks
location:
row: 1
column: 1
end_location:
row: 4
column: 1
fix: ~
- kind: IfTuple
location:
row: 7
column: 5
end_location:
row: 9
column: 5
fix: ~

View File

@@ -6,20 +6,32 @@ expression: checks
location:
row: 4
column: 5
end_location:
row: 4
column: 10
fix: ~
- kind: BreakOutsideLoop
location:
row: 16
column: 5
end_location:
row: 16
column: 10
fix: ~
- kind: BreakOutsideLoop
location:
row: 20
column: 5
end_location:
row: 20
column: 10
fix: ~
- kind: BreakOutsideLoop
location:
row: 23
column: 1
end_location:
row: 23
column: 6
fix: ~

View File

@@ -6,20 +6,32 @@ expression: checks
location:
row: 4
column: 5
end_location:
row: 4
column: 13
fix: ~
- kind: ContinueOutsideLoop
location:
row: 16
column: 5
end_location:
row: 16
column: 13
fix: ~
- kind: ContinueOutsideLoop
location:
row: 20
column: 5
end_location:
row: 20
column: 13
fix: ~
- kind: ContinueOutsideLoop
location:
row: 23
column: 1
end_location:
row: 23
column: 9
fix: ~

View File

@@ -6,15 +6,24 @@ expression: checks
location:
row: 6
column: 5
end_location:
row: 6
column: 12
fix: ~
- kind: YieldOutsideFunction
location:
row: 9
column: 1
end_location:
row: 9
column: 8
fix: ~
- kind: YieldOutsideFunction
location:
row: 10
column: 1
end_location:
row: 10
column: 13
fix: ~

View File

@@ -6,10 +6,16 @@ expression: checks
location:
row: 6
column: 5
end_location:
row: 6
column: 13
fix: ~
- kind: ReturnOutsideFunction
location:
row: 9
column: 1
end_location:
row: 9
column: 9
fix: ~

View File

@@ -6,15 +6,24 @@ expression: checks
location:
row: 3
column: 1
end_location:
row: 5
column: 1
fix: ~
- kind: DefaultExceptNotLast
location:
row: 10
column: 1
end_location:
row: 12
column: 1
fix: ~
- kind: DefaultExceptNotLast
location:
row: 19
column: 1
end_location:
row: 21
column: 1
fix: ~

View File

@@ -7,5 +7,8 @@ expression: checks
location:
row: 9
column: 13
end_location:
row: 9
column: 17
fix: ~

View File

@@ -7,47 +7,71 @@ expression: checks
location:
row: 2
column: 12
end_location:
row: 2
column: 16
fix: ~
- kind:
UndefinedName: self
location:
row: 6
column: 13
end_location:
row: 6
column: 17
fix: ~
- kind:
UndefinedName: self
location:
row: 10
column: 9
end_location:
row: 10
column: 13
fix: ~
- kind:
UndefinedName: numeric_string
location:
row: 21
column: 12
end_location:
row: 21
column: 26
fix: ~
- kind:
UndefinedName: Bar
location:
row: 58
column: 5
end_location:
row: 58
column: 9
fix: ~
- kind:
UndefinedName: TOMATO
location:
row: 83
column: 11
end_location:
row: 83
column: 17
fix: ~
- kind:
UndefinedName: B
location:
row: 87
column: 7
end_location:
row: 87
column: 11
fix: ~
- kind:
UndefinedName: B
location:
row: 89
column: 7
end_location:
row: 89
column: 9
fix: ~

View File

@@ -7,5 +7,8 @@ expression: checks
location:
row: 3
column: 1
end_location:
row: 3
column: 8
fix: ~

View File

@@ -7,5 +7,8 @@ expression: checks
location:
row: 6
column: 5
end_location:
row: 6
column: 11
fix: ~

View File

@@ -6,15 +6,24 @@ expression: checks
location:
row: 1
column: 25
end_location:
row: 1
column: 31
fix: ~
- kind: DuplicateArgumentName
location:
row: 5
column: 28
end_location:
row: 5
column: 34
fix: ~
- kind: DuplicateArgumentName
location:
row: 9
column: 27
end_location:
row: 9
column: 33
fix: ~

View File

@@ -7,29 +7,44 @@ expression: checks
location:
row: 3
column: 1
end_location:
row: 7
column: 1
fix: ~
- kind:
UnusedVariable: z
location:
row: 16
column: 5
end_location:
row: 16
column: 6
fix: ~
- kind:
UnusedVariable: foo
location:
row: 20
column: 5
end_location:
row: 20
column: 8
fix: ~
- kind:
UnusedVariable: a
location:
row: 21
column: 6
end_location:
row: 21
column: 7
fix: ~
- kind:
UnusedVariable: b
location:
row: 21
column: 9
end_location:
row: 21
column: 10
fix: ~

View File

@@ -7,41 +7,62 @@ expression: checks
location:
row: 3
column: 1
end_location:
row: 7
column: 1
fix: ~
- kind:
UnusedVariable: foo
location:
row: 20
column: 5
end_location:
row: 20
column: 8
fix: ~
- kind:
UnusedVariable: a
location:
row: 21
column: 6
end_location:
row: 21
column: 7
fix: ~
- kind:
UnusedVariable: b
location:
row: 21
column: 9
end_location:
row: 21
column: 10
fix: ~
- kind:
UnusedVariable: _
location:
row: 35
column: 5
end_location:
row: 35
column: 6
fix: ~
- kind:
UnusedVariable: __
location:
row: 36
column: 5
end_location:
row: 36
column: 7
fix: ~
- kind:
UnusedVariable: _discarded
location:
row: 37
column: 5
end_location:
row: 37
column: 15
fix: ~

View File

@@ -5,11 +5,17 @@ expression: checks
- kind: RaiseNotImplemented
location:
row: 2
column: 25
column: 11
end_location:
row: 2
column: 27
fix: ~
- kind: RaiseNotImplemented
location:
row: 6
column: 11
end_location:
row: 6
column: 25
fix: ~

View File

@@ -7,11 +7,25 @@ expression: checks
location:
row: 5
column: 1
fix: ~
end_location:
row: 8
column: 2
fix:
content: "from models import (\n Fruit,\n)"
location:
row: 5
column: 1
end_location:
row: 8
column: 2
applied: false
- kind:
UndefinedName: Bar
location:
row: 22
row: 25
column: 19
end_location:
row: 25
column: 22
fix: ~

View File

@@ -7,12 +7,15 @@ expression: checks
location:
row: 9
column: 10
end_location:
row: 9
column: 18
fix:
content: ""
start:
location:
row: 9
column: 10
end:
end_location:
row: 9
column: 18
applied: false
@@ -21,12 +24,15 @@ expression: checks
location:
row: 13
column: 10
end_location:
row: 13
column: 24
fix:
content: ""
start:
location:
row: 13
column: 10
end:
end_location:
row: 13
column: 24
applied: false
@@ -35,12 +41,15 @@ expression: checks
location:
row: 16
column: 10
end_location:
row: 16
column: 30
fix:
content: " # noqa: F841"
start:
location:
row: 16
column: 10
end:
end_location:
row: 16
column: 30
applied: false
@@ -49,12 +58,15 @@ expression: checks
location:
row: 41
column: 4
end_location:
row: 41
column: 24
fix:
content: " # noqa: E501"
start:
location:
row: 41
column: 4
end:
end_location:
row: 41
column: 24
applied: false
@@ -63,12 +75,15 @@ expression: checks
location:
row: 49
column: 4
end_location:
row: 49
column: 18
fix:
content: ""
start:
location:
row: 49
column: 4
end:
end_location:
row: 49
column: 18
applied: false
@@ -77,12 +92,15 @@ expression: checks
location:
row: 57
column: 4
end_location:
row: 57
column: 12
fix:
content: ""
start:
location:
row: 57
column: 4
end:
end_location:
row: 57
column: 12
applied: false

View File

@@ -7,12 +7,15 @@ expression: checks
location:
row: 5
column: 9
end_location:
row: 5
column: 15
fix:
content: ""
start:
location:
row: 5
column: 8
end:
end_location:
row: 5
column: 16
applied: false
@@ -21,12 +24,15 @@ expression: checks
location:
row: 10
column: 5
end_location:
row: 10
column: 11
fix:
content: ""
start:
location:
row: 9
column: 8
end:
end_location:
row: 11
column: 2
applied: false
@@ -35,12 +41,15 @@ expression: checks
location:
row: 16
column: 5
end_location:
row: 16
column: 11
fix:
content: ""
start:
location:
row: 15
column: 8
end:
end_location:
row: 18
column: 2
applied: false
@@ -49,12 +58,15 @@ expression: checks
location:
row: 24
column: 5
end_location:
row: 24
column: 11
fix:
content: ""
start:
location:
row: 22
column: 8
end:
end_location:
row: 25
column: 2
applied: false
@@ -63,12 +75,15 @@ expression: checks
location:
row: 31
column: 5
end_location:
row: 31
column: 11
fix:
content: ""
start:
location:
row: 29
column: 8
end:
end_location:
row: 32
column: 2
applied: false
@@ -77,12 +92,15 @@ expression: checks
location:
row: 37
column: 5
end_location:
row: 37
column: 11
fix:
content: ""
start:
location:
row: 36
column: 8
end:
end_location:
row: 39
column: 2
applied: false
@@ -91,12 +109,15 @@ expression: checks
location:
row: 45
column: 5
end_location:
row: 45
column: 11
fix:
content: ""
start:
location:
row: 43
column: 8
end:
end_location:
row: 47
column: 2
applied: false
@@ -105,12 +126,15 @@ expression: checks
location:
row: 53
column: 5
end_location:
row: 53
column: 11
fix:
content: ""
start:
location:
row: 51
column: 8
end:
end_location:
row: 55
column: 2
applied: false
@@ -119,12 +143,15 @@ expression: checks
location:
row: 61
column: 5
end_location:
row: 61
column: 11
fix:
content: ""
start:
location:
row: 59
column: 8
end:
end_location:
row: 63
column: 2
applied: false
@@ -133,12 +160,15 @@ expression: checks
location:
row: 69
column: 5
end_location:
row: 69
column: 11
fix:
content: ""
start:
location:
row: 67
column: 8
end:
end_location:
row: 71
column: 2
applied: false
@@ -147,12 +177,15 @@ expression: checks
location:
row: 75
column: 12
end_location:
row: 75
column: 18
fix:
content: ""
start:
location:
row: 75
column: 10
end:
end_location:
row: 75
column: 18
applied: false
@@ -161,12 +194,15 @@ expression: checks
location:
row: 79
column: 9
end_location:
row: 79
column: 15
fix:
content: ""
start:
location:
row: 79
column: 9
end:
end_location:
row: 79
column: 17
applied: false
@@ -175,12 +211,15 @@ expression: checks
location:
row: 84
column: 5
end_location:
row: 84
column: 11
fix:
content: ""
start:
location:
row: 84
column: 5
end:
end_location:
row: 85
column: 5
applied: false
@@ -189,12 +228,15 @@ expression: checks
location:
row: 92
column: 5
end_location:
row: 92
column: 11
fix:
content: ""
start:
location:
row: 91
column: 6
end:
end_location:
row: 92
column: 11
applied: false
@@ -203,12 +245,15 @@ expression: checks
location:
row: 98
column: 5
end_location:
row: 98
column: 11
fix:
content: ""
start:
location:
row: 98
column: 5
end:
end_location:
row: 100
column: 5
applied: false
@@ -217,12 +262,15 @@ expression: checks
location:
row: 108
column: 5
end_location:
row: 108
column: 11
fix:
content: ""
start:
location:
row: 107
column: 6
end:
end_location:
row: 108
column: 11
applied: false
@@ -231,12 +279,15 @@ expression: checks
location:
row: 114
column: 13
end_location:
row: 114
column: 19
fix:
content: ""
start:
location:
row: 114
column: 12
end:
end_location:
row: 114
column: 20
applied: false
@@ -245,12 +296,15 @@ expression: checks
location:
row: 119
column: 5
end_location:
row: 119
column: 11
fix:
content: ""
start:
location:
row: 118
column: 8
end:
end_location:
row: 120
column: 2
applied: false
@@ -259,12 +313,15 @@ expression: checks
location:
row: 125
column: 5
end_location:
row: 125
column: 11
fix:
content: ""
start:
location:
row: 124
column: 8
end:
end_location:
row: 126
column: 2
applied: false
@@ -273,12 +330,15 @@ expression: checks
location:
row: 131
column: 5
end_location:
row: 131
column: 11
fix:
content: ""
start:
location:
row: 130
column: 8
end:
end_location:
row: 133
column: 2
applied: false

View File

@@ -5,27 +5,33 @@ expression: checks
- kind: NoAssertEquals
location:
row: 1
column: 5
column: 1
end_location:
row: 1
column: 18
fix:
content: assertEqual
start:
location:
row: 1
column: 6
end:
column: 2
end_location:
row: 1
column: 18
column: 14
applied: false
- kind: NoAssertEquals
location:
row: 2
column: 5
column: 1
end_location:
row: 2
column: 18
fix:
content: assertEqual
start:
location:
row: 2
column: 6
end:
column: 2
end_location:
row: 2
column: 18
column: 14
applied: false

View File

@@ -6,14 +6,48 @@ expression: checks
location:
row: 17
column: 18
fix: ~
end_location:
row: 17
column: 36
fix:
content: super()
location:
row: 17
column: 18
end_location:
row: 17
column: 36
applied: false
- kind: SuperCallWithParameters
location:
row: 18
column: 9
fix: ~
end_location:
row: 18
column: 27
fix:
content: super()
location:
row: 18
column: 9
end_location:
row: 18
column: 27
applied: false
- kind: SuperCallWithParameters
location:
row: 19
column: 9
fix: ~
end_location:
row: 22
column: 10
fix:
content: super()
location:
row: 19
column: 9
end_location:
row: 22
column: 10
applied: false

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: PrintFound
location:
row: 1
column: 1
end_location:
row: 1
column: 23
fix:
content: ""
location:
row: 1
column: 1
end_location:
row: 2
column: 1
applied: false

View File

@@ -0,0 +1,37 @@
---
source: src/linter.rs
expression: checks
---
- kind: PPrintFound
location:
row: 3
column: 1
end_location:
row: 3
column: 24
fix:
content: ""
location:
row: 3
column: 1
end_location:
row: 4
column: 1
applied: false
- kind: PPrintFound
location:
row: 8
column: 1
end_location:
row: 8
column: 31
fix:
content: ""
location:
row: 8
column: 1
end_location:
row: 9
column: 1
applied: false