From d2f661f7953b298f5ccd86d73f3d271e7bbc0392 Mon Sep 17 00:00:00 2001 From: ABDULRAHMAN ALRAHMA Date: Mon, 10 Feb 2025 08:46:23 +0000 Subject: [PATCH] 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() ``` --- .../resources/test/fixtures/ruff/RUF009.py | 13 +++ .../function_call_in_dataclass_default.rs | 5 +- ..._rules__ruff__tests__RUF009_RUF009.py.snap | 11 ++ ...__RUF009_RUF009_attrs_auto_attribs.py.snap | 107 +----------------- 4 files changed, 29 insertions(+), 107 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF009.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF009.py index 09984cfb2a..a98835577c 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF009.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF009.py @@ -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() + diff --git a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs index b5db8114f8..7a514d61fb 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/function_call_in_dataclass_default.rs @@ -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; @@ -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()) diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009.py.snap index 1ed33b5fa1..8a297d3c15 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009.py.snap @@ -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 | @@ -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() + | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs_auto_attribs.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs_auto_attribs.py.snap index 0e271a25ce..b3a37c0921 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs_auto_attribs.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs_auto_attribs.py.snap @@ -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() - |