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

View file

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

View file

@ -134,7 +134,7 @@ pub(crate) fn abstract_base_class(
AbstractBaseClassWithoutAbstractMethod { AbstractBaseClassWithoutAbstractMethod {
name: name.to_string(), 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 { let Expr::JoinedStr ( _) = value.as_ref() else {
return; return;
}; };
checker.diagnostics.push(Diagnostic::new( checker
FStringDocstring, .diagnostics
stmt.identifier(checker.locator), .push(Diagnostic::new(FStringDocstring, stmt.identifier()));
));
} }

View file

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

View file

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

View file

@ -1,6 +1,7 @@
use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation; use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -68,7 +69,7 @@ pub(crate) fn builtin_variable_shadowing(
BuiltinVariableShadowing { BuiltinVariableShadowing {
name: name.to_string(), 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 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: 19 | try:
20 | ... 20 | ...
21 | / except ImportError as ValueError: 21 | except ImportError as ValueError:
22 | | ... | ^^^^^^^^^^ A001
| |_______^ A001 22 | ...
23 |
24 | for memoryview, *bytearray in []:
| |
A001.py:24:5: A001 Variable `memoryview` is shadowing a Python builtin 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 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: 19 | try:
20 | ... 20 | ...
21 | / except ImportError as ValueError: 21 | except ImportError as ValueError:
22 | | ... | ^^^^^^^^^^ A001
| |_______^ A001 22 | ...
23 |
24 | for memoryview, *bytearray in []:
| |
A001.py:24:5: A001 Variable `memoryview` is shadowing a Python builtin 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 3 | import altair # unconventional
4 | import dask.array # unconventional 4 | import dask.array # unconventional
| ^^^^ ICN001 | ^^^^^^^^^^ ICN001
5 | import dask.dataframe # unconventional 5 | import dask.dataframe # unconventional
6 | import matplotlib.pyplot # 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 3 | import altair # unconventional
4 | import dask.array # unconventional 4 | import dask.array # unconventional
5 | import dask.dataframe # unconventional 5 | import dask.dataframe # unconventional
| ^^^^ ICN001 | ^^^^^^^^^^^^^^ ICN001
6 | import matplotlib.pyplot # unconventional 6 | import matplotlib.pyplot # unconventional
7 | import numpy # 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 4 | import dask.array # unconventional
5 | import dask.dataframe # unconventional 5 | import dask.dataframe # unconventional
6 | import matplotlib.pyplot # unconventional 6 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001 | ^^^^^^^^^^^^^^^^^ ICN001
7 | import numpy # unconventional 7 | import numpy # unconventional
8 | import pandas # 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 11 | import holoviews # unconventional
12 | import panel # unconventional 12 | import panel # unconventional
13 | import plotly.express # unconventional 13 | import plotly.express # unconventional
| ^^^^^^ ICN001 | ^^^^^^^^^^^^^^ ICN001
14 | import matplotlib # unconventional 14 | import matplotlib # unconventional
15 | import polars # 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 3 | import altair # unconventional
4 | import matplotlib.pyplot # unconventional 4 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001 | ^^^^^^^^^^^^^^^^^ ICN001
5 | import numpy # unconventional 5 | import numpy # unconventional
6 | import pandas # 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 1 | # Test absolute imports
2 | # Violation cases 2 | # Violation cases
3 | import xml.dom.minidom 3 | import xml.dom.minidom
| ^^^ ICN001 | ^^^^^^^^^^^^^^^ ICN001
4 | import xml.dom.minidom as wrong 4 | import xml.dom.minidom as wrong
5 | from xml.dom import 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 3 | import altair # unconventional
4 | import matplotlib.pyplot # unconventional 4 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001 | ^^^^^^^^^^^^^^^^^ ICN001
5 | import numpy # unconventional 5 | import numpy # unconventional
6 | import pandas # 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 3 | import altair # unconventional
4 | import matplotlib.pyplot # unconventional 4 | import matplotlib.pyplot # unconventional
| ^^^^^^^^^^ ICN001 | ^^^^^^^^^^^^^^^^^ ICN001
5 | import numpy # not checked 5 | import numpy # not checked
6 | import pandas # unconventional 6 | import pandas # unconventional
| |

View file

@ -148,7 +148,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(), class_name: class_def.name.to_string(),
method_name: name.to_string(), method_name: name.to_string(),
}, },
stmt.identifier(checker.locator), stmt.identifier(),
)); ));
} }
return; return;
@ -162,7 +162,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(), class_name: class_def.name.to_string(),
method_name: name.to_string(), method_name: name.to_string(),
}, },
stmt.identifier(checker.locator), stmt.identifier(),
)); ));
} }
return; return;
@ -177,7 +177,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(), class_name: class_def.name.to_string(),
method_name: name.to_string(), method_name: name.to_string(),
}, },
stmt.identifier(checker.locator), stmt.identifier(),
)); ));
} }
return; return;
@ -193,7 +193,7 @@ pub(crate) fn non_self_return_type(
class_name: class_def.name.to_string(), class_name: class_def.name.to_string(),
method_name: 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(), class_name: class_def.name.to_string(),
method_name: 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 { StrOrReprDefinedInStub {
name: name.to_string(), name: name.to_string(),
}, },
stmt.identifier(checker.locator), stmt.identifier(),
); );
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
let stmt = checker.semantic().stmt(); 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( checker.diagnostics.push(Diagnostic::new(
StubBodyMultipleStatements, 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 { PytestIncorrectFixtureNameUnderscore {
function: name.to_string(), function: name.to_string(),
}, },
stmt.identifier(checker.locator), stmt.identifier(),
)); ));
} else if checker.enabled(Rule::PytestMissingFixtureNameUnderscore) } else if checker.enabled(Rule::PytestMissingFixtureNameUnderscore)
&& !visitor.has_return_with_value && !visitor.has_return_with_value
@ -390,7 +390,7 @@ fn check_fixture_returns(checker: &mut Checker, stmt: &Stmt, name: &str, body: &
PytestMissingFixtureNameUnderscore { PytestMissingFixtureNameUnderscore {
function: name.to_string(), 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) { if !has_slots(&class.body) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
NoSlotsInNamedtupleSubclass, 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) { if !has_slots(&class.body) {
checker.diagnostics.push(Diagnostic::new( checker
NoSlotsInStrSubclass, .diagnostics
stmt.identifier(checker.locator), .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) { if !has_slots(&class.body) {
checker.diagnostics.push(Diagnostic::new( checker
NoSlotsInTupleSubclass, .diagnostics
stmt.identifier(checker.locator), .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_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier; use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
/// ## What it does /// ## What it does
/// Checks for functions with a high `McCabe` complexity. /// Checks for functions with a high `McCabe` complexity.
@ -142,7 +141,6 @@ pub(crate) fn function_is_too_complex(
name: &str, name: &str,
body: &[Stmt], body: &[Stmt],
max_complexity: usize, max_complexity: usize,
locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
let complexity = get_complexity_number(body) + 1; let complexity = get_complexity_number(body) + 1;
if complexity > max_complexity { if complexity > max_complexity {
@ -152,7 +150,7 @@ pub(crate) fn function_is_too_complex(
complexity, complexity,
max_complexity, max_complexity,
}, },
stmt.identifier(locator), stmt.identifier(),
)) ))
} else { } else {
None None

View file

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

View file

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

View file

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

View file

@ -133,10 +133,9 @@ pub(crate) fn not_missing(
.. ..
}) => { }) => {
if checker.enabled(Rule::UndocumentedPublicClass) { if checker.enabled(Rule::UndocumentedPublicClass) {
checker.diagnostics.push(Diagnostic::new( checker
UndocumentedPublicClass, .diagnostics
stmt.identifier(checker.locator), .push(Diagnostic::new(UndocumentedPublicClass, stmt.identifier()));
));
} }
false false
} }
@ -148,7 +147,7 @@ pub(crate) fn not_missing(
if checker.enabled(Rule::UndocumentedPublicNestedClass) { if checker.enabled(Rule::UndocumentedPublicNestedClass) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicNestedClass, UndocumentedPublicNestedClass,
stmt.identifier(checker.locator), stmt.identifier(),
)); ));
} }
false false
@ -164,7 +163,7 @@ pub(crate) fn not_missing(
if checker.enabled(Rule::UndocumentedPublicFunction) { if checker.enabled(Rule::UndocumentedPublicFunction) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
UndocumentedPublicFunction, UndocumentedPublicFunction,
stmt.identifier(checker.locator), stmt.identifier(),
)); ));
} }
false false
@ -181,34 +180,30 @@ pub(crate) fn not_missing(
true true
} else if is_init(cast::name(stmt)) { } else if is_init(cast::name(stmt)) {
if checker.enabled(Rule::UndocumentedPublicInit) { if checker.enabled(Rule::UndocumentedPublicInit) {
checker.diagnostics.push(Diagnostic::new( checker
UndocumentedPublicInit, .diagnostics
stmt.identifier(checker.locator), .push(Diagnostic::new(UndocumentedPublicInit, stmt.identifier()));
));
} }
true true
} else if is_new(cast::name(stmt)) || is_call(cast::name(stmt)) { } else if is_new(cast::name(stmt)) || is_call(cast::name(stmt)) {
if checker.enabled(Rule::UndocumentedPublicMethod) { if checker.enabled(Rule::UndocumentedPublicMethod) {
checker.diagnostics.push(Diagnostic::new( checker
UndocumentedPublicMethod, .diagnostics
stmt.identifier(checker.locator), .push(Diagnostic::new(UndocumentedPublicMethod, stmt.identifier()));
));
} }
true true
} else if is_magic(cast::name(stmt)) { } else if is_magic(cast::name(stmt)) {
if checker.enabled(Rule::UndocumentedMagicMethod) { if checker.enabled(Rule::UndocumentedMagicMethod) {
checker.diagnostics.push(Diagnostic::new( checker
UndocumentedMagicMethod, .diagnostics
stmt.identifier(checker.locator), .push(Diagnostic::new(UndocumentedMagicMethod, stmt.identifier()));
));
} }
true true
} else { } else {
if checker.enabled(Rule::UndocumentedPublicMethod) { if checker.enabled(Rule::UndocumentedPublicMethod) {
checker.diagnostics.push(Diagnostic::new( checker
UndocumentedPublicMethod, .diagnostics
stmt.identifier(checker.locator), .push(Diagnostic::new(UndocumentedPublicMethod, stmt.identifier()));
));
} }
true 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(); let names = missing_arg_names.into_iter().sorted().collect();
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
UndocumentedParam { names }, 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 10 | import multiprocessing.process
11 | import logging.config 11 | import logging.config
12 | import logging.handlers 12 | import logging.handlers
| ^^^^^^^ F401 | ^^^^^^^^^^^^^^^^ F401
13 | from typing import ( 13 | from typing import (
14 | TYPE_CHECKING, 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 2 | from a.b import c
3 | from d.e import f as g 3 | from d.e import f as g
4 | import h.i 4 | import h.i
| ^ F401 | ^^^ F401
5 | import j.k as l 5 | import j.k as l
| |
= help: Remove unused import: `h.i` = help: Remove unused import: `h.i`

View file

@ -68,9 +68,8 @@ pub(crate) fn property_with_parameters(
> 1 > 1
&& checker.semantic().is_builtin("property") && checker.semantic().is_builtin("property")
{ {
checker.diagnostics.push(Diagnostic::new( checker
PropertyWithParameters, .diagnostics
stmt.identifier(checker.locator), .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, c_args: num_arguments,
max_args: checker.settings.pylint.max_args, 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_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier; use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::source_code::Locator;
/// ## What it does /// ## What it does
/// Checks for functions or methods with too many branches. /// Checks for functions or methods with too many branches.
@ -166,7 +165,6 @@ pub(crate) fn too_many_branches(
stmt: &Stmt, stmt: &Stmt,
body: &[Stmt], body: &[Stmt],
max_branches: usize, max_branches: usize,
locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
let branches = num_branches(body); let branches = num_branches(body);
if branches > max_branches { if branches > max_branches {
@ -175,7 +173,7 @@ pub(crate) fn too_many_branches(
branches, branches,
max_branches, max_branches,
}, },
stmt.identifier(locator), stmt.identifier(),
)) ))
} else { } else {
None None

View file

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

View file

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

View file

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

View file

@ -53,7 +53,7 @@ pub(crate) fn unnecessary_class_parentheses(
return; return;
} }
let offset = stmt.identifier(checker.locator).start(); let offset = stmt.identifier().start();
let contents = checker.locator.after(offset); let contents = checker.locator.after(offset);
// Find the open and closing parentheses between the class name and the colon, if they exist. // 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(|| { diagnostic.try_set_fix(|| {
let edit = remove_argument( let edit = remove_argument(
checker.locator, checker.locator,
stmt.identifier(checker.locator).start(), stmt.identifier().start(),
expr.range(), expr.range(),
&class_def.bases, &class_def.bases,
&class_def.keywords, &class_def.keywords,

View file

@ -29,13 +29,13 @@ use crate::source_code::Locator;
pub trait Identifier { pub trait Identifier {
/// Return the [`TextRange`] of the identifier in the given AST node. /// Return the [`TextRange`] of the identifier in the given AST node.
fn identifier(&self, locator: &Locator) -> TextRange; fn identifier(&self) -> TextRange;
} }
pub trait TryIdentifier { pub trait TryIdentifier {
/// Return the [`TextRange`] of the identifier in the given AST node, or `None` if /// Return the [`TextRange`] of the identifier in the given AST node, or `None` if
/// the node does not have an identifier. /// 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 { impl Identifier for Stmt {
@ -46,44 +46,11 @@ impl Identifier for Stmt {
/// def f(): /// def f():
/// ... /// ...
/// ``` /// ```
fn identifier(&self, locator: &Locator) -> TextRange { fn identifier(&self) -> TextRange {
match self { match self {
Stmt::ClassDef(ast::StmtClassDef { Stmt::ClassDef(ast::StmtClassDef { name, .. })
decorator_list, | Stmt::FunctionDef(ast::StmtFunctionDef { name, .. })
range, | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. }) => name.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")
}
_ => self.range(), _ => self.range(),
} }
} }
@ -97,10 +64,8 @@ impl Identifier for Arg {
/// def f(x: int): /// def f(x: int):
/// ... /// ...
/// ``` /// ```
fn identifier(&self, locator: &Locator) -> TextRange { fn identifier(&self) -> TextRange {
IdentifierTokenizer::new(locator.contents(), self.range()) self.arg.range()
.next()
.expect("Failed to find argument identifier")
} }
} }
@ -112,8 +77,8 @@ impl Identifier for ArgWithDefault {
/// def f(x: int = 0): /// def f(x: int = 0):
/// ... /// ...
/// ``` /// ```
fn identifier(&self, locator: &Locator) -> TextRange { fn identifier(&self) -> TextRange {
self.def.identifier(locator) self.def.identifier()
} }
} }
@ -124,22 +89,10 @@ impl Identifier for Alias {
/// ```python /// ```python
/// from foo import bar as x /// from foo import bar as x
/// ``` /// ```
fn identifier(&self, locator: &Locator) -> TextRange { fn identifier(&self) -> TextRange {
if matches!(self.name.as_str(), "*") { self.asname
self.range() .as_ref()
} else if self.asname.is_none() { .map_or_else(|| self.name.range(), Ranged::range)
// 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")
}
} }
} }
@ -177,78 +130,20 @@ impl TryIdentifier for Pattern {
/// case *z: /// case *z:
/// ... /// ...
/// ``` /// ```
fn try_identifier(&self, locator: &Locator) -> Option<TextRange> { fn try_identifier(&self) -> Option<TextRange> {
match self { let name = match self {
Pattern::MatchAs(ast::PatternMatchAs { Pattern::MatchAs(ast::PatternMatchAs {
name: Some(_), name: Some(name), ..
pattern, }) => Some(name),
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
})
}
Pattern::MatchMapping(ast::PatternMatchMapping { Pattern::MatchMapping(ast::PatternMatchMapping {
patterns, rest: Some(rest), ..
rest: Some(_), }) => Some(rest),
.. Pattern::MatchStar(ast::PatternMatchStar {
}) => { name: Some(name), ..
Some(if let Some(pattern) = patterns.last() { }) => Some(name),
// 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"),
)
}
_ => None, _ => None,
} };
name.map(Ranged::range)
} }
} }
@ -262,24 +157,9 @@ impl TryIdentifier for ExceptHandler {
/// except ValueError as e: /// except ValueError as e:
/// ... /// ...
/// ``` /// ```
fn try_identifier(&self, locator: &Locator) -> Option<TextRange> { fn try_identifier(&self) -> Option<TextRange> {
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, name, .. }) = let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { name, .. }) = self;
self; name.as_ref().map(Ranged::range)
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"),
)
} }
} }
@ -481,137 +361,8 @@ mod tests {
use rustpython_parser::Parse; use rustpython_parser::Parse;
use crate::identifier; use crate::identifier;
use crate::identifier::Identifier;
use crate::source_code::Locator; 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] #[test]
fn extract_else_range() -> Result<()> { fn extract_else_range() -> Result<()> {
let contents = r#" let contents = r#"