RUF009 should behave similar to B008 and ignore attributes with immutable types (#16048)

This PR resolved #15772

Before PR:
```
def _(
    this_is_fine: int = f(),           # No error
    this_is_not: list[int] = f()       # B008: Do not perform function call `f` in argument defaults
): ...


@dataclass
class _:
    this_is_not_fine: list[int] = f()  # RUF009: Do not perform function call `f` in dataclass defaults
    this_is_also_not: int = f()        # RUF009: Do not perform function call `f` in dataclass defaults
```

After PR:
```
def _(
    this_is_fine: int = f(),           # No error
    this_is_not: list[int] = f()       # B008: Do not perform function call `f` in argument defaults
): ...


@dataclass
class _:
    this_is_not_fine: list[int] = f()  # RUF009: Do not perform function call `f` in dataclass defaults
    this_is_fine: int = f()
```
This commit is contained in:
ABDULRAHMAN ALRAHMA 2025-02-10 08:46:23 +00:00 committed by GitHub
parent 07cf8852a3
commit d2f661f795
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 29 additions and 107 deletions

View file

@ -97,3 +97,16 @@ class DataclassWithNewTypeFields:
# No errors # No errors
e: SpecialString = SpecialString("Lorem ipsum") e: SpecialString = SpecialString("Lorem ipsum")
f: NegativeInteger = NegativeInteger(-110) f: NegativeInteger = NegativeInteger(-110)
# Test for:
# https://github.com/astral-sh/ruff/issues/15772
def f() -> int:
return 0
@dataclass
class ShouldMatchB008RuleOfImmutableTypeAnnotationIgnored:
this_is_not_fine: list[int] = default_function()
# ignored
this_is_fine: int = f()

View file

@ -3,7 +3,9 @@ use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::name::{QualifiedName, UnqualifiedName}; use ruff_python_ast::name::{QualifiedName, UnqualifiedName};
use ruff_python_semantic::analyze::typing::{is_immutable_func, is_immutable_newtype_call}; use ruff_python_semantic::analyze::typing::{
is_immutable_annotation, is_immutable_func, is_immutable_newtype_call,
};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
@ -134,6 +136,7 @@ pub(crate) fn function_call_in_dataclass_default(checker: &Checker, class_def: &
} }
if is_field if is_field
|| is_immutable_annotation(annotation, checker.semantic(), &extend_immutable_calls)
|| is_class_var_annotation(annotation, checker.semantic()) || is_class_var_annotation(annotation, checker.semantic())
|| is_immutable_func(func, checker.semantic(), &extend_immutable_calls) || is_immutable_func(func, checker.semantic(), &extend_immutable_calls)
|| is_descriptor_class(func, checker.semantic()) || is_descriptor_class(func, checker.semantic())

View file

@ -1,5 +1,6 @@
--- ---
source: crates/ruff_linter/src/rules/ruff/mod.rs source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
--- ---
RUF009.py:20:41: RUF009 Do not perform function call `default_function` in dataclass defaults RUF009.py:20:41: RUF009 Do not perform function call `default_function` in dataclass defaults
| |
@ -89,3 +90,13 @@ RUF009.py:95:19: RUF009 Do not perform function call `Invalid3` in dataclass def
96 | 96 |
97 | # No errors 97 | # No errors
| |
RUF009.py:109:35: RUF009 Do not perform function call `default_function` in dataclass defaults
|
107 | @dataclass
108 | class ShouldMatchB008RuleOfImmutableTypeAnnotationIgnored:
109 | this_is_not_fine: list[int] = default_function()
| ^^^^^^^^^^^^^^^^^^ RUF009
110 | # ignored
111 | this_is_fine: int = f()
|

View file

@ -1,110 +1,5 @@
--- ---
source: crates/ruff_linter/src/rules/ruff/mod.rs source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
--- ---
RUF009_attrs_auto_attribs.py:12:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
10 | a: str = 0
11 | b = field()
12 | c: int = foo()
| ^^^^^ RUF009
13 | d = list()
|
RUF009_attrs_auto_attribs.py:20:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
18 | a: str = 0
19 | b = field()
20 | c: int = foo()
| ^^^^^ RUF009
21 | d = list()
|
RUF009_attrs_auto_attribs.py:28:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
26 | a: str = 0
27 | b = field()
28 | c: int = foo()
| ^^^^^ RUF009
29 | d = list()
|
RUF009_attrs_auto_attribs.py:36:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
34 | a: str = 0
35 | b = field()
36 | c: int = foo()
| ^^^^^ RUF009
37 | d = list()
|
RUF009_attrs_auto_attribs.py:44:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
42 | a: str = 0
43 | b = field()
44 | c: int = foo()
| ^^^^^ RUF009
45 | d = list()
|
RUF009_attrs_auto_attribs.py:52:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
50 | a: str = 0
51 | b = field()
52 | c: int = foo()
| ^^^^^ RUF009
53 | d = list()
|
RUF009_attrs_auto_attribs.py:60:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
58 | a: str = 0
59 | b = field()
60 | c: int = foo()
| ^^^^^ RUF009
61 | d = list()
|
RUF009_attrs_auto_attribs.py:68:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
66 | a: str = 0
67 | b = field()
68 | c: int = foo()
| ^^^^^ RUF009
69 | d = list()
|
RUF009_attrs_auto_attribs.py:76:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
74 | a: str = 0
75 | b = field()
76 | c: int = foo()
| ^^^^^ RUF009
77 | d = list()
|
RUF009_attrs_auto_attribs.py:92:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
90 | a: str = 0
91 | b = field()
92 | c: int = foo()
| ^^^^^ RUF009
93 | d = list()
|
RUF009_attrs_auto_attribs.py:100:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
98 | a: str = 0
99 | b = field()
100 | c: int = foo()
| ^^^^^ RUF009
101 | d = list()
|
RUF009_attrs_auto_attribs.py:116:14: RUF009 Do not perform function call `foo` in dataclass defaults
|
114 | a: str = 0
115 | b = field()
116 | c: int = foo()
| ^^^^^ RUF009
117 | d = list()
|