Upgrade RustPython (#4900)

This commit is contained in:
Micha Reiser 2023-06-08 07:53:14 +02:00 committed by GitHub
parent 4b78141f6b
commit 39a1f3980f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 416 additions and 320 deletions

12
Cargo.lock generated
View file

@ -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",

View file

@ -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"] }

View file

@ -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(

View file

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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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 | ...
|

View file

@ -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<Diagnostic>
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"]

View file

@ -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
|

View file

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

View file

@ -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);

View file

@ -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<Item = (&Expr, CallPath)> {
pub(super) fn get_mark_decorators(
decorators: &[Decorator],
) -> impl Iterator<Item = (&Decorator, CallPath)> {
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"]
})

View file

@ -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);
}
}
}

View file

@ -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<String> {
/// ```
///
/// 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);

View file

@ -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
|

View file

@ -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

View file

@ -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
|

View file

@ -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
|

View file

@ -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
|

View file

@ -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
|

View file

@ -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
|

View file

@ -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
|

View file

@ -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
|

View file

@ -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);

View file

@ -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)

View file

@ -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<Diagnostic> {
if !matches!(

View file

@ -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<Diagnostic> {
if !matches!(

View file

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

View file

@ -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)

View file

@ -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;
}

View file

@ -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,
) {

View file

@ -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]))
});

View file

@ -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);

View file

@ -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"]
})

View file

@ -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, .. }) => {

View file

@ -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<ComparableStmt<'a>>,
decorator_list: Vec<ComparableExpr<'a>>,
decorator_list: Vec<ComparableDecorator<'a>>,
returns: Option<ComparableExpr<'a>>,
type_comment: Option<&'a str>,
},
@ -785,7 +799,7 @@ pub enum ComparableStmt<'a> {
name: &'a str,
args: ComparableArguments<'a>,
body: Vec<ComparableStmt<'a>>,
decorator_list: Vec<ComparableExpr<'a>>,
decorator_list: Vec<ComparableDecorator<'a>>,
returns: Option<ComparableExpr<'a>>,
type_comment: Option<&'a str>,
},
@ -794,7 +808,7 @@ pub enum ComparableStmt<'a> {
bases: Vec<ComparableExpr<'a>>,
keywords: Vec<ComparableKeyword<'a>>,
body: Vec<ComparableStmt<'a>>,
decorator_list: Vec<ComparableExpr<'a>>,
decorator_list: Vec<ComparableDecorator<'a>>,
},
Return {
value: Option<ComparableExpr<'a>>,

View file

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

View file

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

View file

@ -92,6 +92,7 @@ pub enum AnyNode {
Alias(Alias<TextRange>),
Withitem(Withitem<TextRange>),
MatchCase(MatchCase<TextRange>),
Decorator(Decorator<TextRange>),
}
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<TextRange> {
}
}
impl AstNode for Decorator<TextRange> {
fn cast(kind: AnyNode) -> Option<Self>
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<Stmt> for AnyNode {
fn from(stmt: Stmt) -> Self {
match stmt {
@ -3346,6 +3383,11 @@ impl From<MatchCase> for AnyNode {
AnyNode::MatchCase(node)
}
}
impl From<Decorator> 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<TextRange>),
Withitem(&'a Withitem<TextRange>),
MatchCase(&'a MatchCase<TextRange>),
Decorator(&'a Decorator<TextRange>),
}
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,
}

View file

@ -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!({

View file

@ -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 {

View file

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

View file

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

View file

@ -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": [

View file

@ -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": [

View file

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

View file

@ -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": [],
},
}

View file

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

View file

@ -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()

View file

@ -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"]
})