Remove identifier lexing in favor of parser ranges (#5195)

## Summary

Now that all identifiers include ranges (#5194), we can remove a ton of
this "custom lexing" code that we have to sketchily extract identifier
ranges from source.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-06-20 12:07:29 -04:00 committed by GitHub
parent 6331598511
commit 7bc33a8d5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 134 additions and 428 deletions

View file

@ -366,9 +366,7 @@ where
}
if self.enabled(Rule::AmbiguousFunctionName) {
if let Some(diagnostic) =
pycodestyle::rules::ambiguous_function_name(name, || {
stmt.identifier(self.locator)
})
pycodestyle::rules::ambiguous_function_name(name, || stmt.identifier())
{
self.diagnostics.push(diagnostic);
}
@ -383,7 +381,6 @@ where
decorator_list,
&self.settings.pep8_naming.ignore_names,
&self.semantic,
self.locator,
) {
self.diagnostics.push(diagnostic);
}
@ -452,7 +449,6 @@ where
stmt,
name,
&self.settings.pep8_naming.ignore_names,
self.locator,
) {
self.diagnostics.push(diagnostic);
}
@ -503,7 +499,6 @@ where
name,
body,
self.settings.mccabe.max_complexity,
self.locator,
) {
self.diagnostics.push(diagnostic);
}
@ -523,7 +518,6 @@ where
stmt,
body,
self.settings.pylint.max_returns,
self.locator,
) {
self.diagnostics.push(diagnostic);
}
@ -533,7 +527,6 @@ where
stmt,
body,
self.settings.pylint.max_branches,
self.locator,
) {
self.diagnostics.push(diagnostic);
}
@ -543,7 +536,6 @@ where
stmt,
body,
self.settings.pylint.max_statements,
self.locator,
) {
self.diagnostics.push(diagnostic);
}
@ -605,7 +597,6 @@ where
name,
decorator_list,
args,
self.locator,
);
}
if self.enabled(Rule::FStringDocstring) {
@ -693,9 +684,9 @@ where
pyupgrade::rules::unnecessary_class_parentheses(self, class_def, stmt);
}
if self.enabled(Rule::AmbiguousClassName) {
if let Some(diagnostic) = pycodestyle::rules::ambiguous_class_name(name, || {
stmt.identifier(self.locator)
}) {
if let Some(diagnostic) =
pycodestyle::rules::ambiguous_class_name(name, || stmt.identifier())
{
self.diagnostics.push(diagnostic);
}
}
@ -704,7 +695,6 @@ where
stmt,
name,
&self.settings.pep8_naming.ignore_names,
self.locator,
) {
self.diagnostics.push(diagnostic);
}
@ -714,7 +704,6 @@ where
stmt,
bases,
name,
self.locator,
&self.settings.pep8_naming.ignore_names,
) {
self.diagnostics.push(diagnostic);
@ -813,7 +802,7 @@ where
let qualified_name = &alias.name;
self.add_binding(
name,
alias.identifier(self.locator),
alias.identifier(),
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }),
BindingFlags::EXTERNAL,
);
@ -834,7 +823,7 @@ where
let qualified_name = &alias.name;
self.add_binding(
name,
alias.identifier(self.locator),
alias.identifier(),
BindingKind::Import(Import { qualified_name }),
flags,
);
@ -1052,7 +1041,7 @@ where
self.add_binding(
name,
alias.identifier(self.locator),
alias.identifier(),
BindingKind::FutureImport,
BindingFlags::empty(),
);
@ -1123,7 +1112,7 @@ where
helpers::format_import_from_member(level, module, &alias.name);
self.add_binding(
name,
alias.identifier(self.locator),
alias.identifier(),
BindingKind::FromImport(FromImport { qualified_name }),
flags,
);
@ -1804,7 +1793,7 @@ where
self.add_binding(
name,
stmt.identifier(self.locator),
stmt.identifier(),
BindingKind::FunctionDefinition,
BindingFlags::empty(),
);
@ -2029,7 +2018,7 @@ where
self.semantic.pop_definition();
self.add_binding(
name,
stmt.identifier(self.locator),
stmt.identifier(),
BindingKind::ClassDefinition,
BindingFlags::empty(),
);
@ -3846,7 +3835,7 @@ where
}
match name {
Some(name) => {
let range = except_handler.try_identifier(self.locator).unwrap();
let range = except_handler.try_identifier().unwrap();
if self.enabled(Rule::AmbiguousVariableName) {
if let Some(diagnostic) =
@ -3961,7 +3950,7 @@ where
// upstream.
self.add_binding(
&arg.arg,
arg.identifier(self.locator),
arg.identifier(),
BindingKind::Argument,
BindingFlags::empty(),
);
@ -4001,7 +3990,7 @@ where
{
self.add_binding(
name,
pattern.try_identifier(self.locator).unwrap(),
pattern.try_identifier().unwrap(),
BindingKind::Assignment,
BindingFlags::empty(),
);

View file

@ -653,7 +653,7 @@ pub(crate) fn definition(
MissingReturnTypeClassMethod {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
} else if is_method
@ -664,7 +664,7 @@ pub(crate) fn definition(
MissingReturnTypeStaticMethod {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
} else if is_method && visibility::is_init(name) {
@ -676,7 +676,7 @@ pub(crate) fn definition(
MissingReturnTypeSpecialMethod {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
);
if checker.patch(diagnostic.kind.rule()) {
diagnostic.try_set_fix(|| {
@ -693,7 +693,7 @@ pub(crate) fn definition(
MissingReturnTypeSpecialMethod {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
);
if checker.patch(diagnostic.kind.rule()) {
if let Some(return_type) = simple_magic_return_type(name) {
@ -713,7 +713,7 @@ pub(crate) fn definition(
MissingReturnTypeUndocumentedPublicFunction {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}
@ -723,7 +723,7 @@ pub(crate) fn definition(
MissingReturnTypePrivateFunction {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}

View file

@ -134,7 +134,7 @@ pub(crate) fn abstract_base_class(
AbstractBaseClassWithoutAbstractMethod {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}

View file

@ -29,8 +29,7 @@ pub(crate) fn f_string_docstring(checker: &mut Checker, body: &[Stmt]) {
let Expr::JoinedStr ( _) = value.as_ref() else {
return;
};
checker.diagnostics.push(Diagnostic::new(
FStringDocstring,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(FStringDocstring, stmt.identifier()));
}

View file

@ -1,8 +1,7 @@
use ruff_text_size::TextRange;
use rustpython_parser::ast::{ExceptHandler, Expr, Ranged, Stmt};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::identifier::{Identifier, TryIdentifier};
use ruff_python_stdlib::builtins::BUILTINS;
pub(super) fn shadows_builtin(name: &str, ignorelist: &[String]) -> bool {
@ -16,12 +15,12 @@ pub(crate) enum AnyShadowing<'a> {
ExceptHandler(&'a ExceptHandler),
}
impl AnyShadowing<'_> {
pub(crate) fn range(self, locator: &Locator) -> TextRange {
impl Identifier for AnyShadowing<'_> {
fn identifier(&self) -> TextRange {
match self {
AnyShadowing::Expression(expr) => expr.range(),
AnyShadowing::Statement(stmt) => stmt.identifier(locator),
AnyShadowing::ExceptHandler(handler) => handler.range(),
AnyShadowing::Statement(stmt) => stmt.identifier(),
AnyShadowing::ExceptHandler(handler) => handler.try_identifier().unwrap(),
}
}
}

View file

@ -1,6 +1,7 @@
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use rustpython_parser::ast;
use crate::checkers::ast::Checker;
@ -83,7 +84,7 @@ pub(crate) fn builtin_attribute_shadowing(
BuiltinAttributeShadowing {
name: name.to_string(),
},
shadowing.range(checker.locator),
shadowing.identifier(),
));
}
}

View file

@ -1,6 +1,7 @@
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use crate::checkers::ast::Checker;
@ -68,7 +69,7 @@ pub(crate) fn builtin_variable_shadowing(
BuiltinVariableShadowing {
name: name.to_string(),
},
shadowing.range(checker.locator),
shadowing.identifier(),
));
}
}

View file

@ -122,15 +122,13 @@ A001.py:16:7: A001 Variable `slice` is shadowing a Python builtin
17 | pass
|
A001.py:21:1: A001 Variable `ValueError` is shadowing a Python builtin
A001.py:21:23: A001 Variable `ValueError` is shadowing a Python builtin
|
19 | try:
20 | ...
21 | / except ImportError as ValueError:
22 | | ...
| |_______^ A001
23 |
24 | for memoryview, *bytearray in []:
19 | try:
20 | ...
21 | except ImportError as ValueError:
| ^^^^^^^^^^ A001
22 | ...
|
A001.py:24:5: A001 Variable `memoryview` is shadowing a Python builtin

View file

@ -102,15 +102,13 @@ A001.py:16:7: A001 Variable `slice` is shadowing a Python builtin
17 | pass
|
A001.py:21:1: A001 Variable `ValueError` is shadowing a Python builtin
A001.py:21:23: A001 Variable `ValueError` is shadowing a Python builtin
|
19 | try:
20 | ...
21 | / except ImportError as ValueError:
22 | | ...
| |_______^ A001
23 |
24 | for memoryview, *bytearray in []:
19 | try:
20 | ...
21 | except ImportError as ValueError:
| ^^^^^^^^^^ A001
22 | ...
|
A001.py:24:5: A001 Variable `memoryview` is shadowing a Python builtin

View file

@ -16,7 +16,7 @@ custom.py:4:8: ICN001 `dask.array` should be imported as `da`
|
3 | import altair # unconventional
4 | import dask.array # unconventional
| ^^^^ ICN001
| ^^^^^^^^^^ ICN001
5 | import dask.dataframe # unconventional
6 | import matplotlib.pyplot # unconventional
|
@ -27,7 +27,7 @@ custom.py:5:8: ICN001 `dask.dataframe` should be imported as `dd`
3 | import altair # unconventional
4 | import dask.array # unconventional
5 | import dask.dataframe # unconventional
| ^^^^ ICN001
| ^^^^^^^^^^^^^^ ICN001
6 | import matplotlib.pyplot # unconventional
7 | import numpy # unconventional
|
@ -38,7 +38,7 @@ custom.py:6:8: ICN001 `matplotlib.pyplot` should be imported as `plt`
4 | import dask.array # unconventional
5 | import dask.dataframe # unconventional
6 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001
| ^^^^^^^^^^^^^^^^^ ICN001
7 | import numpy # unconventional
8 | import pandas # unconventional
|
@ -115,7 +115,7 @@ custom.py:13:8: ICN001 `plotly.express` should be imported as `px`
11 | import holoviews # unconventional
12 | import panel # unconventional
13 | import plotly.express # unconventional
| ^^^^^^ ICN001
| ^^^^^^^^^^^^^^ ICN001
14 | import matplotlib # unconventional
15 | import polars # unconventional
|

View file

@ -16,7 +16,7 @@ defaults.py:4:8: ICN001 `matplotlib.pyplot` should be imported as `plt`
|
3 | import altair # unconventional
4 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001
| ^^^^^^^^^^^^^^^^^ ICN001
5 | import numpy # unconventional
6 | import pandas # unconventional
|

View file

@ -6,7 +6,7 @@ from_imports.py:3:8: ICN001 `xml.dom.minidom` should be imported as `md`
1 | # Test absolute imports
2 | # Violation cases
3 | import xml.dom.minidom
| ^^^ ICN001
| ^^^^^^^^^^^^^^^ ICN001
4 | import xml.dom.minidom as wrong
5 | from xml.dom import minidom as wrong
|

View file

@ -16,7 +16,7 @@ override_default.py:4:8: ICN001 `matplotlib.pyplot` should be imported as `plt`
|
3 | import altair # unconventional
4 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001
| ^^^^^^^^^^^^^^^^^ ICN001
5 | import numpy # unconventional
6 | import pandas # unconventional
|

View file

@ -16,7 +16,7 @@ remove_default.py:4:8: ICN001 `matplotlib.pyplot` should be imported as `plt`
|
3 | import altair # unconventional
4 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001
| ^^^^^^^^^^^^^^^^^ ICN001
5 | import numpy # not checked
6 | import pandas # unconventional
|

View file

@ -148,7 +148,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(),
method_name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
return;
@ -162,7 +162,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(),
method_name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
return;
@ -177,7 +177,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(),
method_name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
return;
@ -193,7 +193,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(),
method_name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}
@ -206,7 +206,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(),
method_name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}

View file

@ -90,7 +90,7 @@ pub(crate) fn str_or_repr_defined_in_stub(checker: &mut Checker, stmt: &Stmt) {
StrOrReprDefinedInStub {
name: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
);
if checker.patch(diagnostic.kind.rule()) {
let stmt = checker.semantic().stmt();

View file

@ -32,6 +32,6 @@ pub(crate) fn stub_body_multiple_statements(checker: &mut Checker, stmt: &Stmt,
checker.diagnostics.push(Diagnostic::new(
StubBodyMultipleStatements,
stmt.identifier(checker.locator),
stmt.identifier(),
));
}

View file

@ -379,7 +379,7 @@ fn check_fixture_returns(checker: &mut Checker, stmt: &Stmt, name: &str, body: &
PytestIncorrectFixtureNameUnderscore {
function: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
} else if checker.enabled(Rule::PytestMissingFixtureNameUnderscore)
&& !visitor.has_return_with_value
@ -390,7 +390,7 @@ fn check_fixture_returns(checker: &mut Checker, stmt: &Stmt, name: &str, body: &
PytestMissingFixtureNameUnderscore {
function: name.to_string(),
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}

View file

@ -77,7 +77,7 @@ pub(crate) fn no_slots_in_namedtuple_subclass(
if !has_slots(&class.body) {
checker.diagnostics.push(Diagnostic::new(
NoSlotsInNamedtupleSubclass,
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}

View file

@ -59,10 +59,9 @@ pub(crate) fn no_slots_in_str_subclass(checker: &mut Checker, stmt: &Stmt, class
})
}) {
if !has_slots(&class.body) {
checker.diagnostics.push(Diagnostic::new(
NoSlotsInStrSubclass,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(NoSlotsInStrSubclass, stmt.identifier()));
}
}
}

View file

@ -63,10 +63,9 @@ pub(crate) fn no_slots_in_tuple_subclass(checker: &mut Checker, stmt: &Stmt, cla
})
}) {
if !has_slots(&class.body) {
checker.diagnostics.push(Diagnostic::new(
NoSlotsInTupleSubclass,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(NoSlotsInTupleSubclass, stmt.identifier()));
}
}
}

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{self, ExceptHandler, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
/// ## What it does
/// Checks for functions with a high `McCabe` complexity.
@ -142,7 +141,6 @@ pub(crate) fn function_is_too_complex(
name: &str,
body: &[Stmt],
max_complexity: usize,
locator: &Locator,
) -> Option<Diagnostic> {
let complexity = get_complexity_number(body) + 1;
if complexity > max_complexity {
@ -152,7 +150,7 @@ pub(crate) fn function_is_too_complex(
complexity,
max_complexity,
},
stmt.identifier(locator),
stmt.identifier(),
))
} else {
None

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::Stmt;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
use ruff_python_semantic::{Scope, ScopeKind};
use crate::settings::types::IdentifierPattern;
@ -48,7 +47,6 @@ pub(crate) fn dunder_function_name(
stmt: &Stmt,
name: &str,
ignore_names: &[IdentifierPattern],
locator: &Locator,
) -> Option<Diagnostic> {
if matches!(scope.kind, ScopeKind::Class(_)) {
return None;
@ -67,8 +65,5 @@ pub(crate) fn dunder_function_name(
return None;
}
Some(Diagnostic::new(
DunderFunctionName,
stmt.identifier(locator),
))
Some(Diagnostic::new(DunderFunctionName, stmt.identifier()))
}

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{self, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
use crate::settings::types::IdentifierPattern;
@ -48,7 +47,6 @@ pub(crate) fn error_suffix_on_exception_name(
class_def: &Stmt,
bases: &[Expr],
name: &str,
locator: &Locator,
ignore_names: &[IdentifierPattern],
) -> Option<Diagnostic> {
if ignore_names
@ -75,6 +73,6 @@ pub(crate) fn error_suffix_on_exception_name(
ErrorSuffixOnExceptionName {
name: name.to_string(),
},
class_def.identifier(locator),
class_def.identifier(),
))
}

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::Stmt;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
use crate::settings::types::IdentifierPattern;
@ -54,7 +53,6 @@ pub(crate) fn invalid_class_name(
class_def: &Stmt,
name: &str,
ignore_names: &[IdentifierPattern],
locator: &Locator,
) -> Option<Diagnostic> {
if ignore_names
.iter()
@ -69,7 +67,7 @@ pub(crate) fn invalid_class_name(
InvalidClassName {
name: name.to_string(),
},
class_def.identifier(locator),
class_def.identifier(),
));
}
None

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{Decorator, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
use ruff_python_semantic::analyze::visibility;
use ruff_python_semantic::SemanticModel;
use ruff_python_stdlib::str;
@ -57,7 +56,6 @@ pub(crate) fn invalid_function_name(
decorator_list: &[Decorator],
ignore_names: &[IdentifierPattern],
semantic: &SemanticModel,
locator: &Locator,
) -> Option<Diagnostic> {
// Ignore any explicitly-ignored function names.
if ignore_names
@ -82,6 +80,6 @@ pub(crate) fn invalid_function_name(
InvalidFunctionName {
name: name.to_string(),
},
stmt.identifier(locator),
stmt.identifier(),
))
}

View file

@ -30,8 +30,7 @@ pub(crate) fn if_needed(checker: &mut Checker, docstring: &Docstring) {
if !is_overload(cast::decorator_list(stmt), checker.semantic()) {
return;
}
checker.diagnostics.push(Diagnostic::new(
OverloadWithDocstring,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(OverloadWithDocstring, stmt.identifier()));
}

View file

@ -133,10 +133,9 @@ pub(crate) fn not_missing(
..
}) => {
if checker.enabled(Rule::UndocumentedPublicClass) {
checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicClass,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(UndocumentedPublicClass, stmt.identifier()));
}
false
}
@ -148,7 +147,7 @@ pub(crate) fn not_missing(
if checker.enabled(Rule::UndocumentedPublicNestedClass) {
checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicNestedClass,
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
false
@ -164,7 +163,7 @@ pub(crate) fn not_missing(
if checker.enabled(Rule::UndocumentedPublicFunction) {
checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicFunction,
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
false
@ -181,34 +180,30 @@ pub(crate) fn not_missing(
true
} else if is_init(cast::name(stmt)) {
if checker.enabled(Rule::UndocumentedPublicInit) {
checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicInit,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(UndocumentedPublicInit, stmt.identifier()));
}
true
} else if is_new(cast::name(stmt)) || is_call(cast::name(stmt)) {
if checker.enabled(Rule::UndocumentedPublicMethod) {
checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicMethod,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(UndocumentedPublicMethod, stmt.identifier()));
}
true
} else if is_magic(cast::name(stmt)) {
if checker.enabled(Rule::UndocumentedMagicMethod) {
checker.diagnostics.push(Diagnostic::new(
UndocumentedMagicMethod,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(UndocumentedMagicMethod, stmt.identifier()));
}
true
} else {
if checker.enabled(Rule::UndocumentedPublicMethod) {
checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicMethod,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(UndocumentedPublicMethod, stmt.identifier()));
}
true
}

View file

@ -763,7 +763,7 @@ fn missing_args(checker: &mut Checker, docstring: &Docstring, docstrings_args: &
let names = missing_arg_names.into_iter().sorted().collect();
checker.diagnostics.push(Diagnostic::new(
UndocumentedParam { names },
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}

View file

@ -44,7 +44,7 @@ F401_0.py:12:8: F401 [*] `logging.handlers` imported but unused
10 | import multiprocessing.process
11 | import logging.config
12 | import logging.handlers
| ^^^^^^^ F401
| ^^^^^^^^^^^^^^^^ F401
13 | from typing import (
14 | TYPE_CHECKING,
|

View file

@ -41,7 +41,7 @@ F401_5.py:4:8: F401 [*] `h.i` imported but unused
2 | from a.b import c
3 | from d.e import f as g
4 | import h.i
| ^ F401
| ^^^ F401
5 | import j.k as l
|
= help: Remove unused import: `h.i`

View file

@ -68,9 +68,8 @@ pub(crate) fn property_with_parameters(
> 1
&& checker.semantic().is_builtin("property")
{
checker.diagnostics.push(Diagnostic::new(
PropertyWithParameters,
stmt.identifier(checker.locator),
));
checker
.diagnostics
.push(Diagnostic::new(PropertyWithParameters, stmt.identifier()));
}
}

View file

@ -72,7 +72,7 @@ pub(crate) fn too_many_arguments(checker: &mut Checker, arguments: &Arguments, s
c_args: num_arguments,
max_args: checker.settings.pylint.max_args,
},
stmt.identifier(checker.locator),
stmt.identifier(),
));
}
}

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{self, ExceptHandler, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
/// ## What it does
/// Checks for functions or methods with too many branches.
@ -166,7 +165,6 @@ pub(crate) fn too_many_branches(
stmt: &Stmt,
body: &[Stmt],
max_branches: usize,
locator: &Locator,
) -> Option<Diagnostic> {
let branches = num_branches(body);
if branches > max_branches {
@ -175,7 +173,7 @@ pub(crate) fn too_many_branches(
branches,
max_branches,
},
stmt.identifier(locator),
stmt.identifier(),
))
} else {
None

View file

@ -4,7 +4,6 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::ReturnStatementVisitor;
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::statement_visitor::StatementVisitor;
/// ## What it does
@ -81,7 +80,6 @@ pub(crate) fn too_many_return_statements(
stmt: &Stmt,
body: &[Stmt],
max_returns: usize,
locator: &Locator,
) -> Option<Diagnostic> {
let returns = num_returns(body);
if returns > max_returns {
@ -90,7 +88,7 @@ pub(crate) fn too_many_return_statements(
returns,
max_returns,
},
stmt.identifier(locator),
stmt.identifier(),
))
} else {
None

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{self, ExceptHandler, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
/// ## What it does
/// Checks for functions or methods with too many statements.
@ -149,7 +148,6 @@ pub(crate) fn too_many_statements(
stmt: &Stmt,
body: &[Stmt],
max_statements: usize,
locator: &Locator,
) -> Option<Diagnostic> {
let statements = num_statements(body);
if statements > max_statements {
@ -158,7 +156,7 @@ pub(crate) fn too_many_statements(
statements,
max_statements,
},
stmt.identifier(locator),
stmt.identifier(),
))
} else {
None

View file

@ -5,7 +5,6 @@ use rustpython_parser::ast::{Arguments, Decorator, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
use ruff_python_semantic::analyze::visibility::is_staticmethod;
use crate::checkers::ast::Checker;
@ -143,7 +142,6 @@ pub(crate) fn unexpected_special_method_signature(
name: &str,
decorator_list: &[Decorator],
args: &Arguments,
locator: &Locator,
) {
if !checker.semantic().scope().kind.is_class() {
return;
@ -188,7 +186,7 @@ pub(crate) fn unexpected_special_method_signature(
expected_params,
actual_params,
},
stmt.identifier(locator),
stmt.identifier(),
));
}
}

View file

@ -53,7 +53,7 @@ pub(crate) fn unnecessary_class_parentheses(
return;
}
let offset = stmt.identifier(checker.locator).start();
let offset = stmt.identifier().start();
let contents = checker.locator.after(offset);
// Find the open and closing parentheses between the class name and the colon, if they exist.

View file

@ -73,7 +73,7 @@ pub(crate) fn useless_object_inheritance(
diagnostic.try_set_fix(|| {
let edit = remove_argument(
checker.locator,
stmt.identifier(checker.locator).start(),
stmt.identifier().start(),
expr.range(),
&class_def.bases,
&class_def.keywords,

View file

@ -29,13 +29,13 @@ use crate::source_code::Locator;
pub trait Identifier {
/// Return the [`TextRange`] of the identifier in the given AST node.
fn identifier(&self, locator: &Locator) -> TextRange;
fn identifier(&self) -> TextRange;
}
pub trait TryIdentifier {
/// Return the [`TextRange`] of the identifier in the given AST node, or `None` if
/// the node does not have an identifier.
fn try_identifier(&self, locator: &Locator) -> Option<TextRange>;
fn try_identifier(&self) -> Option<TextRange>;
}
impl Identifier for Stmt {
@ -46,44 +46,11 @@ impl Identifier for Stmt {
/// def f():
/// ...
/// ```
fn identifier(&self, locator: &Locator) -> TextRange {
fn identifier(&self) -> TextRange {
match self {
Stmt::ClassDef(ast::StmtClassDef {
decorator_list,
range,
..
})
| Stmt::FunctionDef(ast::StmtFunctionDef {
decorator_list,
range,
..
}) => {
let range = decorator_list.last().map_or(*range, |last_decorator| {
TextRange::new(last_decorator.end(), range.end())
});
// The first "identifier" is the `def` or `class` keyword.
// The second "identifier" is the function or class name.
IdentifierTokenizer::starts_at(range.start(), locator.contents())
.nth(1)
.expect("Unable to identify identifier in function or class definition")
}
Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
decorator_list,
range,
..
}) => {
let range = decorator_list.last().map_or(*range, |last_decorator| {
TextRange::new(last_decorator.end(), range.end())
});
// The first "identifier" is the `async` keyword.
// The second "identifier" is the `def` or `class` keyword.
// The third "identifier" is the function or class name.
IdentifierTokenizer::starts_at(range.start(), locator.contents())
.nth(2)
.expect("Unable to identify identifier in function or class definition")
}
Stmt::ClassDef(ast::StmtClassDef { name, .. })
| Stmt::FunctionDef(ast::StmtFunctionDef { name, .. })
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. }) => name.range(),
_ => self.range(),
}
}
@ -97,10 +64,8 @@ impl Identifier for Arg {
/// def f(x: int):
/// ...
/// ```
fn identifier(&self, locator: &Locator) -> TextRange {
IdentifierTokenizer::new(locator.contents(), self.range())
.next()
.expect("Failed to find argument identifier")
fn identifier(&self) -> TextRange {
self.arg.range()
}
}
@ -112,8 +77,8 @@ impl Identifier for ArgWithDefault {
/// def f(x: int = 0):
/// ...
/// ```
fn identifier(&self, locator: &Locator) -> TextRange {
self.def.identifier(locator)
fn identifier(&self) -> TextRange {
self.def.identifier()
}
}
@ -124,22 +89,10 @@ impl Identifier for Alias {
/// ```python
/// from foo import bar as x
/// ```
fn identifier(&self, locator: &Locator) -> TextRange {
if matches!(self.name.as_str(), "*") {
self.range()
} else if self.asname.is_none() {
// The first identifier is the module name.
IdentifierTokenizer::new(locator.contents(), self.range())
.next()
.expect("Failed to find alias identifier")
} else {
// The first identifier is the module name.
// The second identifier is the "as" keyword.
// The third identifier is the alias name.
IdentifierTokenizer::new(locator.contents(), self.range())
.last()
.expect("Failed to find alias identifier")
}
fn identifier(&self) -> TextRange {
self.asname
.as_ref()
.map_or_else(|| self.name.range(), Ranged::range)
}
}
@ -177,78 +130,20 @@ impl TryIdentifier for Pattern {
/// case *z:
/// ...
/// ```
fn try_identifier(&self, locator: &Locator) -> Option<TextRange> {
match self {
fn try_identifier(&self) -> Option<TextRange> {
let name = match self {
Pattern::MatchAs(ast::PatternMatchAs {
name: Some(_),
pattern,
range,
}) => {
Some(if let Some(pattern) = pattern {
// Identify `z` in:
// ```python
// match x:
// case Foo(bar) as z:
// ...
// ```
IdentifierTokenizer::starts_at(pattern.end(), locator.contents())
.nth(1)
.expect("Unable to identify identifier in pattern")
} else {
// Identify `z` in:
// ```python
// match x:
// case z:
// ...
// ```
*range
})
}
name: Some(name), ..
}) => Some(name),
Pattern::MatchMapping(ast::PatternMatchMapping {
patterns,
rest: Some(_),
..
}) => {
Some(if let Some(pattern) = patterns.last() {
// Identify `z` in:
// ```python
// match x:
// case {"a": 1, **z}
// ...
// ```
//
// A mapping pattern can contain at most one double-star pattern,
// and it must be the last pattern in the mapping.
IdentifierTokenizer::starts_at(pattern.end(), locator.contents())
.next()
.expect("Unable to identify identifier in pattern")
} else {
// Identify `z` in:
// ```python
// match x:
// case {**z}
// ...
// ```
IdentifierTokenizer::starts_at(self.start(), locator.contents())
.next()
.expect("Unable to identify identifier in pattern")
})
}
Pattern::MatchStar(ast::PatternMatchStar { name: Some(_), .. }) => {
// Identify `z` in:
// ```python
// match x:
// case *z:
// ...
// ```
Some(
IdentifierTokenizer::starts_at(self.start(), locator.contents())
.next()
.expect("Unable to identify identifier in pattern"),
)
}
rest: Some(rest), ..
}) => Some(rest),
Pattern::MatchStar(ast::PatternMatchStar {
name: Some(name), ..
}) => Some(name),
_ => None,
}
};
name.map(Ranged::range)
}
}
@ -262,24 +157,9 @@ impl TryIdentifier for ExceptHandler {
/// except ValueError as e:
/// ...
/// ```
fn try_identifier(&self, locator: &Locator) -> Option<TextRange> {
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, name, .. }) =
self;
if name.is_none() {
return None;
}
let Some(type_) = type_ else {
return None;
};
// The exception name is the first identifier token after the `as` keyword.
Some(
IdentifierTokenizer::starts_at(type_.end(), locator.contents())
.nth(1)
.expect("Failed to find exception identifier in exception handler"),
)
fn try_identifier(&self) -> Option<TextRange> {
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { name, .. }) = self;
name.as_ref().map(Ranged::range)
}
}
@ -481,137 +361,8 @@ mod tests {
use rustpython_parser::Parse;
use crate::identifier;
use crate::identifier::Identifier;
use crate::source_code::Locator;
#[test]
fn extract_arg_range() -> Result<()> {
let contents = "def f(x): pass".trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let function_def = stmt.as_function_def_stmt().unwrap();
let args = &function_def.args.args;
let arg = &args[0];
let locator = Locator::new(contents);
assert_eq!(
arg.identifier(&locator),
TextRange::new(TextSize::from(6), TextSize::from(7))
);
let contents = "def f(x: int): pass".trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let function_def = stmt.as_function_def_stmt().unwrap();
let args = &function_def.args.args;
let arg = &args[0];
let locator = Locator::new(contents);
assert_eq!(
arg.identifier(&locator),
TextRange::new(TextSize::from(6), TextSize::from(7))
);
let contents = r#"
def f(
x: int, # Comment
):
pass
"#
.trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let function_def = stmt.as_function_def_stmt().unwrap();
let args = &function_def.args.args;
let arg = &args[0];
let locator = Locator::new(contents);
assert_eq!(
arg.identifier(&locator),
TextRange::new(TextSize::from(11), TextSize::from(12))
);
Ok(())
}
#[test]
fn extract_identifier_range() -> Result<()> {
let contents = "def f(): pass".trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(4), TextSize::from(5))
);
let contents = "async def f(): pass".trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(10), TextSize::from(11))
);
let contents = r#"
def \
f():
pass
"#
.trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(8), TextSize::from(9))
);
let contents = "class Class(): pass".trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(6), TextSize::from(11))
);
let contents = "class Class: pass".trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(6), TextSize::from(11))
);
let contents = r#"
@decorator()
class Class():
pass
"#
.trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(19), TextSize::from(24))
);
let contents = r#"
@decorator() # Comment
class Class():
pass
"#
.trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(30), TextSize::from(35))
);
let contents = r#"x = y + 1"#.trim();
let stmt = Stmt::parse(contents, "<filename>")?;
let locator = Locator::new(contents);
assert_eq!(
stmt.identifier(&locator),
TextRange::new(TextSize::from(0), TextSize::from(9))
);
Ok(())
}
#[test]
fn extract_else_range() -> Result<()> {
let contents = r#"