diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF009_attrs_auto_attribs.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF009_attrs_auto_attribs.py index 6366304fdf..ba7517d4fb 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF009_attrs_auto_attribs.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF009_attrs_auto_attribs.py @@ -99,3 +99,27 @@ class C: b = field() c: int = foo() d = list() + + +@attr.s(auto_attribs=False) # auto_attribs = False +class C: + a: str = 0 + b = field() + c: int = foo() + d = list() + + +@attr.s(auto_attribs=True) # auto_attribs = True +class C: + a: str = 0 + b = field() + c: int = foo() + d = list() + + +@attr.s(auto_attribs=[1, 2, 3]) # auto_attribs = False +class C: + a: str = 0 + b = field() + c: int = foo() + d = list() diff --git a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs index 76533d75ac..584ae5f801 100644 --- a/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs +++ b/crates/ruff_linter/src/rules/flake8_bandit/rules/shell_injection.rs @@ -311,7 +311,8 @@ pub(crate) fn shell_injection(checker: &mut Checker, call: &ast::ExprCall) { } // S603 Some(ShellKeyword { - truthiness: Truthiness::False | Truthiness::Falsey | Truthiness::Unknown, + truthiness: + Truthiness::False | Truthiness::Falsey | Truthiness::None | Truthiness::Unknown, }) => { if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) { checker.diagnostics.push(Diagnostic::new( diff --git a/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs b/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs index faeee91be0..91aba60224 100644 --- a/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs +++ b/crates/ruff_linter/src/rules/flake8_logging/rules/exception_without_exc_info.rs @@ -87,6 +87,6 @@ fn exc_info_arg_is_falsey(call: &ExprCall, checker: &mut Checker) -> bool { .is_some_and(|value| { let truthiness = Truthiness::from_expr(value, |id| checker.semantic().has_builtin_binding(id)); - matches!(truthiness, Truthiness::False | Truthiness::Falsey) + truthiness.into_bool() == Some(false) }) } diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs index 324da7a813..fd79b92e9a 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/assertion.rs @@ -493,7 +493,7 @@ fn to_pytest_raises_args<'a>( /// PT015 pub(crate) fn assert_falsy(checker: &mut Checker, stmt: &Stmt, test: &Expr) { let truthiness = Truthiness::from_expr(test, |id| checker.semantic().has_builtin_binding(id)); - if matches!(truthiness, Truthiness::False | Truthiness::Falsey) { + if truthiness.into_bool() == Some(false) { checker .diagnostics .push(Diagnostic::new(PytestAssertAlwaysFalse, stmt.range())); diff --git a/crates/ruff_linter/src/rules/ruff/rules/helpers.rs b/crates/ruff_linter/src/rules/ruff/rules/helpers.rs index d6a743b677..00abd6ebec 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/helpers.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/helpers.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::helpers::{map_callable, map_subscript}; +use ruff_python_ast::helpers::{map_callable, map_subscript, Truthiness}; use ruff_python_ast::{self as ast, Expr, ExprCall}; use ruff_python_semantic::{analyze, BindingKind, Modules, SemanticModel}; @@ -148,16 +148,19 @@ pub(super) fn dataclass_kind( return Some(DataclassKind::Attrs(AttrsAutoAttribs::None)); }; - let auto_attribs = match &auto_attribs.value { - Expr::BooleanLiteral(literal) => { - if literal.value { - AttrsAutoAttribs::True - } else { - AttrsAutoAttribs::False - } + let auto_attribs = match Truthiness::from_expr(&auto_attribs.value, |id| { + semantic.has_builtin_binding(id) + }) { + // `auto_attribs` requires an exact `True` to be true + Truthiness::True => AttrsAutoAttribs::True, + // Or an exact `None` to auto-detect. + Truthiness::None => AttrsAutoAttribs::None, + // Otherwise, anything else (even a truthy value, like `1`) is considered `False`. + Truthiness::Truthy | Truthiness::False | Truthiness::Falsey => { + AttrsAutoAttribs::False } - Expr::NoneLiteral(..) => AttrsAutoAttribs::None, - _ => AttrsAutoAttribs::Unknown, + // Unless, of course, we can't determine the value. + Truthiness::Unknown => AttrsAutoAttribs::Unknown, }; return Some(DataclassKind::Attrs(auto_attribs)); 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__preview__RUF009_RUF009_attrs_auto_attribs.py.snap index 6324b9b5e0..0e271a25ce 100644 --- 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__preview__RUF009_RUF009_attrs_auto_attribs.py.snap @@ -1,6 +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 | @@ -100,3 +99,12 @@ RUF009_attrs_auto_attribs.py:100:14: RUF009 Do not perform function call `foo` i | ^^^^^ 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() + | diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 13c0b89d57..456bc34ead 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -1118,6 +1118,8 @@ pub enum Truthiness { Falsey, /// The expression evaluates to a `True`-like value (e.g., `1`, `"foo"`). Truthy, + /// The expression evaluates to `None`. + None, /// The expression evaluates to an unknown value (e.g., a variable `x` of unknown type). Unknown, } @@ -1173,7 +1175,7 @@ impl Truthiness { Self::False } } - Expr::NoneLiteral(_) => Self::Falsey, + Expr::NoneLiteral(_) => Self::None, Expr::EllipsisLiteral(_) => Self::Truthy, Expr::FString(f_string) => { if is_empty_f_string(f_string) { @@ -1247,6 +1249,7 @@ impl Truthiness { match self { Self::True | Self::Truthy => Some(true), Self::False | Self::Falsey => Some(false), + Self::None => Some(false), Self::Unknown => None, } }