Compare commits

...

11 Commits

Author SHA1 Message Date
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
70 changed files with 1772 additions and 313 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.51"
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.51"
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

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

View File

@@ -11,14 +11,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 +50,9 @@ class X:
y = Counter()
z = multiprocessing.pool.ThreadPool()
def b(self) -> None:
import pickle
__all__ = ["ClassA"] + ["ClassB"]
__all__ += ["ClassC"]
@@ -39,3 +64,5 @@ Z = TypeVar("Z", "List", "Set")
a = list["Fruit"]
b = Union["Nut", None]
c = cast("Vegetable", b)
Field = lambda default=MISSING: field(default=default)

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,26 @@ 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
}

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,210 @@ 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.")),
}
}
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(stmt: &Stmt, parent: Option<&Stmt>, deleted: &[&Stmt]) -> Result<Fix> {
remove_stmt(stmt, parent, deleted)
}
/// Generate a Fix to remove any unused imports from an `import from` statement.
pub fn remove_unused_import_froms(
locator: &mut SourceCodeLocator,
full_names: &[String],
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) {
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,25 @@
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::{fixer, fixes};
use crate::checks::{Check, CheckCode, CheckKind};
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::python::future::ALL_FEATURE_NAMES;
@@ -21,6 +28,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 +45,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 +92,7 @@ impl<'a> Checker<'a> {
seen_docstring: false,
futures_allowed: true,
annotations_future_enabled: false,
deletions: Default::default(),
}
}
}
@@ -101,6 +113,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 +199,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 +226,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 +235,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 +284,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 +335,7 @@ where
Binding {
kind: BindingKind::Definition,
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
);
}
@@ -306,7 +350,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 +382,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 +417,7 @@ where
{
self.checks.push(Check::new(
CheckKind::ModuleImportNotAtTopOfFile,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
@@ -381,14 +430,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 +454,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 +477,7 @@ where
{
self.checks.push(Check::new(
CheckKind::ModuleImportNotAtTopOfFile,
self.locate_check(stmt.location),
self.locate_check(Range::from_located(stmt)),
));
}
@@ -447,9 +498,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 +513,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 +521,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 +536,7 @@ where
Binding {
kind: BindingKind::StarImportation,
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
);
@@ -495,7 +546,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 +554,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 +565,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 +598,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 +606,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 +623,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 +636,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 +672,7 @@ where
Binding {
kind: BindingKind::ClassDefinition,
used: None,
location: stmt.location,
range: Range::from_located(stmt),
},
);
};
@@ -663,7 +719,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 +731,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 +754,9 @@ 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)
}
}
@@ -741,7 +799,7 @@ where
{
self.checks.push(Check::new(
CheckKind::YieldOutsideFunction,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
));
}
}
@@ -757,10 +815,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 +835,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 +880,7 @@ where
left,
ops,
comparators,
self.locate_check(expr.location),
self.locate_check(Range::from_located(expr)),
));
}
@@ -828,7 +888,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 +897,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 +1081,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 +1089,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 +1109,7 @@ where
self.handle_node_store(
&Expr::new(
excepthandler.location,
excepthandler.end_location,
ExprKind::Name {
id: name.to_string(),
ctx: ExprContext::Store,
@@ -1018,7 +1117,6 @@ where
),
parent,
);
self.parents.push(parent);
}
let parent =
@@ -1029,6 +1127,7 @@ where
self.handle_node_store(
&Expr::new(
excepthandler.location,
excepthandler.end_location,
ExprKind::Name {
id: name.to_string(),
ctx: ExprContext::Store,
@@ -1036,7 +1135,6 @@ where
),
parent,
);
self.parents.push(parent);
walk_excepthandler(self, excepthandler);
@@ -1048,7 +1146,7 @@ where
{
self.checks.push(Check::new(
CheckKind::UnusedVariable(name.to_string()),
excepthandler.location,
Range::from_located(excepthandler),
));
}
}
@@ -1094,25 +1192,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 +1248,7 @@ impl<'a> Checker<'a> {
(*builtin).to_string(),
Binding {
kind: BindingKind::Builtin,
location: Default::default(),
range: Default::default(),
used: None,
},
);
@@ -1160,13 +1258,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 +1283,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 +1326,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 +1350,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 +1363,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 +1400,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::Annotation,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
return;
@@ -1302,7 +1416,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::LoopVar,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
return;
@@ -1314,7 +1428,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::Binding,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
return;
@@ -1334,7 +1448,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 +1459,7 @@ impl<'a> Checker<'a> {
Binding {
kind: BindingKind::Assignment,
used: None,
location: expr.location,
range: Range::from_located(expr),
},
);
}
@@ -1364,7 +1478,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 +1593,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 +1616,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 +1625,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<String>> =
BTreeMap::new();
for (name, binding) in scope.values.iter().rev() {
let used = binding.used.is_some()
|| all_names
@@ -1519,35 +1638,99 @@ 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.to_string());
}
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.to_string());
}
_ => {}
}
}
}
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();
match kind {
ImportKind::Import => {
match fixes::remove_unused_imports(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),
}
}
ImportKind::ImportFrom => {
match fixes::remove_unused_import_froms(
&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 +1743,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 +1758,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,5 +1,6 @@
use std::str::FromStr;
use crate::ast::types::Range;
use anyhow::Result;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
@@ -711,6 +712,8 @@ impl CheckKind {
CheckKind::NoAssertEquals
| CheckKind::UselessObjectInheritance(_)
| CheckKind::UnusedNOQA(_)
| CheckKind::SuperCallWithParameters
| CheckKind::UnusedImport(_)
)
}
}
@@ -718,8 +721,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 +730,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();

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,
},
@@ -133,8 +134,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 +162,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;
@@ -236,7 +238,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 +252,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 +274,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

@@ -7,17 +7,118 @@ expression: checks
location:
row: 3
column: 1
fix: ~
end_location:
row: 3
column: 17
fix:
content: ""
location:
row: 3
column: 1
end_location:
row: 4
column: 1
applied: false
- kind:
UnusedImport: collections.OrderedDict
location:
row: 5
column: 1
fix: ~
end_location:
row: 9
column: 2
fix:
content: "from collections import (\n Counter,\n namedtuple,\n)"
location:
row: 5
column: 1
end_location:
row: 9
column: 2
applied: false
- kind:
UnusedImport: logging.handlers
location:
row: 13
column: 1
fix: ~
end_location:
row: 13
column: 24
fix:
content: ""
location:
row: 13
column: 1
end_location:
row: 14
column: 1
applied: false
- kind:
UnusedImport: shelve
location:
row: 34
column: 5
end_location:
row: 34
column: 18
fix:
content: ""
location:
row: 34
column: 1
end_location:
row: 35
column: 1
applied: false
- kind:
UnusedImport: importlib
location:
row: 35
column: 5
end_location:
row: 35
column: 21
fix:
content: pass
location:
row: 35
column: 5
end_location:
row: 35
column: 21
applied: false
- kind:
UnusedImport: pathlib
location:
row: 39
column: 5
end_location:
row: 39
column: 19
fix:
content: ""
location:
row: 39
column: 1
end_location:
row: 40
column: 1
applied: false
- kind:
UnusedImport: pickle
location:
row: 54
column: 9
end_location:
row: 54
column: 22
fix:
content: pass
location:
row: 54
column: 9
end_location:
row: 54
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