diff --git a/Cargo.lock b/Cargo.lock index 89efc25897..c9df30ad26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2030,7 +2030,7 @@ dependencies = [ [[package]] name = "ruff_text_size" version = "0.0.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=0dc8fdf52d146698c5bcf0b842fddc9e398ad8db#0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" dependencies = [ "schemars", "serde", @@ -2108,7 +2108,7 @@ dependencies = [ [[package]] name = "rustpython-ast" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=0dc8fdf52d146698c5bcf0b842fddc9e398ad8db#0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" dependencies = [ "is-macro", "num-bigint", @@ -2119,7 +2119,7 @@ dependencies = [ [[package]] name = "rustpython-format" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=0dc8fdf52d146698c5bcf0b842fddc9e398ad8db#0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" dependencies = [ "bitflags 2.3.1", "itertools", @@ -2131,7 +2131,7 @@ dependencies = [ [[package]] name = "rustpython-literal" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=0dc8fdf52d146698c5bcf0b842fddc9e398ad8db#0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" dependencies = [ "hexf-parse", "is-macro", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "rustpython-parser" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=0dc8fdf52d146698c5bcf0b842fddc9e398ad8db#0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" dependencies = [ "anyhow", "is-macro", @@ -2166,7 +2166,7 @@ dependencies = [ [[package]] name = "rustpython-parser-core" version = "0.2.0" -source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd#7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" +source = "git+https://github.com/astral-sh/RustPython-Parser.git?rev=0dc8fdf52d146698c5bcf0b842fddc9e398ad8db#0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" dependencies = [ "is-macro", "ruff_text_size", diff --git a/Cargo.toml b/Cargo.toml index 8e477ffd6f..c17a7a2a69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = ["crates/*"] +resolver = "2" [workspace.package] edition = "2021" @@ -34,11 +35,11 @@ proc-macro2 = { version = "1.0.51" } quote = { version = "1.0.23" } regex = { version = "1.7.1" } rustc-hash = { version = "1.1.0" } -ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" } -rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["all-nodes-with-ranges"]} -rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" } -rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd" } -rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "7a3eedbf6fb4ea7068a1bf7fe0e97e963ea95ffd", default-features = false, features = ["full-lexer", "all-nodes-with-ranges"] } +ruff_text_size = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" } +rustpython-ast = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "0dc8fdf52d146698c5bcf0b842fddc9e398ad8db", default-features = false, features = ["all-nodes-with-ranges"]} +rustpython-format = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" } +rustpython-literal = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "0dc8fdf52d146698c5bcf0b842fddc9e398ad8db" } +rustpython-parser = { git = "https://github.com/astral-sh/RustPython-Parser.git", rev = "0dc8fdf52d146698c5bcf0b842fddc9e398ad8db", default-features = false, features = ["full-lexer", "all-nodes-with-ranges"] } schemars = { version = "0.8.12" } serde = { version = "1.0.152", features = ["derive"] } serde_json = { version = "1.0.93", features = ["preserve_order"] } diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 8b210f721d..b41b485357 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -1833,8 +1833,8 @@ where }) => { // Visit the decorators and arguments, but avoid the body, which will be // deferred. - for expr in decorator_list { - self.visit_expr(expr); + for decorator in decorator_list { + self.visit_decorator(decorator); } // Function annotations are always evaluated at runtime, unless future annotations @@ -1943,8 +1943,8 @@ where for keyword in keywords { self.visit_keyword(keyword); } - for expr in decorator_list { - self.visit_expr(expr); + for decorator in decorator_list { + self.visit_decorator(decorator); } let definition = docstrings::extraction::extract_definition( diff --git a/crates/ruff/src/rules/flake8_annotations/helpers.rs b/crates/ruff/src/rules/flake8_annotations/helpers.rs index 6813af9581..82732c5819 100644 --- a/crates/ruff/src/rules/flake8_annotations/helpers.rs +++ b/crates/ruff/src/rules/flake8_annotations/helpers.rs @@ -7,7 +7,7 @@ use ruff_python_semantic::model::SemanticModel; pub(super) fn match_function_def( stmt: &Stmt, -) -> (&str, &Arguments, Option<&Expr>, &[Stmt], &[Expr]) { +) -> (&str, &Arguments, Option<&Expr>, &[Stmt], &[ast::Decorator]) { match stmt { Stmt::FunctionDef(ast::StmtFunctionDef { name, diff --git a/crates/ruff/src/rules/flake8_boolean_trap/rules/check_boolean_default_value_in_function_definition.rs b/crates/ruff/src/rules/flake8_boolean_trap/rules/check_boolean_default_value_in_function_definition.rs index 610a6c8491..4295b60d54 100644 --- a/crates/ruff/src/rules/flake8_boolean_trap/rules/check_boolean_default_value_in_function_definition.rs +++ b/crates/ruff/src/rules/flake8_boolean_trap/rules/check_boolean_default_value_in_function_definition.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{Arguments, Expr}; +use rustpython_parser::ast::{Arguments, Decorator}; use ruff_diagnostics::Violation; @@ -61,15 +61,16 @@ impl Violation for BooleanDefaultValueInFunctionDefinition { pub(crate) fn check_boolean_default_value_in_function_definition( checker: &mut Checker, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], arguments: &Arguments, ) { if FUNC_DEF_NAME_ALLOWLIST.contains(&name) { return; } - if decorator_list.iter().any(|expr| { - collect_call_path(expr).map_or(false, |call_path| call_path.as_slice() == [name, "setter"]) + if decorator_list.iter().any(|decorator| { + collect_call_path(&decorator.expression) + .map_or(false, |call_path| call_path.as_slice() == [name, "setter"]) }) { return; } diff --git a/crates/ruff/src/rules/flake8_boolean_trap/rules/check_positional_boolean_in_def.rs b/crates/ruff/src/rules/flake8_boolean_trap/rules/check_positional_boolean_in_def.rs index 3a6f2d4df3..7d8ed6f574 100644 --- a/crates/ruff/src/rules/flake8_boolean_trap/rules/check_positional_boolean_in_def.rs +++ b/crates/ruff/src/rules/flake8_boolean_trap/rules/check_positional_boolean_in_def.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{self, Arguments, Constant, Expr, Ranged}; +use rustpython_parser::ast::{self, Arguments, Constant, Decorator, Expr, Ranged}; use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Violation; @@ -79,15 +79,16 @@ impl Violation for BooleanPositionalArgInFunctionDefinition { pub(crate) fn check_positional_boolean_in_def( checker: &mut Checker, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], arguments: &Arguments, ) { if FUNC_DEF_NAME_ALLOWLIST.contains(&name) { return; } - if decorator_list.iter().any(|expr| { - collect_call_path(expr).map_or(false, |call_path| call_path.as_slice() == [name, "setter"]) + if decorator_list.iter().any(|decorator| { + collect_call_path(&decorator.expression) + .map_or(false, |call_path| call_path.as_slice() == [name, "setter"]) }) { return; } diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs index cb19ec0904..d16bfabf1f 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{self, Expr, Ranged}; +use rustpython_parser::ast::{self, Decorator, Expr, Ranged}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -26,14 +26,14 @@ fn is_cache_func(model: &SemanticModel, expr: &Expr) -> bool { } /// B019 -pub(crate) fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) { +pub(crate) fn cached_instance_method(checker: &mut Checker, decorator_list: &[Decorator]) { if !checker.semantic_model().scope().kind.is_class() { return; } for decorator in decorator_list { // TODO(charlie): This should take into account `classmethod-decorators` and // `staticmethod-decorators`. - if let Expr::Name(ast::ExprName { id, .. }) = decorator { + if let Expr::Name(ast::ExprName { id, .. }) = &decorator.expression { if id == "classmethod" || id == "staticmethod" { return; } @@ -42,9 +42,9 @@ pub(crate) fn cached_instance_method(checker: &mut Checker, decorator_list: &[Ex for decorator in decorator_list { if is_cache_func( checker.semantic_model(), - match decorator { + match &decorator.expression { Expr::Call(ast::ExprCall { func, .. }) => func, - _ => decorator, + _ => &decorator.expression, }, ) { checker diff --git a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B019_B019.py.snap b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B019_B019.py.snap index 07e5374807..10f2fe0a08 100644 --- a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B019_B019.py.snap +++ b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B019_B019.py.snap @@ -1,81 +1,81 @@ --- source: crates/ruff/src/rules/flake8_bugbear/mod.rs --- -B019.py:78:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:78:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 78 | # Remaining methods should emit B019 79 | @functools.cache - | ^^^^^^^^^^^^^^^ B019 + | ^^^^^^^^^^^^^^^^ B019 80 | def cached_instance_method(self, y): 81 | ... | -B019.py:82:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:82:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 82 | ... 83 | 84 | @cache - | ^^^^^ B019 + | ^^^^^^ B019 85 | def another_cached_instance_method(self, y): 86 | ... | -B019.py:86:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:86:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 86 | ... 87 | 88 | @functools.cache() - | ^^^^^^^^^^^^^^^^^ B019 + | ^^^^^^^^^^^^^^^^^^ B019 89 | def called_cached_instance_method(self, y): 90 | ... | -B019.py:90:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:90:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 90 | ... 91 | 92 | @cache() - | ^^^^^^^ B019 + | ^^^^^^^^ B019 93 | def another_called_cached_instance_method(self, y): 94 | ... | -B019.py:94:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:94:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 94 | ... 95 | 96 | @functools.lru_cache - | ^^^^^^^^^^^^^^^^^^^ B019 + | ^^^^^^^^^^^^^^^^^^^^ B019 97 | def lru_cached_instance_method(self, y): 98 | ... | -B019.py:98:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:98:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 98 | ... 99 | 100 | @lru_cache - | ^^^^^^^^^ B019 + | ^^^^^^^^^^ B019 101 | def another_lru_cached_instance_method(self, y): 102 | ... | -B019.py:102:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:102:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 102 | ... 103 | 104 | @functools.lru_cache() - | ^^^^^^^^^^^^^^^^^^^^^ B019 + | ^^^^^^^^^^^^^^^^^^^^^^ B019 105 | def called_lru_cached_instance_method(self, y): 106 | ... | -B019.py:106:6: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +B019.py:106:5: B019 Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | 106 | ... 107 | 108 | @lru_cache() - | ^^^^^^^^^^^ B019 + | ^^^^^^^^^^^^ B019 109 | def another_called_lru_cached_instance_method(self, y): 110 | ... | diff --git a/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs b/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs index 1500926eca..7e68309510 100644 --- a/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs +++ b/crates/ruff/src/rules/flake8_django/rules/non_leading_receiver_decorator.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{self, Expr, Ranged}; +use rustpython_parser::ast::{self, Decorator, Expr, Ranged}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -49,7 +49,7 @@ impl Violation for DjangoNonLeadingReceiverDecorator { /// DJ013 pub(crate) fn non_leading_receiver_decorator<'a, F>( - decorator_list: &'a [Expr], + decorator_list: &'a [Decorator], resolve_call_path: F, ) -> Vec where @@ -58,7 +58,7 @@ where let mut diagnostics = vec![]; let mut seen_receiver = false; for (i, decorator) in decorator_list.iter().enumerate() { - let is_receiver = match decorator { + let is_receiver = match &decorator.expression { Expr::Call(ast::ExprCall { func, .. }) => resolve_call_path(func) .map_or(false, |call_path| { call_path.as_slice() == ["django", "dispatch", "receiver"] diff --git a/crates/ruff/src/rules/flake8_django/snapshots/ruff__rules__flake8_django__tests__DJ013_DJ013.py.snap b/crates/ruff/src/rules/flake8_django/snapshots/ruff__rules__flake8_django__tests__DJ013_DJ013.py.snap index 9867a8e089..2677ee3a76 100644 --- a/crates/ruff/src/rules/flake8_django/snapshots/ruff__rules__flake8_django__tests__DJ013_DJ013.py.snap +++ b/crates/ruff/src/rules/flake8_django/snapshots/ruff__rules__flake8_django__tests__DJ013_DJ013.py.snap @@ -1,21 +1,21 @@ --- source: crates/ruff/src/rules/flake8_django/mod.rs --- -DJ013.py:15:2: DJ013 `@receiver` decorator must be on top of all the other decorators +DJ013.py:15:1: DJ013 `@receiver` decorator must be on top of all the other decorators | 15 | @test_decorator 16 | @receiver(pre_save, sender=MyModel) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DJ013 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DJ013 17 | def incorrect_pre_save_handler(): 18 | pass | -DJ013.py:35:2: DJ013 `@receiver` decorator must be on top of all the other decorators +DJ013.py:35:1: DJ013 `@receiver` decorator must be on top of all the other decorators | 35 | @receiver(pre_save, sender=MyModel) 36 | @test_decorator 37 | @receiver(pre_save, sender=MyModel) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DJ013 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DJ013 38 | def incorrect_multiple(): 39 | pass | diff --git a/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs b/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs index 52a01ef709..a5dd877d44 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/non_self_return_type.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{self, Arguments, Expr, Stmt}; +use rustpython_parser::ast::{self, Arguments, Decorator, Expr, Stmt}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -114,7 +114,7 @@ pub(crate) fn non_self_return_type( checker: &mut Checker, stmt: &Stmt, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], returns: Option<&Expr>, args: &Arguments, async_: bool, diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs index 6d019d9000..c0c53e8e3a 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs @@ -9,6 +9,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::call_path::collect_call_path; use ruff_python_ast::helpers::collect_arg_names; +use ruff_python_ast::prelude::Decorator; use ruff_python_ast::source_code::Locator; use ruff_python_ast::visitor::Visitor; use ruff_python_ast::{helpers, visitor}; @@ -243,7 +244,10 @@ where } } -fn get_fixture_decorator<'a>(model: &SemanticModel, decorators: &'a [Expr]) -> Option<&'a Expr> { +fn get_fixture_decorator<'a>( + model: &SemanticModel, + decorators: &'a [Decorator], +) -> Option<&'a Decorator> { decorators.iter().find(|decorator| { is_pytest_fixture(model, decorator) || is_pytest_yield_fixture(model, decorator) }) @@ -251,7 +255,7 @@ fn get_fixture_decorator<'a>(model: &SemanticModel, decorators: &'a [Expr]) -> O fn pytest_fixture_parentheses( checker: &mut Checker, - decorator: &Expr, + decorator: &Decorator, fix: Fix, expected: Parentheses, actual: Parentheses, @@ -277,8 +281,8 @@ pub(crate) fn fix_extraneous_scope_function( } /// PT001, PT002, PT003 -fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &Expr) { - match decorator { +fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &Decorator) { + match &decorator.expression { Expr::Call(ast::ExprCall { func, args, @@ -431,7 +435,7 @@ fn check_test_function_args(checker: &mut Checker, args: &Arguments) { } /// PT020 -fn check_fixture_decorator_name(checker: &mut Checker, decorator: &Expr) { +fn check_fixture_decorator_name(checker: &mut Checker, decorator: &Decorator) { if is_pytest_yield_fixture(checker.semantic_model(), decorator) { checker.diagnostics.push(Diagnostic::new( PytestDeprecatedYieldFixture, @@ -461,7 +465,7 @@ fn check_fixture_addfinalizer(checker: &mut Checker, args: &Arguments, body: &[S } /// PT024, PT025 -fn check_fixture_marks(checker: &mut Checker, decorators: &[Expr]) { +fn check_fixture_marks(checker: &mut Checker, decorators: &[Decorator]) { for (expr, call_path) in get_mark_decorators(decorators) { let name = call_path.last().expect("Expected a mark name"); if checker.enabled(Rule::PytestUnnecessaryAsyncioMarkOnFixture) { @@ -497,7 +501,7 @@ pub(crate) fn fixture( stmt: &Stmt, name: &str, args: &Arguments, - decorators: &[Expr], + decorators: &[Decorator], body: &[Stmt], ) { let decorator = get_fixture_decorator(checker.semantic_model(), decorators); diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs index 36beb36f79..248005a0c7 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs @@ -1,12 +1,14 @@ -use rustpython_parser::ast::{self, Constant, Expr, Keyword}; +use rustpython_parser::ast::{self, Constant, Decorator, Expr, Keyword}; use ruff_python_ast::call_path::{collect_call_path, CallPath}; use ruff_python_ast::helpers::map_callable; use ruff_python_semantic::model::SemanticModel; -pub(super) fn get_mark_decorators(decorators: &[Expr]) -> impl Iterator { +pub(super) fn get_mark_decorators( + decorators: &[Decorator], +) -> impl Iterator { decorators.iter().filter_map(|decorator| { - let Some(call_path) = collect_call_path(map_callable(decorator)) else { + let Some(call_path) = collect_call_path(map_callable(&decorator.expression)) else { return None; }; if call_path.len() > 2 && call_path.as_slice()[..2] == ["pytest", "mark"] { @@ -23,25 +25,25 @@ pub(super) fn is_pytest_fail(model: &SemanticModel, call: &Expr) -> bool { }) } -pub(super) fn is_pytest_fixture(model: &SemanticModel, decorator: &Expr) -> bool { +pub(super) fn is_pytest_fixture(model: &SemanticModel, decorator: &Decorator) -> bool { model - .resolve_call_path(map_callable(decorator)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["pytest", "fixture"] }) } -pub(super) fn is_pytest_yield_fixture(model: &SemanticModel, decorator: &Expr) -> bool { +pub(super) fn is_pytest_yield_fixture(model: &SemanticModel, decorator: &Decorator) -> bool { model - .resolve_call_path(map_callable(decorator)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["pytest", "yield_fixture"] }) } -pub(super) fn is_pytest_parametrize(model: &SemanticModel, decorator: &Expr) -> bool { +pub(super) fn is_pytest_parametrize(model: &SemanticModel, decorator: &Decorator) -> bool { model - .resolve_call_path(map_callable(decorator)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["pytest", "mark", "parametrize"] }) diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/marks.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/marks.rs index 6c2e178a76..40b34ea309 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/marks.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/marks.rs @@ -1,5 +1,4 @@ -use ruff_text_size::TextSize; -use rustpython_parser::ast::{self, Expr, Ranged}; +use rustpython_parser::ast::{self, Decorator, Expr, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; @@ -52,7 +51,7 @@ impl AlwaysAutofixableViolation for PytestUseFixturesWithoutParameters { fn pytest_mark_parentheses( checker: &mut Checker, - decorator: &Expr, + decorator: &Decorator, call_path: &CallPath, fix: Fix, preferred: &str, @@ -72,8 +71,8 @@ fn pytest_mark_parentheses( checker.diagnostics.push(diagnostic); } -fn check_mark_parentheses(checker: &mut Checker, decorator: &Expr, call_path: &CallPath) { - match decorator { +fn check_mark_parentheses(checker: &mut Checker, decorator: &Decorator, call_path: &CallPath) { + match &decorator.expression { Expr::Call(ast::ExprCall { func, args, @@ -99,14 +98,14 @@ fn check_mark_parentheses(checker: &mut Checker, decorator: &Expr, call_path: &C } } -fn check_useless_usefixtures(checker: &mut Checker, decorator: &Expr, call_path: &CallPath) { +fn check_useless_usefixtures(checker: &mut Checker, decorator: &Decorator, call_path: &CallPath) { if *call_path.last().unwrap() != "usefixtures" { return; } let mut has_parameters = false; - if let Expr::Call(ast::ExprCall { args, keywords, .. }) = decorator { + if let Expr::Call(ast::ExprCall { args, keywords, .. }) = &decorator.expression { if !args.is_empty() || !keywords.is_empty() { has_parameters = true; } @@ -116,24 +115,22 @@ fn check_useless_usefixtures(checker: &mut Checker, decorator: &Expr, call_path: let mut diagnostic = Diagnostic::new(PytestUseFixturesWithoutParameters, decorator.range()); if checker.patch(diagnostic.kind.rule()) { #[allow(deprecated)] - diagnostic.set_fix(Fix::unspecified(Edit::range_deletion( - decorator.range().sub_start(TextSize::from(1)), - ))); + diagnostic.set_fix(Fix::unspecified(Edit::range_deletion(decorator.range()))); } checker.diagnostics.push(diagnostic); } } -pub(crate) fn marks(checker: &mut Checker, decorators: &[Expr]) { +pub(crate) fn marks(checker: &mut Checker, decorators: &[Decorator]) { let enforce_parentheses = checker.enabled(Rule::PytestIncorrectMarkParenthesesStyle); let enforce_useless_usefixtures = checker.enabled(Rule::PytestUseFixturesWithoutParameters); - for (expr, call_path) in get_mark_decorators(decorators) { + for (decorator, call_path) in get_mark_decorators(decorators) { if enforce_parentheses { - check_mark_parentheses(checker, expr, &call_path); + check_mark_parentheses(checker, decorator, &call_path); } if enforce_useless_usefixtures { - check_useless_usefixtures(checker, expr, &call_path); + check_useless_usefixtures(checker, decorator, &call_path); } } } diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/parametrize.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/parametrize.rs index 51e8f0a1fe..a0a3b2e75b 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/parametrize.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/parametrize.rs @@ -1,5 +1,5 @@ use ruff_text_size::TextRange; -use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged}; +use rustpython_parser::ast::{self, Constant, Decorator, Expr, ExprContext, Ranged}; use rustpython_parser::{lexer, Mode, Tok}; use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; @@ -94,7 +94,7 @@ fn elts_to_csv(elts: &[Expr], generator: Generator) -> Option { /// ``` /// /// This method assumes that the first argument is a string. -fn get_parametrize_name_range(decorator: &Expr, expr: &Expr, locator: &Locator) -> TextRange { +fn get_parametrize_name_range(decorator: &Decorator, expr: &Expr, locator: &Locator) -> TextRange { let mut locations = Vec::new(); let mut implicit_concat = None; @@ -128,7 +128,7 @@ fn get_parametrize_name_range(decorator: &Expr, expr: &Expr, locator: &Locator) } /// PT006 -fn check_names(checker: &mut Checker, decorator: &Expr, expr: &Expr) { +fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) { let names_type = checker.settings.flake8_pytest_style.parametrize_names_type; match expr { @@ -417,10 +417,10 @@ fn handle_value_rows( } } -pub(crate) fn parametrize(checker: &mut Checker, decorators: &[Expr]) { +pub(crate) fn parametrize(checker: &mut Checker, decorators: &[Decorator]) { for decorator in decorators { if is_pytest_parametrize(checker.semantic_model(), decorator) { - if let Expr::Call(ast::ExprCall { args, .. }) = decorator { + if let Expr::Call(ast::ExprCall { args, .. }) = &decorator.expression { if checker.enabled(Rule::PytestParametrizeNamesWrongType) { if let Some(names) = args.get(0) { check_names(checker, decorator, names); diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_default.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_default.snap index c17efd2cf3..16ef00156f 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_default.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_default.snap @@ -1,10 +1,10 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT001.py:9:2: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` +PT001.py:9:1: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` | 9 | @pytest.fixture - | ^^^^^^^^^^^^^^ PT001 + | ^^^^^^^^^^^^^^^ PT001 10 | def no_parentheses(): 11 | return 42 | @@ -20,10 +20,10 @@ PT001.py:9:2: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` 11 11 | return 42 12 12 | -PT001.py:34:2: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` +PT001.py:34:1: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` | 34 | @fixture - | ^^^^^^^ PT001 + | ^^^^^^^^ PT001 35 | def imported_from_no_parentheses(): 36 | return 42 | @@ -39,10 +39,10 @@ PT001.py:34:2: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` 36 36 | return 42 37 37 | -PT001.py:59:2: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` +PT001.py:59:1: PT001 [*] Use `@pytest.fixture()` over `@pytest.fixture` | 59 | @aliased - | ^^^^^^^ PT001 + | ^^^^^^^^ PT001 60 | def aliased_no_parentheses(): 61 | return 42 | diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_no_parentheses.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_no_parentheses.snap index 3dc9a14d43..ffbff683cf 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_no_parentheses.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT001_no_parentheses.snap @@ -1,10 +1,10 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT001.py:14:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` +PT001.py:14:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` | 14 | @pytest.fixture() - | ^^^^^^^^^^^^^^^^ PT001 + | ^^^^^^^^^^^^^^^^^ PT001 15 | def parentheses_no_params(): 16 | return 42 | @@ -20,10 +20,9 @@ PT001.py:14:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` 16 16 | return 42 17 17 | -PT001.py:24:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` +PT001.py:24:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` | -24 | @pytest.fixture( - | __^ +24 | / @pytest.fixture( 25 | | 26 | | ) | |_^ PT001 @@ -44,10 +43,10 @@ PT001.py:24:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` 28 26 | return 42 29 27 | -PT001.py:39:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` +PT001.py:39:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` | 39 | @fixture() - | ^^^^^^^^^ PT001 + | ^^^^^^^^^^ PT001 40 | def imported_from_parentheses_no_params(): 41 | return 42 | @@ -63,10 +62,9 @@ PT001.py:39:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` 41 41 | return 42 42 42 | -PT001.py:49:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` +PT001.py:49:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` | -49 | @fixture( - | __^ +49 | / @fixture( 50 | | 51 | | ) | |_^ PT001 @@ -87,10 +85,10 @@ PT001.py:49:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` 53 51 | return 42 54 52 | -PT001.py:64:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` +PT001.py:64:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` | 64 | @aliased() - | ^^^^^^^^^ PT001 + | ^^^^^^^^^^ PT001 65 | def aliased_parentheses_no_params(): 66 | return 42 | @@ -106,10 +104,9 @@ PT001.py:64:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` 66 66 | return 42 67 67 | -PT001.py:74:2: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` +PT001.py:74:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` | -74 | @aliased( - | __^ +74 | / @aliased( 75 | | 76 | | ) | |_^ PT001 diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT002.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT002.snap index 4f923db4cd..2f5da96213 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT002.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT002.snap @@ -1,18 +1,18 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT002.py:14:2: PT002 Configuration for fixture `my_fixture` specified via positional args, use kwargs +PT002.py:14:1: PT002 Configuration for fixture `my_fixture` specified via positional args, use kwargs | 14 | @pytest.fixture("module") - | ^^^^^^^^^^^^^^^^^^^^^^^^ PT002 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PT002 15 | def my_fixture(): # Error only args 16 | return 0 | -PT002.py:19:2: PT002 Configuration for fixture `my_fixture` specified via positional args, use kwargs +PT002.py:19:1: PT002 Configuration for fixture `my_fixture` specified via positional args, use kwargs | 19 | @pytest.fixture("module", autouse=True) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PT002 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PT002 20 | def my_fixture(): # Error mixed 21 | return 0 | diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT020.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT020.snap index 389b6882dd..258108b551 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT020.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT020.snap @@ -1,18 +1,18 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT020.py:14:2: PT020 `@pytest.yield_fixture` is deprecated, use `@pytest.fixture` +PT020.py:14:1: PT020 `@pytest.yield_fixture` is deprecated, use `@pytest.fixture` | 14 | @pytest.yield_fixture() - | ^^^^^^^^^^^^^^^^^^^^^^ PT020 + | ^^^^^^^^^^^^^^^^^^^^^^^ PT020 15 | def error_without_parens(): 16 | return 0 | -PT020.py:19:2: PT020 `@pytest.yield_fixture` is deprecated, use `@pytest.fixture` +PT020.py:19:1: PT020 `@pytest.yield_fixture` is deprecated, use `@pytest.fixture` | 19 | @pytest.yield_fixture - | ^^^^^^^^^^^^^^^^^^^^ PT020 + | ^^^^^^^^^^^^^^^^^^^^^ PT020 20 | def error_with_parens(): 21 | return 0 | diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_default.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_default.snap index e5ac493591..9b1fa66de9 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_default.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_default.snap @@ -1,10 +1,10 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT023.py:12:2: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` +PT023.py:12:1: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` | 12 | @pytest.mark.foo - | ^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^ PT023 13 | def test_something(): 14 | pass | @@ -20,10 +20,10 @@ PT023.py:12:2: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` 14 14 | pass 15 15 | -PT023.py:17:2: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` +PT023.py:17:1: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` | 17 | @pytest.mark.foo - | ^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^ PT023 18 | class TestClass: 19 | def test_something(): | @@ -39,11 +39,11 @@ PT023.py:17:2: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` 19 19 | def test_something(): 20 20 | pass -PT023.py:24:6: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` +PT023.py:24:5: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` | 24 | class TestClass: 25 | @pytest.mark.foo - | ^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^ PT023 26 | def test_something(): 27 | pass | @@ -59,11 +59,11 @@ PT023.py:24:6: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` 26 26 | pass 27 27 | -PT023.py:30:6: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` +PT023.py:30:5: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` | 30 | class TestClass: 31 | @pytest.mark.foo - | ^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^ PT023 32 | class TestNestedClass: 33 | def test_something(): | @@ -79,12 +79,12 @@ PT023.py:30:6: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` 32 32 | def test_something(): 33 33 | pass -PT023.py:38:10: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` +PT023.py:38:9: PT023 [*] Use `@pytest.mark.foo()` over `@pytest.mark.foo` | 38 | class TestClass: 39 | class TestNestedClass: 40 | @pytest.mark.foo - | ^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^ PT023 41 | def test_something(): 42 | pass | diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_no_parentheses.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_no_parentheses.snap index 27ef92121d..742f4f726e 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_no_parentheses.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT023_no_parentheses.snap @@ -1,10 +1,10 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT023.py:46:2: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` +PT023.py:46:1: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` | 46 | @pytest.mark.foo() - | ^^^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^^^ PT023 47 | def test_something(): 48 | pass | @@ -20,10 +20,10 @@ PT023.py:46:2: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` 48 48 | pass 49 49 | -PT023.py:51:2: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` +PT023.py:51:1: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` | 51 | @pytest.mark.foo() - | ^^^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^^^ PT023 52 | class TestClass: 53 | def test_something(): | @@ -39,11 +39,11 @@ PT023.py:51:2: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` 53 53 | def test_something(): 54 54 | pass -PT023.py:58:6: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` +PT023.py:58:5: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` | 58 | class TestClass: 59 | @pytest.mark.foo() - | ^^^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^^^ PT023 60 | def test_something(): 61 | pass | @@ -59,11 +59,11 @@ PT023.py:58:6: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` 60 60 | pass 61 61 | -PT023.py:64:6: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` +PT023.py:64:5: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` | 64 | class TestClass: 65 | @pytest.mark.foo() - | ^^^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^^^ PT023 66 | class TestNestedClass: 67 | def test_something(): | @@ -79,12 +79,12 @@ PT023.py:64:6: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` 66 66 | def test_something(): 67 67 | pass -PT023.py:72:10: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` +PT023.py:72:9: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` | 72 | class TestClass: 73 | class TestNestedClass: 74 | @pytest.mark.foo() - | ^^^^^^^^^^^^^^^^^ PT023 + | ^^^^^^^^^^^^^^^^^^ PT023 75 | def test_something(): 76 | pass | diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT024.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT024.snap index 3809d5928e..a7c4566ae8 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT024.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT024.snap @@ -1,10 +1,10 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT024.py:14:2: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures +PT024.py:14:1: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures | 14 | @pytest.mark.asyncio() - | ^^^^^^^^^^^^^^^^^^^^^ PT024 + | ^^^^^^^^^^^^^^^^^^^^^^ PT024 15 | @pytest.fixture() 16 | async def my_fixture(): # Error before | @@ -19,10 +19,10 @@ PT024.py:14:2: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures 16 15 | async def my_fixture(): # Error before 17 16 | return 0 -PT024.py:20:2: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures +PT024.py:20:1: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures | 20 | @pytest.mark.asyncio - | ^^^^^^^^^^^^^^^^^^^ PT024 + | ^^^^^^^^^^^^^^^^^^^^ PT024 21 | @pytest.fixture() 22 | async def my_fixture(): # Error before no parens | @@ -37,11 +37,11 @@ PT024.py:20:2: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures 22 21 | async def my_fixture(): # Error before no parens 23 22 | return 0 -PT024.py:27:2: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures +PT024.py:27:1: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures | 27 | @pytest.fixture() 28 | @pytest.mark.asyncio() - | ^^^^^^^^^^^^^^^^^^^^^ PT024 + | ^^^^^^^^^^^^^^^^^^^^^^ PT024 29 | async def my_fixture(): # Error after 30 | return 0 | @@ -56,11 +56,11 @@ PT024.py:27:2: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures 29 28 | return 0 30 29 | -PT024.py:33:2: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures +PT024.py:33:1: PT024 [*] `pytest.mark.asyncio` is unnecessary for fixtures | 33 | @pytest.fixture() 34 | @pytest.mark.asyncio - | ^^^^^^^^^^^^^^^^^^^ PT024 + | ^^^^^^^^^^^^^^^^^^^^ PT024 35 | async def my_fixture(): # Error after no parens 36 | return 0 | diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT025.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT025.snap index 23296b9440..76c3580d97 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT025.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT025.snap @@ -1,10 +1,10 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT025.py:9:2: PT025 [*] `pytest.mark.usefixtures` has no effect on fixtures +PT025.py:9:1: PT025 [*] `pytest.mark.usefixtures` has no effect on fixtures | 9 | @pytest.mark.usefixtures("a") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PT025 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PT025 10 | @pytest.fixture() 11 | def my_fixture(): # Error before | @@ -19,11 +19,11 @@ PT025.py:9:2: PT025 [*] `pytest.mark.usefixtures` has no effect on fixtures 11 10 | def my_fixture(): # Error before 12 11 | return 0 -PT025.py:16:2: PT025 [*] `pytest.mark.usefixtures` has no effect on fixtures +PT025.py:16:1: PT025 [*] `pytest.mark.usefixtures` has no effect on fixtures | 16 | @pytest.fixture() 17 | @pytest.mark.usefixtures("a") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PT025 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PT025 18 | def my_fixture(): # Error after 19 | return 0 | diff --git a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT026.snap b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT026.snap index f08e80be28..1b22f5b2c9 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT026.snap +++ b/crates/ruff/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT026.snap @@ -1,10 +1,10 @@ --- source: crates/ruff/src/rules/flake8_pytest_style/mod.rs --- -PT026.py:19:2: PT026 [*] Useless `pytest.mark.usefixtures` without parameters +PT026.py:19:1: PT026 [*] Useless `pytest.mark.usefixtures` without parameters | 19 | @pytest.mark.usefixtures() - | ^^^^^^^^^^^^^^^^^^^^^^^^^ PT026 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ PT026 20 | def test_error_with_parens(): 21 | pass | @@ -20,10 +20,10 @@ PT026.py:19:2: PT026 [*] Useless `pytest.mark.usefixtures` without parameters 21 21 | pass 22 22 | -PT026.py:24:2: PT026 [*] Useless `pytest.mark.usefixtures` without parameters +PT026.py:24:1: PT026 [*] Useless `pytest.mark.usefixtures` without parameters | 24 | @pytest.mark.usefixtures - | ^^^^^^^^^^^^^^^^^^^^^^^ PT026 + | ^^^^^^^^^^^^^^^^^^^^^^^^ PT026 25 | def test_error_no_parens(): 26 | pass | diff --git a/crates/ruff/src/rules/flake8_return/visitor.rs b/crates/ruff/src/rules/flake8_return/visitor.rs index f0d419c7bc..c6ac93c4ce 100644 --- a/crates/ruff/src/rules/flake8_return/visitor.rs +++ b/crates/ruff/src/rules/flake8_return/visitor.rs @@ -87,8 +87,8 @@ impl<'a> Visitor<'a> for ReturnVisitor<'a> { .push(stmt.start()); // Don't recurse into the body, but visit the decorators, etc. - for expr in decorator_list { - visitor::walk_expr(self, expr); + for decorator in decorator_list { + visitor::walk_decorator(self, decorator); } } Stmt::FunctionDef(ast::StmtFunctionDef { @@ -113,8 +113,8 @@ impl<'a> Visitor<'a> for ReturnVisitor<'a> { .push(stmt.start()); // Don't recurse into the body, but visit the decorators, etc. - for expr in decorator_list { - visitor::walk_expr(self, expr); + for decorator in decorator_list { + visitor::walk_decorator(self, decorator); } if let Some(returns) = returns { visitor::walk_expr(self, returns); diff --git a/crates/ruff/src/rules/flake8_type_checking/helpers.rs b/crates/ruff/src/rules/flake8_type_checking/helpers.rs index 6b509f058e..53f8f1fa93 100644 --- a/crates/ruff/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff/src/rules/flake8_type_checking/helpers.rs @@ -63,7 +63,9 @@ fn runtime_evaluated_decorators(semantic_model: &SemanticModel, decorators: &[St if let ScopeKind::Class(ast::StmtClassDef { decorator_list, .. }) = &semantic_model.scope().kind { for decorator in decorator_list.iter() { - if let Some(call_path) = semantic_model.resolve_call_path(map_callable(decorator)) { + if let Some(call_path) = + semantic_model.resolve_call_path(map_callable(&decorator.expression)) + { if decorators .iter() .any(|decorator| from_qualified_name(decorator) == call_path) diff --git a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs index df5e7c64bc..05f42674d1 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{Arguments, Expr, Ranged}; +use rustpython_parser::ast::{Arguments, Decorator, Ranged}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -59,7 +59,7 @@ pub(crate) fn invalid_first_argument_name_for_class_method( checker: &Checker, scope: &Scope, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], args: &Arguments, ) -> Option { if !matches!( diff --git a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs index c0e0cb3895..eeedca93c1 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{Arguments, Expr, Ranged}; +use rustpython_parser::ast::{Arguments, Decorator, Ranged}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -56,7 +56,7 @@ pub(crate) fn invalid_first_argument_name_for_method( checker: &Checker, scope: &Scope, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], args: &Arguments, ) -> Option { if !matches!( diff --git a/crates/ruff/src/rules/pep8_naming/rules/invalid_function_name.rs b/crates/ruff/src/rules/pep8_naming/rules/invalid_function_name.rs index 1b908c2256..3713bc8ef2 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/invalid_function_name.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/invalid_function_name.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{Expr, Stmt}; +use rustpython_parser::ast::{Decorator, Stmt}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -51,7 +51,7 @@ impl Violation for InvalidFunctionName { pub(crate) fn invalid_function_name( stmt: &Stmt, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], ignore_names: &[String], model: &SemanticModel, locator: &Locator, diff --git a/crates/ruff/src/rules/pydocstyle/helpers.rs b/crates/ruff/src/rules/pydocstyle/helpers.rs index 9cc9d84e11..3c22305049 100644 --- a/crates/ruff/src/rules/pydocstyle/helpers.rs +++ b/crates/ruff/src/rules/pydocstyle/helpers.rs @@ -51,7 +51,7 @@ pub(crate) fn should_ignore_definition( }) = definition { for decorator in cast::decorator_list(stmt) { - if let Some(call_path) = model.resolve_call_path(map_callable(decorator)) { + if let Some(call_path) = model.resolve_call_path(map_callable(&decorator.expression)) { if ignore_decorators .iter() .any(|decorator| from_qualified_name(decorator) == call_path) diff --git a/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs b/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs index 79b06331a4..d63dd5025b 100644 --- a/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs +++ b/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{self, Arguments, Expr, Stmt}; +use rustpython_parser::ast::{self, Arguments, Decorator, Expr, Stmt}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -50,12 +50,12 @@ impl Violation for PropertyWithParameters { pub(crate) fn property_with_parameters( checker: &mut Checker, stmt: &Stmt, - decorator_list: &[Expr], + decorator_list: &[Decorator], args: &Arguments, ) { if !decorator_list .iter() - .any(|d| matches!(&d, Expr::Name(ast::ExprName { id, .. }) if id == "property")) + .any(|d| matches!(&d.expression, Expr::Name(ast::ExprName { id, .. }) if id == "property")) { return; } diff --git a/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs b/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs index 94b8cdbf0a..e22a2a2655 100644 --- a/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs +++ b/crates/ruff/src/rules/pylint/rules/unexpected_special_method_signature.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use rustpython_parser::ast::{Arguments, Expr, Stmt}; +use rustpython_parser::ast::{Arguments, Decorator, Stmt}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -141,7 +141,7 @@ pub(crate) fn unexpected_special_method_signature( checker: &mut Checker, stmt: &Stmt, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], args: &Arguments, locator: &Locator, ) { diff --git a/crates/ruff/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs b/crates/ruff/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs index 9303ddfd28..9ecf931f2c 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/lru_cache_with_maxsize_none.rs @@ -1,5 +1,5 @@ use ruff_text_size::TextRange; -use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged}; +use rustpython_parser::ast::{self, Constant, Decorator, Expr, Keyword, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; @@ -23,14 +23,14 @@ impl AlwaysAutofixableViolation for LRUCacheWithMaxsizeNone { } /// UP033 -pub(crate) fn lru_cache_with_maxsize_none(checker: &mut Checker, decorator_list: &[Expr]) { - for expr in decorator_list.iter() { +pub(crate) fn lru_cache_with_maxsize_none(checker: &mut Checker, decorator_list: &[Decorator]) { + for decorator in decorator_list.iter() { let Expr::Call(ast::ExprCall { func, args, keywords, range: _, - }) = expr else { + }) = &decorator.expression else { continue; }; @@ -61,16 +61,17 @@ pub(crate) fn lru_cache_with_maxsize_none(checker: &mut Checker, decorator_list: { let mut diagnostic = Diagnostic::new( LRUCacheWithMaxsizeNone, - TextRange::new(func.end(), expr.end()), + TextRange::new(func.end(), decorator.end()), ); if checker.patch(diagnostic.kind.rule()) { diagnostic.try_set_fix(|| { let (import_edit, binding) = checker.importer.get_or_import_symbol( &ImportRequest::import("functools", "cache"), - expr.start(), + decorator.start(), checker.semantic_model(), )?; - let reference_edit = Edit::range_replacement(binding, expr.range()); + let reference_edit = + Edit::range_replacement(binding, decorator.expression.range()); #[allow(deprecated)] Ok(Fix::unspecified_edits(import_edit, [reference_edit])) }); diff --git a/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs b/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs index f276d8f8e5..0f482afb58 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs @@ -1,5 +1,5 @@ use ruff_text_size::TextRange; -use rustpython_parser::ast::{self, Expr, Ranged}; +use rustpython_parser::ast::{self, Decorator, Expr, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; @@ -22,14 +22,14 @@ impl AlwaysAutofixableViolation for LRUCacheWithoutParameters { } /// UP011 -pub(crate) fn lru_cache_without_parameters(checker: &mut Checker, decorator_list: &[Expr]) { - for expr in decorator_list.iter() { +pub(crate) fn lru_cache_without_parameters(checker: &mut Checker, decorator_list: &[Decorator]) { + for decorator in decorator_list.iter() { let Expr::Call(ast::ExprCall { func, args, keywords, range: _, - }) = expr else { + }) = &decorator.expression else { continue; }; @@ -45,13 +45,13 @@ pub(crate) fn lru_cache_without_parameters(checker: &mut Checker, decorator_list { let mut diagnostic = Diagnostic::new( LRUCacheWithoutParameters, - TextRange::new(func.end(), expr.end()), + TextRange::new(func.end(), decorator.end()), ); if checker.patch(diagnostic.kind.rule()) { #[allow(deprecated)] diagnostic.set_fix(Fix::unspecified(Edit::range_replacement( checker.generator().expr(func), - expr.range(), + decorator.expression.range(), ))); } checker.diagnostics.push(diagnostic); diff --git a/crates/ruff/src/rules/ruff/rules/mutable_defaults_in_dataclass_fields.rs b/crates/ruff/src/rules/ruff/rules/mutable_defaults_in_dataclass_fields.rs index b1c72ecdf8..1f300b303b 100644 --- a/crates/ruff/src/rules/ruff/rules/mutable_defaults_in_dataclass_fields.rs +++ b/crates/ruff/src/rules/ruff/rules/mutable_defaults_in_dataclass_fields.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{self, Expr, Ranged, Stmt}; +use rustpython_parser::ast::{self, Decorator, Expr, Ranged, Stmt}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; @@ -244,10 +244,10 @@ pub(crate) fn mutable_dataclass_default(checker: &mut Checker, body: &[Stmt]) { } } -pub(crate) fn is_dataclass(model: &SemanticModel, decorator_list: &[Expr]) -> bool { +pub(crate) fn is_dataclass(model: &SemanticModel, decorator_list: &[Decorator]) -> bool { decorator_list.iter().any(|decorator| { model - .resolve_call_path(map_callable(decorator)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["dataclasses", "dataclass"] }) diff --git a/crates/ruff_python_ast/src/cast.rs b/crates/ruff_python_ast/src/cast.rs index 7e75ac4998..98889d6673 100644 --- a/crates/ruff_python_ast/src/cast.rs +++ b/crates/ruff_python_ast/src/cast.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::{self, Expr, Stmt}; +use rustpython_parser::ast::{self, Decorator, Stmt}; pub fn name(stmt: &Stmt) -> &str { match stmt { @@ -8,7 +8,7 @@ pub fn name(stmt: &Stmt) -> &str { } } -pub fn decorator_list(stmt: &Stmt) -> &[Expr] { +pub fn decorator_list(stmt: &Stmt) -> &[Decorator] { match stmt { Stmt::FunctionDef(ast::StmtFunctionDef { decorator_list, .. }) | Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { decorator_list, .. }) => { diff --git a/crates/ruff_python_ast/src/comparable.rs b/crates/ruff_python_ast/src/comparable.rs index 3d6cba47ef..6477644477 100644 --- a/crates/ruff_python_ast/src/comparable.rs +++ b/crates/ruff_python_ast/src/comparable.rs @@ -2,6 +2,7 @@ //! ability to compare expressions for equality (via [`Eq`] and [`Hash`]). use num_bigint::BigInt; +use rustpython_ast::Decorator; use rustpython_parser::ast::{ self, Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, ConversionFlag, Excepthandler, Expr, ExprContext, Identifier, Int, Keyword, MatchCase, Operator, Pattern, Stmt, @@ -267,6 +268,19 @@ impl<'a> From<&'a MatchCase> for ComparableMatchCase<'a> { } } +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct ComparableDecorator<'a> { + pub expression: ComparableExpr<'a>, +} + +impl<'a> From<&'a Decorator> for ComparableDecorator<'a> { + fn from(decorator: &'a Decorator) -> Self { + Self { + expression: (&decorator.expression).into(), + } + } +} + #[derive(Debug, PartialEq, Eq, Hash)] pub enum ComparableConstant<'a> { None, @@ -777,7 +791,7 @@ pub enum ComparableStmt<'a> { name: &'a str, args: ComparableArguments<'a>, body: Vec>, - decorator_list: Vec>, + decorator_list: Vec>, returns: Option>, type_comment: Option<&'a str>, }, @@ -785,7 +799,7 @@ pub enum ComparableStmt<'a> { name: &'a str, args: ComparableArguments<'a>, body: Vec>, - decorator_list: Vec>, + decorator_list: Vec>, returns: Option>, type_comment: Option<&'a str>, }, @@ -794,7 +808,7 @@ pub enum ComparableStmt<'a> { bases: Vec>, keywords: Vec>, body: Vec>, - decorator_list: Vec>, + decorator_list: Vec>, }, Return { value: Option>, diff --git a/crates/ruff_python_ast/src/function.rs b/crates/ruff_python_ast/src/function.rs index 1f7f114f69..924e729248 100644 --- a/crates/ruff_python_ast/src/function.rs +++ b/crates/ruff_python_ast/src/function.rs @@ -1,7 +1,7 @@ use crate::node::AnyNodeRef; use ruff_text_size::TextRange; use rustpython_ast::{ - Arguments, Expr, Identifier, Ranged, StmtAsyncFunctionDef, StmtFunctionDef, Suite, + Arguments, Decorator, Expr, Identifier, Ranged, StmtAsyncFunctionDef, StmtFunctionDef, Suite, }; /// Enum that represents any python function definition. @@ -65,7 +65,7 @@ impl<'a> AnyFunctionDefinition<'a> { } /// Returns the decorators attributing the function. - pub fn decorators(self) -> &'a [Expr] { + pub fn decorators(self) -> &'a [Decorator] { match self { Self::FunctionDefinition(definition) => &definition.decorator_list, Self::AsyncFunctionDefinition(definition) => &definition.decorator_list, diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 0f3340c1f7..9789e1abcb 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -363,7 +363,9 @@ where .map_or(false, |expr| any_over_expr(expr, func)) }) || body.iter().any(|stmt| any_over_stmt(stmt, func)) - || decorator_list.iter().any(|expr| any_over_expr(expr, func)) + || decorator_list + .iter() + .any(|decorator| any_over_expr(&decorator.expression, func)) || returns .as_ref() .map_or(false, |value| any_over_expr(value, func)) @@ -380,7 +382,9 @@ where .iter() .any(|keyword| any_over_expr(&keyword.value, func)) || body.iter().any(|stmt| any_over_stmt(stmt, func)) - || decorator_list.iter().any(|expr| any_over_expr(expr, func)) + || decorator_list + .iter() + .any(|decorator| any_over_expr(&decorator.expression, func)) } Stmt::Return(ast::StmtReturn { value, diff --git a/crates/ruff_python_ast/src/node.rs b/crates/ruff_python_ast/src/node.rs index 96c77e8fdc..1c8d66ee51 100644 --- a/crates/ruff_python_ast/src/node.rs +++ b/crates/ruff_python_ast/src/node.rs @@ -92,6 +92,7 @@ pub enum AnyNode { Alias(Alias), Withitem(Withitem), MatchCase(MatchCase), + Decorator(Decorator), } impl AnyNode { @@ -172,7 +173,8 @@ impl AnyNode { | AnyNode::Keyword(_) | AnyNode::Alias(_) | AnyNode::Withitem(_) - | AnyNode::MatchCase(_) => None, + | AnyNode::MatchCase(_) + | AnyNode::Decorator(_) => None, } } @@ -253,7 +255,8 @@ impl AnyNode { | AnyNode::Keyword(_) | AnyNode::Alias(_) | AnyNode::Withitem(_) - | AnyNode::MatchCase(_) => None, + | AnyNode::MatchCase(_) + | AnyNode::Decorator(_) => None, } } @@ -334,7 +337,8 @@ impl AnyNode { | AnyNode::Keyword(_) | AnyNode::Alias(_) | AnyNode::Withitem(_) - | AnyNode::MatchCase(_) => None, + | AnyNode::MatchCase(_) + | AnyNode::Decorator(_) => None, } } @@ -415,7 +419,8 @@ impl AnyNode { | AnyNode::Keyword(_) | AnyNode::Alias(_) | AnyNode::Withitem(_) - | AnyNode::MatchCase(_) => None, + | AnyNode::MatchCase(_) + | AnyNode::Decorator(_) => None, } } @@ -496,7 +501,8 @@ impl AnyNode { | AnyNode::Keyword(_) | AnyNode::Alias(_) | AnyNode::Withitem(_) - | AnyNode::MatchCase(_) => None, + | AnyNode::MatchCase(_) + | AnyNode::Decorator(_) => None, } } @@ -577,7 +583,8 @@ impl AnyNode { | AnyNode::Keyword(_) | AnyNode::Alias(_) | AnyNode::Withitem(_) - | AnyNode::MatchCase(_) => None, + | AnyNode::MatchCase(_) + | AnyNode::Decorator(_) => None, } } @@ -682,6 +689,7 @@ impl AnyNode { Self::Alias(node) => AnyNodeRef::Alias(node), Self::Withitem(node) => AnyNodeRef::Withitem(node), Self::MatchCase(node) => AnyNodeRef::MatchCase(node), + Self::Decorator(node) => AnyNodeRef::Decorator(node), } } @@ -2793,6 +2801,35 @@ impl AstNode for MatchCase { } } +impl AstNode for Decorator { + fn cast(kind: AnyNode) -> Option + where + Self: Sized, + { + if let AnyNode::Decorator(node) = kind { + Some(node) + } else { + None + } + } + + fn cast_ref(kind: AnyNodeRef) -> Option<&Self> { + if let AnyNodeRef::Decorator(node) = kind { + Some(node) + } else { + None + } + } + + fn as_any_node_ref(&self) -> AnyNodeRef { + AnyNodeRef::from(self) + } + + fn into_any_node(self) -> AnyNode { + AnyNode::from(self) + } +} + impl From for AnyNode { fn from(stmt: Stmt) -> Self { match stmt { @@ -3346,6 +3383,11 @@ impl From for AnyNode { AnyNode::MatchCase(node) } } +impl From for AnyNode { + fn from(node: Decorator) -> Self { + AnyNode::Decorator(node) + } +} impl Ranged for AnyNode { fn range(&self) -> TextRange { @@ -3425,6 +3467,7 @@ impl Ranged for AnyNode { AnyNode::Alias(node) => node.range(), AnyNode::Withitem(node) => node.range(), AnyNode::MatchCase(node) => node.range(), + AnyNode::Decorator(node) => node.range(), } } } @@ -3506,6 +3549,7 @@ pub enum AnyNodeRef<'a> { Alias(&'a Alias), Withitem(&'a Withitem), MatchCase(&'a MatchCase), + Decorator(&'a Decorator), } impl AnyNodeRef<'_> { @@ -3586,6 +3630,7 @@ impl AnyNodeRef<'_> { AnyNodeRef::Alias(node) => NonNull::from(*node).cast(), AnyNodeRef::Withitem(node) => NonNull::from(*node).cast(), AnyNodeRef::MatchCase(node) => NonNull::from(*node).cast(), + AnyNodeRef::Decorator(node) => NonNull::from(*node).cast(), } } @@ -3672,6 +3717,7 @@ impl AnyNodeRef<'_> { AnyNodeRef::Alias(_) => NodeKind::Alias, AnyNodeRef::Withitem(_) => NodeKind::Withitem, AnyNodeRef::MatchCase(_) => NodeKind::MatchCase, + AnyNodeRef::Decorator(_) => NodeKind::Decorator, } } @@ -3752,7 +3798,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::Keyword(_) | AnyNodeRef::Alias(_) | AnyNodeRef::Withitem(_) - | AnyNodeRef::MatchCase(_) => false, + | AnyNodeRef::MatchCase(_) + | AnyNodeRef::Decorator(_) => false, } } @@ -3833,7 +3880,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::Keyword(_) | AnyNodeRef::Alias(_) | AnyNodeRef::Withitem(_) - | AnyNodeRef::MatchCase(_) => false, + | AnyNodeRef::MatchCase(_) + | AnyNodeRef::Decorator(_) => false, } } @@ -3914,7 +3962,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::Keyword(_) | AnyNodeRef::Alias(_) | AnyNodeRef::Withitem(_) - | AnyNodeRef::MatchCase(_) => false, + | AnyNodeRef::MatchCase(_) + | AnyNodeRef::Decorator(_) => false, } } @@ -3995,7 +4044,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::Keyword(_) | AnyNodeRef::Alias(_) | AnyNodeRef::Withitem(_) - | AnyNodeRef::MatchCase(_) => false, + | AnyNodeRef::MatchCase(_) + | AnyNodeRef::Decorator(_) => false, } } @@ -4076,7 +4126,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::Keyword(_) | AnyNodeRef::Alias(_) | AnyNodeRef::Withitem(_) - | AnyNodeRef::MatchCase(_) => false, + | AnyNodeRef::MatchCase(_) + | AnyNodeRef::Decorator(_) => false, } } @@ -4157,7 +4208,8 @@ impl AnyNodeRef<'_> { | AnyNodeRef::Keyword(_) | AnyNodeRef::Alias(_) | AnyNodeRef::Withitem(_) - | AnyNodeRef::MatchCase(_) => false, + | AnyNodeRef::MatchCase(_) + | AnyNodeRef::Decorator(_) => false, } } } @@ -4570,6 +4622,12 @@ impl<'a> From<&'a TypeIgnoreTypeIgnore> for AnyNodeRef<'a> { } } +impl<'a> From<&'a Decorator> for AnyNodeRef<'a> { + fn from(node: &'a Decorator) -> Self { + AnyNodeRef::Decorator(node) + } +} + impl<'a> From<&'a Stmt> for AnyNodeRef<'a> { fn from(stmt: &'a Stmt) -> Self { match stmt { @@ -4796,6 +4854,7 @@ impl Ranged for AnyNodeRef<'_> { AnyNodeRef::Alias(node) => node.range(), AnyNodeRef::Withitem(node) => node.range(), AnyNodeRef::MatchCase(node) => node.range(), + AnyNodeRef::Decorator(node) => node.range(), } } } @@ -4877,4 +4936,5 @@ pub enum NodeKind { Alias, Withitem, MatchCase, + Decorator, } diff --git a/crates/ruff_python_ast/src/source_code/generator.rs b/crates/ruff_python_ast/src/source_code/generator.rs index 7c3e1efde4..718364591f 100644 --- a/crates/ruff_python_ast/src/source_code/generator.rs +++ b/crates/ruff_python_ast/src/source_code/generator.rs @@ -212,7 +212,7 @@ impl<'a> Generator<'a> { for decorator in decorator_list { statement!({ self.p("@"); - self.unparse_expr(decorator, precedence::MAX); + self.unparse_expr(&decorator.expression, precedence::MAX); }); } statement!({ @@ -244,7 +244,7 @@ impl<'a> Generator<'a> { for decorator in decorator_list { statement!({ self.p("@"); - self.unparse_expr(decorator, precedence::MAX); + self.unparse_expr(&decorator.expression, precedence::MAX); }); } statement!({ @@ -276,7 +276,7 @@ impl<'a> Generator<'a> { for decorator in decorator_list { statement!({ self.p("@"); - self.unparse_expr(decorator, precedence::MAX); + self.unparse_expr(&decorator.expression, precedence::MAX); }); } statement!({ diff --git a/crates/ruff_python_ast/src/visitor.rs b/crates/ruff_python_ast/src/visitor.rs index 17c0366444..2864df5acb 100644 --- a/crates/ruff_python_ast/src/visitor.rs +++ b/crates/ruff_python_ast/src/visitor.rs @@ -2,6 +2,7 @@ pub mod preorder; +use rustpython_ast::Decorator; use rustpython_parser::ast::{ self, Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler, Expr, ExprContext, Keyword, MatchCase, Operator, Pattern, Stmt, Unaryop, Withitem, @@ -21,6 +22,9 @@ pub trait Visitor<'a> { fn visit_annotation(&mut self, expr: &'a Expr) { walk_expr(self, expr); } + fn visit_decorator(&mut self, decorator: &'a Decorator) { + walk_decorator(self, decorator); + } fn visit_expr(&mut self, expr: &'a Expr) { walk_expr(self, expr); } @@ -93,8 +97,8 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { .. }) => { visitor.visit_arguments(args); - for expr in decorator_list { - visitor.visit_expr(expr); + for decorator in decorator_list { + visitor.visit_decorator(decorator); } for expr in returns { visitor.visit_annotation(expr); @@ -109,8 +113,8 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { .. }) => { visitor.visit_arguments(args); - for expr in decorator_list { - visitor.visit_expr(expr); + for decorator in decorator_list { + visitor.visit_decorator(decorator); } for expr in returns { visitor.visit_annotation(expr); @@ -130,8 +134,8 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { for keyword in keywords { visitor.visit_keyword(keyword); } - for expr in decorator_list { - visitor.visit_expr(expr); + for decorator in decorator_list { + visitor.visit_decorator(decorator); } visitor.visit_body(body); } @@ -318,6 +322,10 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) { } } +pub fn walk_decorator<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, decorator: &'a Decorator) { + visitor.visit_expr(&decorator.expression); +} + pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) { match expr { Expr::BoolOp(ast::ExprBoolOp { diff --git a/crates/ruff_python_ast/src/visitor/preorder.rs b/crates/ruff_python_ast/src/visitor/preorder.rs index 3aeb4e3ad3..974469ce0a 100644 --- a/crates/ruff_python_ast/src/visitor/preorder.rs +++ b/crates/ruff_python_ast/src/visitor/preorder.rs @@ -18,6 +18,10 @@ pub trait PreorderVisitor<'a> { walk_expr(self, expr); } + fn visit_decorator(&mut self, decorator: &'a Decorator) { + walk_decorator(self, decorator); + } + fn visit_constant(&mut self, constant: &'a Constant) { walk_constant(self, constant); } @@ -151,8 +155,8 @@ where returns, .. }) => { - for expr in decorator_list { - visitor.visit_expr(expr); + for decorator in decorator_list { + visitor.visit_decorator(decorator); } visitor.visit_arguments(args); @@ -171,8 +175,8 @@ where decorator_list, .. }) => { - for expr in decorator_list { - visitor.visit_expr(expr); + for decorator in decorator_list { + visitor.visit_decorator(decorator); } for expr in bases { @@ -387,6 +391,13 @@ where } } +pub fn walk_decorator<'a, V>(visitor: &mut V, decorator: &'a Decorator) +where + V: PreorderVisitor<'a> + ?Sized, +{ + visitor.visit_expr(&decorator.expression); +} + pub fn walk_expr<'a, V>(visitor: &mut V, expr: &'a Expr) where V: PreorderVisitor<'a> + ?Sized, diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index c90d446b25..d3f433b61c 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -745,9 +745,13 @@ fn find_pos_only_slash_offset( ), locator.contents(), ) - .map(|(offset, c)| { - debug_assert_eq!(c, '/'); - offset + .and_then(|(offset, c)| { + if c == '/' { + Some(offset) + } else { + debug_assert_eq!(c, ')'); + None + } }) }, ) diff --git a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_slash_on_same_line.snap b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_slash_on_same_line.snap index be52d27533..ab41a810a3 100644 --- a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_slash_on_same_line.snap +++ b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_slash_on_same_line.snap @@ -5,8 +5,8 @@ expression: comments.debug(test_case.source_code) { Node { kind: Arguments, - range: 10..94, - source: `a=10,/, # trailing position...t comment.⏎`, + range: 9..96, + source: `(a=10,/, # trailing positio...t comment.⏎`, }: { "leading": [], "dangling": [ diff --git a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_with_defaults.snap b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_with_defaults.snap index 8205830fb2..a4fde5b30d 100644 --- a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_with_defaults.snap +++ b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__non_positional_arguments_with_defaults.snap @@ -5,8 +5,8 @@ expression: comments.debug(test_case.source_code) { Node { kind: Arguments, - range: 15..177, - source: `a=10 # trailing positional comment⏎`, + range: 9..179, + source: `(⏎`, }: { "leading": [], "dangling": [ diff --git a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment.snap b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment.snap index 1ad30c1b16..ff26fa7292 100644 --- a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment.snap +++ b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment.snap @@ -3,25 +3,10 @@ source: crates/ruff_python_formatter/src/comments/mod.rs expression: comments.debug(test_case.source_code) --- { - Node { - kind: Arg, - range: 15..16, - source: `a`, - }: { - "leading": [], - "dangling": [], - "trailing": [ - SourceComment { - text: "# trailing positional comment", - position: EndOfLine, - formatted: false, - }, - ], - }, Node { kind: Arguments, - range: 15..168, - source: `a, # trailing positional comment⏎`, + range: 9..170, + source: `(⏎`, }: { "leading": [], "dangling": [ @@ -38,6 +23,21 @@ expression: comments.debug(test_case.source_code) ], "trailing": [], }, + Node { + kind: Arg, + range: 15..16, + source: `a`, + }: { + "leading": [], + "dangling": [], + "trailing": [ + SourceComment { + text: "# trailing positional comment", + position: EndOfLine, + formatted: false, + }, + ], + }, Node { kind: Arg, range: 166..167, diff --git a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment_without_following_node.snap b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment_without_following_node.snap index 2c0b4a41c6..2996b7bee1 100644 --- a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment_without_following_node.snap +++ b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_comment_without_following_node.snap @@ -3,6 +3,26 @@ source: crates/ruff_python_formatter/src/comments/mod.rs expression: comments.debug(test_case.source_code) --- { + Node { + kind: Arguments, + range: 9..166, + source: `(⏎`, + }: { + "leading": [], + "dangling": [ + SourceComment { + text: "# Positional arguments only after here", + position: OwnLine, + formatted: false, + }, + SourceComment { + text: "# trailing positional argument comment.", + position: EndOfLine, + formatted: false, + }, + ], + "trailing": [], + }, Node { kind: Arg, range: 15..16, @@ -16,42 +36,11 @@ expression: comments.debug(test_case.source_code) position: EndOfLine, formatted: false, }, - ], - }, - Node { - kind: Arguments, - range: 15..97, - source: `a, # trailing positional comment⏎`, - }: { - "leading": [], - "dangling": [ - SourceComment { - text: "# Positional arguments only after here", - position: OwnLine, - formatted: false, - }, - ], - "trailing": [ - SourceComment { - text: "# trailing positional argument comment.", - position: EndOfLine, - formatted: false, - }, - ], - }, - Node { - kind: StmtPass, - range: 168..172, - source: `pass`, - }: { - "leading": [ SourceComment { text: "# Trailing on new line", position: OwnLine, formatted: false, }, ], - "dangling": [], - "trailing": [], }, } diff --git a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_leading_comma_comment.snap b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_leading_comma_comment.snap index 0fcc527918..ff26fa7292 100644 --- a/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_leading_comma_comment.snap +++ b/crates/ruff_python_formatter/src/comments/snapshots/ruff_python_formatter__comments__tests__positional_argument_only_leading_comma_comment.snap @@ -3,25 +3,10 @@ source: crates/ruff_python_formatter/src/comments/mod.rs expression: comments.debug(test_case.source_code) --- { - Node { - kind: Arg, - range: 15..16, - source: `a`, - }: { - "leading": [], - "dangling": [], - "trailing": [ - SourceComment { - text: "# trailing positional comment", - position: EndOfLine, - formatted: false, - }, - ], - }, Node { kind: Arguments, - range: 15..168, - source: `a # trailing positional comment⏎`, + range: 9..170, + source: `(⏎`, }: { "leading": [], "dangling": [ @@ -38,6 +23,21 @@ expression: comments.debug(test_case.source_code) ], "trailing": [], }, + Node { + kind: Arg, + range: 15..16, + source: `a`, + }: { + "leading": [], + "dangling": [], + "trailing": [ + SourceComment { + text: "# trailing positional comment", + position: EndOfLine, + formatted: false, + }, + ], + }, Node { kind: Arg, range: 166..167, diff --git a/crates/ruff_python_semantic/src/analyze/function_type.rs b/crates/ruff_python_semantic/src/analyze/function_type.rs index 8d90e1edd7..89f68a78ff 100644 --- a/crates/ruff_python_semantic/src/analyze/function_type.rs +++ b/crates/ruff_python_semantic/src/analyze/function_type.rs @@ -1,4 +1,4 @@ -use rustpython_parser::ast::Expr; +use rustpython_parser::ast::Decorator; use ruff_python_ast::call_path::from_qualified_name; use ruff_python_ast::helpers::map_callable; @@ -22,18 +22,18 @@ pub fn classify( model: &SemanticModel, scope: &Scope, name: &str, - decorator_list: &[Expr], + decorator_list: &[Decorator], classmethod_decorators: &[String], staticmethod_decorators: &[String], ) -> FunctionType { let ScopeKind::Class(scope) = &scope.kind else { return FunctionType::Function; }; - if decorator_list.iter().any(|expr| { + if decorator_list.iter().any(|decorator| { // The method is decorated with a static method decorator (like // `@staticmethod`). model - .resolve_call_path(map_callable(expr)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["", "staticmethod"] || staticmethod_decorators @@ -52,9 +52,9 @@ pub fn classify( .any(|(module, member)| call_path.as_slice() == [*module, *member]) }) }) - || decorator_list.iter().any(|expr| { + || decorator_list.iter().any(|decorator| { // The method is decorated with a class method decorator (like `@classmethod`). - model.resolve_call_path(map_callable(expr)).map_or(false, |call_path| { + model.resolve_call_path(map_callable(&decorator.expression)).map_or(false, |call_path| { call_path.as_slice() == ["", "classmethod"] || classmethod_decorators .iter() diff --git a/crates/ruff_python_semantic/src/analyze/visibility.rs b/crates/ruff_python_semantic/src/analyze/visibility.rs index dd921a8d26..a94ac4ca5c 100644 --- a/crates/ruff_python_semantic/src/analyze/visibility.rs +++ b/crates/ruff_python_semantic/src/analyze/visibility.rs @@ -1,6 +1,6 @@ use std::path::Path; -use rustpython_parser::ast::{self, Expr, Stmt}; +use rustpython_parser::ast::{self, Decorator, Stmt}; use ruff_python_ast::call_path::{collect_call_path, CallPath}; use ruff_python_ast::helpers::map_callable; @@ -14,10 +14,10 @@ pub enum Visibility { } /// Returns `true` if a function is a "static method". -pub fn is_staticmethod(model: &SemanticModel, decorator_list: &[Expr]) -> bool { - decorator_list.iter().any(|expr| { +pub fn is_staticmethod(model: &SemanticModel, decorator_list: &[Decorator]) -> bool { + decorator_list.iter().any(|decorator| { model - .resolve_call_path(map_callable(expr)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["", "staticmethod"] }) @@ -25,10 +25,10 @@ pub fn is_staticmethod(model: &SemanticModel, decorator_list: &[Expr]) -> bool { } /// Returns `true` if a function is a "class method". -pub fn is_classmethod(model: &SemanticModel, decorator_list: &[Expr]) -> bool { - decorator_list.iter().any(|expr| { +pub fn is_classmethod(model: &SemanticModel, decorator_list: &[Decorator]) -> bool { + decorator_list.iter().any(|decorator| { model - .resolve_call_path(map_callable(expr)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["", "classmethod"] }) @@ -36,24 +36,24 @@ pub fn is_classmethod(model: &SemanticModel, decorator_list: &[Expr]) -> bool { } /// Returns `true` if a function definition is an `@overload`. -pub fn is_overload(model: &SemanticModel, decorator_list: &[Expr]) -> bool { +pub fn is_overload(model: &SemanticModel, decorator_list: &[Decorator]) -> bool { decorator_list .iter() - .any(|expr| model.match_typing_expr(map_callable(expr), "overload")) + .any(|decorator| model.match_typing_expr(map_callable(&decorator.expression), "overload")) } /// Returns `true` if a function definition is an `@override` (PEP 698). -pub fn is_override(model: &SemanticModel, decorator_list: &[Expr]) -> bool { +pub fn is_override(model: &SemanticModel, decorator_list: &[Decorator]) -> bool { decorator_list .iter() - .any(|expr| model.match_typing_expr(map_callable(expr), "override")) + .any(|decorator| model.match_typing_expr(map_callable(&decorator.expression), "override")) } /// Returns `true` if a function definition is an abstract method based on its decorators. -pub fn is_abstract(model: &SemanticModel, decorator_list: &[Expr]) -> bool { - decorator_list.iter().any(|expr| { +pub fn is_abstract(model: &SemanticModel, decorator_list: &[Decorator]) -> bool { + decorator_list.iter().any(|decorator| { model - .resolve_call_path(map_callable(expr)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { matches!( call_path.as_slice(), @@ -74,12 +74,12 @@ pub fn is_abstract(model: &SemanticModel, decorator_list: &[Expr]) -> bool { /// `@property`-like decorators. pub fn is_property( model: &SemanticModel, - decorator_list: &[Expr], + decorator_list: &[Decorator], extra_properties: &[CallPath], ) -> bool { - decorator_list.iter().any(|expr| { + decorator_list.iter().any(|decorator| { model - .resolve_call_path(map_callable(expr)) + .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { call_path.as_slice() == ["", "property"] || call_path.as_slice() == ["functools", "cached_property"] @@ -91,10 +91,10 @@ pub fn is_property( } /// Returns `true` if a class is an `final`. -pub fn is_final(model: &SemanticModel, decorator_list: &[Expr]) -> bool { +pub fn is_final(model: &SemanticModel, decorator_list: &[Decorator]) -> bool { decorator_list .iter() - .any(|expr| model.match_typing_expr(map_callable(expr), "final")) + .any(|decorator| model.match_typing_expr(map_callable(&decorator.expression), "final")) } /// Returns `true` if a function is a "magic method". @@ -206,8 +206,8 @@ pub(crate) fn method_visibility(stmt: &Stmt) -> Visibility { .. }) => { // Is this a setter or deleter? - if decorator_list.iter().any(|expr| { - collect_call_path(expr).map_or(false, |call_path| { + if decorator_list.iter().any(|decorator| { + collect_call_path(&decorator.expression).map_or(false, |call_path| { call_path.as_slice() == [name, "setter"] || call_path.as_slice() == [name, "deleter"] })