Skip to content

Commit

Permalink
RUF009 should behave similar to B008 and ignore attributes with immut…
Browse files Browse the repository at this point in the history
…able 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()
```
  • Loading branch information
smokyabdulrahman authored Feb 10, 2025
1 parent 07cf885 commit d2f661f
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 107 deletions.
13 changes: 13 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF009.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,16 @@ class DataclassWithNewTypeFields:
# No errors
e: SpecialString = SpecialString("Lorem ipsum")
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()

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata};
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 crate::checkers::ast::Checker;
Expand Down Expand Up @@ -134,6 +136,7 @@ pub(crate) fn function_call_in_dataclass_default(checker: &Checker, class_def: &
}

if is_field
|| is_immutable_annotation(annotation, checker.semantic(), &extend_immutable_calls)
|| is_class_var_annotation(annotation, checker.semantic())
|| is_immutable_func(func, checker.semantic(), &extend_immutable_calls)
|| is_descriptor_class(func, checker.semantic())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
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
|
Expand Down Expand Up @@ -89,3 +90,13 @@ RUF009.py:95:19: RUF009 Do not perform function call `Invalid3` in dataclass def
96 |
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()
|
Original file line number Diff line number Diff line change
@@ -1,110 +1,5 @@
---
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()
|

0 comments on commit d2f661f

Please sign in to comment.