mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-23 13:36:52 +00:00
[ruff] Avoid false positive on ClassVar reassignment (RUF012) (#21478)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}) (push) Blocked by required conditions
CI / cargo test (macos-latest) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (${{ github.repository == 'astral-sh/ruff' && 'depot-windows-2022-16' || 'windows-latest' }}) (push) Blocked by required conditions
CI / cargo test (macos-latest) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
<!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? --> ## Summary <!-- What's the purpose of the change? What does it do, and why? --> Fixes #21389 Avoid RUF012 false positives when reassigning a ClassVar ## Test Plan <!-- How was it tested? --> Added the new reassignment scenario to `crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py`. --------- Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
This commit is contained in:
parent
e4a32ba644
commit
b1e354bd99
2 changed files with 23 additions and 3 deletions
|
|
@ -132,3 +132,9 @@ class AWithQuotes:
|
|||
final_variable: 'Final[list[int]]' = []
|
||||
class_variable_without_subscript: 'ClassVar' = []
|
||||
final_variable_without_subscript: 'Final' = []
|
||||
|
||||
|
||||
# Reassignment of a ClassVar should not trigger RUF012
|
||||
class P:
|
||||
class_variable: ClassVar[list] = [10, 20, 30, 40, 50]
|
||||
class_variable = [*class_variable[0::1], *class_variable[2::3]]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use ruff_python_ast::{self as ast, Stmt};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
|
|
@ -96,6 +97,9 @@ impl Violation for MutableClassDefault {
|
|||
|
||||
/// RUF012
|
||||
pub(crate) fn mutable_class_default(checker: &Checker, class_def: &ast::StmtClassDef) {
|
||||
// Collect any `ClassVar`s we find in case they get reassigned later.
|
||||
let mut class_var_targets = FxHashSet::default();
|
||||
|
||||
for statement in &class_def.body {
|
||||
match statement {
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
|
|
@ -104,6 +108,12 @@ pub(crate) fn mutable_class_default(checker: &Checker, class_def: &ast::StmtClas
|
|||
value: Some(value),
|
||||
..
|
||||
}) => {
|
||||
if let ast::Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
|
||||
if is_class_var_annotation(annotation, checker.semantic()) {
|
||||
class_var_targets.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
if !is_special_attribute(target)
|
||||
&& is_mutable_expr(value, checker.semantic())
|
||||
&& !is_class_var_annotation(annotation, checker.semantic())
|
||||
|
|
@ -123,8 +133,12 @@ pub(crate) fn mutable_class_default(checker: &Checker, class_def: &ast::StmtClas
|
|||
}
|
||||
}
|
||||
Stmt::Assign(ast::StmtAssign { value, targets, .. }) => {
|
||||
if !targets.iter().all(is_special_attribute)
|
||||
&& is_mutable_expr(value, checker.semantic())
|
||||
if !targets.iter().all(|target| {
|
||||
is_special_attribute(target)
|
||||
|| target
|
||||
.as_name_expr()
|
||||
.is_some_and(|name| class_var_targets.contains(&name.id))
|
||||
}) && is_mutable_expr(value, checker.semantic())
|
||||
{
|
||||
// Avoid, e.g., Pydantic and msgspec models, which end up copying defaults on instance creation.
|
||||
if has_default_copy_semantics(class_def, checker.semantic()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue