Skip to content

Commit

Permalink
[ruff] Stabilize: Detect attrs dataclasses (RUF008, RUF009) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Jan 9, 2025
1 parent 52aeb8a commit 225dd0a
Show file tree
Hide file tree
Showing 8 changed files with 22 additions and 43 deletions.
38 changes: 19 additions & 19 deletions crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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"))]
Expand All @@ -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(
Expand Down Expand Up @@ -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"))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
10 changes: 0 additions & 10 deletions crates/ruff_linter/src/rules/ruff/rules/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 225dd0a

Please sign in to comment.