From 225dd0a02764366b34eebf69272a49593b612a06 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 8 Jan 2025 12:57:21 +0100 Subject: [PATCH] [`ruff`] Stabilize: Detect `attrs` dataclasses (`RUF008`, `RUF009`) (#15345) --- crates/ruff_linter/src/rules/ruff/mod.rs | 38 +++++++++---------- .../function_call_in_dataclass_default.rs | 4 -- .../src/rules/ruff/rules/helpers.rs | 10 ----- .../rules/ruff/rules/mutable_class_default.rs | 7 +--- .../ruff/rules/mutable_dataclass_default.rs | 6 +-- ..._ruff__tests__RUF008_RUF008_attrs.py.snap} | 0 ..._ruff__tests__RUF009_RUF009_attrs.py.snap} | 0 ..._RUF009_RUF009_attrs_auto_attribs.py.snap} | 0 8 files changed, 22 insertions(+), 43 deletions(-) rename crates/ruff_linter/src/rules/ruff/snapshots/{ruff_linter__rules__ruff__tests__preview__RUF008_RUF008_attrs.py.snap => ruff_linter__rules__ruff__tests__RUF008_RUF008_attrs.py.snap} (100%) rename crates/ruff_linter/src/rules/ruff/snapshots/{ruff_linter__rules__ruff__tests__preview__RUF009_RUF009_attrs.py.snap => ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs.py.snap} (100%) rename crates/ruff_linter/src/rules/ruff/snapshots/{ruff_linter__rules__ruff__tests__preview__RUF009_RUF009_attrs_auto_attribs.py.snap => ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs_auto_attribs.py.snap} (100%) diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index 73d5fe0434777..20d7602347e3a 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -25,18 +25,27 @@ mod tests { use crate::test::{test_path, test_resource_path}; use crate::{assert_messages, settings}; - #[test_case(Rule::AsyncioDanglingTask, Path::new("RUF006.py"))] #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005.py"))] - #[test_case(Rule::ExplicitFStringTypeConversion, Path::new("RUF010.py"))] + #[test_case(Rule::AsyncioDanglingTask, Path::new("RUF006.py"))] + #[test_case(Rule::ZipInsteadOfPairwise, Path::new("RUF007.py"))] + #[test_case(Rule::MutableDataclassDefault, Path::new("RUF008.py"))] + #[test_case(Rule::MutableDataclassDefault, Path::new("RUF008_attrs.py"))] #[test_case(Rule::FunctionCallInDataclassDefaultArgument, Path::new("RUF009.py"))] + #[test_case( + Rule::FunctionCallInDataclassDefaultArgument, + Path::new("RUF009_attrs.py") + )] + #[test_case( + Rule::FunctionCallInDataclassDefaultArgument, + Path::new("RUF009_attrs_auto_attribs.py") + )] + #[test_case(Rule::ExplicitFStringTypeConversion, Path::new("RUF010.py"))] + #[test_case(Rule::MutableClassDefault, Path::new("RUF012.py"))] #[test_case(Rule::ImplicitOptional, Path::new("RUF013_0.py"))] #[test_case(Rule::ImplicitOptional, Path::new("RUF013_1.py"))] #[test_case(Rule::ImplicitOptional, Path::new("RUF013_2.py"))] #[test_case(Rule::ImplicitOptional, Path::new("RUF013_3.py"))] #[test_case(Rule::ImplicitOptional, Path::new("RUF013_4.py"))] - #[test_case(Rule::MutableClassDefault, Path::new("RUF012.py"))] - #[test_case(Rule::MutableDataclassDefault, Path::new("RUF008.py"))] - #[test_case(Rule::ZipInsteadOfPairwise, Path::new("RUF007.py"))] #[test_case( Rule::UnnecessaryIterableAllocationForFirstElement, Path::new("RUF015.py") @@ -51,7 +60,6 @@ mod tests { #[test_case(Rule::UnsortedDunderAll, Path::new("RUF022.py"))] #[test_case(Rule::UnsortedDunderSlots, Path::new("RUF023.py"))] #[test_case(Rule::MutableFromkeysValue, Path::new("RUF024.py"))] - #[test_case(Rule::UnnecessaryEmptyIterableWithinDequeCall, Path::new("RUF037.py"))] #[test_case(Rule::DefaultFactoryKwarg, Path::new("RUF026.py"))] #[test_case(Rule::MissingFStringSyntax, Path::new("RUF027_0.py"))] #[test_case(Rule::MissingFStringSyntax, Path::new("RUF027_1.py"))] @@ -61,20 +69,21 @@ mod tests { #[test_case(Rule::AssertWithPrintMessage, Path::new("RUF030.py"))] #[test_case(Rule::IncorrectlyParenthesizedTupleInSubscript, Path::new("RUF031.py"))] #[test_case(Rule::DecimalFromFloatLiteral, Path::new("RUF032.py"))] - #[test_case(Rule::UselessIfElse, Path::new("RUF034.py"))] - #[test_case(Rule::RedirectedNOQA, Path::new("RUF101_0.py"))] - #[test_case(Rule::RedirectedNOQA, Path::new("RUF101_1.py"))] #[test_case(Rule::PostInitDefault, Path::new("RUF033.py"))] + #[test_case(Rule::UselessIfElse, Path::new("RUF034.py"))] #[test_case(Rule::NoneNotAtEndOfUnion, Path::new("RUF036.py"))] #[test_case(Rule::NoneNotAtEndOfUnion, Path::new("RUF036.pyi"))] + #[test_case(Rule::UnnecessaryEmptyIterableWithinDequeCall, Path::new("RUF037.py"))] #[test_case(Rule::RedundantBoolLiteral, Path::new("RUF038.py"))] #[test_case(Rule::RedundantBoolLiteral, Path::new("RUF038.pyi"))] #[test_case(Rule::InvalidAssertMessageLiteralArgument, Path::new("RUF040.py"))] #[test_case(Rule::UnnecessaryNestedLiteral, Path::new("RUF041.py"))] #[test_case(Rule::UnnecessaryNestedLiteral, Path::new("RUF041.pyi"))] #[test_case(Rule::IfKeyInDictDel, Path::new("RUF051.py"))] - #[test_case(Rule::FalsyDictGetFallback, Path::new("RUF056.py"))] #[test_case(Rule::UsedDummyVariable, Path::new("RUF052.py"))] + #[test_case(Rule::FalsyDictGetFallback, Path::new("RUF056.py"))] + #[test_case(Rule::RedirectedNOQA, Path::new("RUF101_0.py"))] + #[test_case(Rule::RedirectedNOQA, Path::new("RUF101_1.py"))] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( @@ -403,15 +412,6 @@ mod tests { } #[test_case(Rule::UnsafeMarkupUse, Path::new("RUF035.py"))] - #[test_case( - Rule::FunctionCallInDataclassDefaultArgument, - Path::new("RUF009_attrs.py") - )] - #[test_case( - Rule::FunctionCallInDataclassDefaultArgument, - Path::new("RUF009_attrs_auto_attribs.py") - )] - #[test_case(Rule::MutableDataclassDefault, Path::new("RUF008_attrs.py"))] #[test_case(Rule::MapIntVersionParsing, Path::new("RUF048.py"))] #[test_case(Rule::MapIntVersionParsing, Path::new("RUF048_1.py"))] #[test_case(Rule::UnrawRePattern, Path::new("RUF039.py"))] 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 b3dd673f488ae..b9d72e3f802b4 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 @@ -81,10 +81,6 @@ pub(crate) fn function_call_in_dataclass_default( return; }; - if dataclass_kind.is_attrs() && checker.settings.preview.is_disabled() { - return; - } - let attrs_auto_attribs = match dataclass_kind { DataclassKind::Stdlib => None, diff --git a/crates/ruff_linter/src/rules/ruff/rules/helpers.rs b/crates/ruff_linter/src/rules/ruff/rules/helpers.rs index e02f500c6ed5f..19894bfa44c79 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/helpers.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/helpers.rs @@ -101,16 +101,6 @@ pub(super) enum DataclassKind { Attrs(AttrsAutoAttribs), } -impl DataclassKind { - pub(super) const fn is_stdlib(self) -> bool { - matches!(self, DataclassKind::Stdlib) - } - - pub(super) const fn is_attrs(self) -> bool { - matches!(self, DataclassKind::Attrs(..)) - } -} - /// Return the kind of dataclass this class definition is (stdlib or `attrs`), /// or `None` if the class is not a dataclass. pub(super) fn dataclass_kind<'a>( diff --git a/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs b/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs index 7b8fad19a1a01..88016c95d6c76 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mutable_class_default.rs @@ -66,11 +66,8 @@ pub(crate) fn mutable_class_default(checker: &mut Checker, class_def: &ast::Stmt && !is_final_annotation(annotation, checker.semantic()) && !is_immutable_annotation(annotation, checker.semantic(), &[]) { - if let Some((dataclass_kind, _)) = dataclass_kind(class_def, checker.semantic()) - { - if dataclass_kind.is_stdlib() || checker.settings.preview.is_enabled() { - continue; - } + if dataclass_kind(class_def, checker.semantic()).is_some() { + continue; } // Avoid, e.g., Pydantic and msgspec models, which end up copying defaults on instance creation. diff --git a/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs b/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs index c06ff28d39932..a587e9cc2b046 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mutable_dataclass_default.rs @@ -68,14 +68,10 @@ impl Violation for MutableDataclassDefault { pub(crate) fn mutable_dataclass_default(checker: &mut Checker, class_def: &ast::StmtClassDef) { let semantic = checker.semantic(); - let Some((dataclass_kind, _)) = dataclass_kind(class_def, semantic) else { + if dataclass_kind(class_def, semantic).is_none() { return; }; - if dataclass_kind.is_attrs() && checker.settings.preview.is_disabled() { - return; - } - for statement in &class_def.body { let Stmt::AnnAssign(ast::StmtAnnAssign { annotation, diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF008_RUF008_attrs.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF008_RUF008_attrs.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF008_RUF008_attrs.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF008_RUF008_attrs.py.snap diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF009_RUF009_attrs.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF009_RUF009_attrs.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs.py.snap diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__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 similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF009_RUF009_attrs_auto_attribs.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF009_RUF009_attrs_auto_attribs.py.snap