[ruff] Avoid treating named expressions as static keys (RUF011) (#9494)

Closes https://github.com/astral-sh/ruff/issues/9487.
This commit is contained in:
Charlie Marsh 2024-01-12 14:33:45 -05:00 committed by GitHub
parent 7504bf347b
commit 009430e034
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 49 deletions

View file

@ -10,6 +10,8 @@ constant = 5
{value.attribute: value.upper() for value in data for constant in data} {value.attribute: value.upper() for value in data for constant in data}
{constant[value]: value.upper() for value in data for constant in data} {constant[value]: value.upper() for value in data for constant in data}
{value[constant]: value.upper() for value in data for constant in data} {value[constant]: value.upper() for value in data for constant in data}
{local_id: token for token in tokens if (local_id := _extract_local_id(token)) is not None}
{key: kwargs.get(key) for key in kwargs.keys() if not params.get(key)}
# Errors # Errors
{"key": value.upper() for value in data} {"key": value.upper() for value in data}
@ -20,3 +22,5 @@ constant = 5
{constant + constant: value.upper() for value in data} {constant + constant: value.upper() for value in data}
{constant.attribute: value.upper() for value in data} {constant.attribute: value.upper() for value in data}
{constant[0]: value.upper() for value in data} {constant[0]: value.upper() for value in data}
{tokens: token for token in tokens}

View file

@ -2,7 +2,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::helpers; use ruff_python_ast::helpers;
use ruff_python_ast::helpers::NameFinder; use ruff_python_ast::helpers::{NameFinder, StoredNameFinder};
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -80,7 +80,7 @@ impl Violation for UnusedLoopControlVariable {
/// B007 /// B007
pub(crate) fn unused_loop_control_variable(checker: &mut Checker, stmt_for: &ast::StmtFor) { pub(crate) fn unused_loop_control_variable(checker: &mut Checker, stmt_for: &ast::StmtFor) {
let control_names = { let control_names = {
let mut finder = NameFinder::default(); let mut finder = StoredNameFinder::default();
finder.visit_expr(stmt_for.target.as_ref()); finder.visit_expr(stmt_for.target.as_ref());
finder.names finder.names
}; };

View file

@ -2,7 +2,7 @@ use rustc_hash::FxHashMap;
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::NameFinder; use ruff_python_ast::helpers::StoredNameFinder;
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -51,9 +51,9 @@ impl Violation for StaticKeyDictComprehension {
pub(crate) fn static_key_dict_comprehension(checker: &mut Checker, dict_comp: &ast::ExprDictComp) { pub(crate) fn static_key_dict_comprehension(checker: &mut Checker, dict_comp: &ast::ExprDictComp) {
// Collect the bound names in the comprehension's generators. // Collect the bound names in the comprehension's generators.
let names = { let names = {
let mut visitor = NameFinder::default(); let mut visitor = StoredNameFinder::default();
for generator in &dict_comp.generators { for generator in &dict_comp.generators {
visitor.visit_expr(&generator.target); visitor.visit_comprehension(generator);
} }
visitor.names visitor.names
}; };

View file

@ -1,80 +1,90 @@
--- ---
source: crates/ruff_linter/src/rules/ruff/mod.rs source: crates/ruff_linter/src/rules/ruff/mod.rs
--- ---
RUF011.py:15:2: RUF011 Dictionary comprehension uses static key: `"key"` RUF011.py:17:2: RUF011 Dictionary comprehension uses static key: `"key"`
| |
14 | # Errors 16 | # Errors
15 | {"key": value.upper() for value in data} 17 | {"key": value.upper() for value in data}
| ^^^^^ RUF011 | ^^^^^ RUF011
16 | {True: value.upper() for value in data} 18 | {True: value.upper() for value in data}
17 | {0: value.upper() for value in data} 19 | {0: value.upper() for value in data}
| |
RUF011.py:16:2: RUF011 Dictionary comprehension uses static key: `True` RUF011.py:18:2: RUF011 Dictionary comprehension uses static key: `True`
| |
14 | # Errors 16 | # Errors
15 | {"key": value.upper() for value in data} 17 | {"key": value.upper() for value in data}
16 | {True: value.upper() for value in data} 18 | {True: value.upper() for value in data}
| ^^^^ RUF011 | ^^^^ RUF011
17 | {0: value.upper() for value in data} 19 | {0: value.upper() for value in data}
18 | {(1, "a"): value.upper() for value in data} # Constant tuple 20 | {(1, "a"): value.upper() for value in data} # Constant tuple
| |
RUF011.py:17:2: RUF011 Dictionary comprehension uses static key: `0` RUF011.py:19:2: RUF011 Dictionary comprehension uses static key: `0`
| |
15 | {"key": value.upper() for value in data} 17 | {"key": value.upper() for value in data}
16 | {True: value.upper() for value in data} 18 | {True: value.upper() for value in data}
17 | {0: value.upper() for value in data} 19 | {0: value.upper() for value in data}
| ^ RUF011 | ^ RUF011
18 | {(1, "a"): value.upper() for value in data} # Constant tuple 20 | {(1, "a"): value.upper() for value in data} # Constant tuple
19 | {constant: value.upper() for value in data} 21 | {constant: value.upper() for value in data}
| |
RUF011.py:18:2: RUF011 Dictionary comprehension uses static key: `(1, "a")` RUF011.py:20:2: RUF011 Dictionary comprehension uses static key: `(1, "a")`
| |
16 | {True: value.upper() for value in data} 18 | {True: value.upper() for value in data}
17 | {0: value.upper() for value in data} 19 | {0: value.upper() for value in data}
18 | {(1, "a"): value.upper() for value in data} # Constant tuple 20 | {(1, "a"): value.upper() for value in data} # Constant tuple
| ^^^^^^^^ RUF011 | ^^^^^^^^ RUF011
19 | {constant: value.upper() for value in data} 21 | {constant: value.upper() for value in data}
20 | {constant + constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data}
| |
RUF011.py:19:2: RUF011 Dictionary comprehension uses static key: `constant` RUF011.py:21:2: RUF011 Dictionary comprehension uses static key: `constant`
| |
17 | {0: value.upper() for value in data} 19 | {0: value.upper() for value in data}
18 | {(1, "a"): value.upper() for value in data} # Constant tuple 20 | {(1, "a"): value.upper() for value in data} # Constant tuple
19 | {constant: value.upper() for value in data} 21 | {constant: value.upper() for value in data}
| ^^^^^^^^ RUF011 | ^^^^^^^^ RUF011
20 | {constant + constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data}
21 | {constant.attribute: value.upper() for value in data} 23 | {constant.attribute: value.upper() for value in data}
| |
RUF011.py:20:2: RUF011 Dictionary comprehension uses static key: `constant + constant` RUF011.py:22:2: RUF011 Dictionary comprehension uses static key: `constant + constant`
| |
18 | {(1, "a"): value.upper() for value in data} # Constant tuple 20 | {(1, "a"): value.upper() for value in data} # Constant tuple
19 | {constant: value.upper() for value in data} 21 | {constant: value.upper() for value in data}
20 | {constant + constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data}
| ^^^^^^^^^^^^^^^^^^^ RUF011 | ^^^^^^^^^^^^^^^^^^^ RUF011
21 | {constant.attribute: value.upper() for value in data} 23 | {constant.attribute: value.upper() for value in data}
22 | {constant[0]: value.upper() for value in data} 24 | {constant[0]: value.upper() for value in data}
| |
RUF011.py:21:2: RUF011 Dictionary comprehension uses static key: `constant.attribute` RUF011.py:23:2: RUF011 Dictionary comprehension uses static key: `constant.attribute`
| |
19 | {constant: value.upper() for value in data} 21 | {constant: value.upper() for value in data}
20 | {constant + constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data}
21 | {constant.attribute: value.upper() for value in data} 23 | {constant.attribute: value.upper() for value in data}
| ^^^^^^^^^^^^^^^^^^ RUF011 | ^^^^^^^^^^^^^^^^^^ RUF011
22 | {constant[0]: value.upper() for value in data} 24 | {constant[0]: value.upper() for value in data}
25 | {tokens: token for token in tokens}
| |
RUF011.py:22:2: RUF011 Dictionary comprehension uses static key: `constant[0]` RUF011.py:24:2: RUF011 Dictionary comprehension uses static key: `constant[0]`
| |
20 | {constant + constant: value.upper() for value in data} 22 | {constant + constant: value.upper() for value in data}
21 | {constant.attribute: value.upper() for value in data} 23 | {constant.attribute: value.upper() for value in data}
22 | {constant[0]: value.upper() for value in data} 24 | {constant[0]: value.upper() for value in data}
| ^^^^^^^^^^^ RUF011 | ^^^^^^^^^^^ RUF011
25 | {tokens: token for token in tokens}
|
RUF011.py:25:2: RUF011 Dictionary comprehension uses static key: `tokens`
|
23 | {constant.attribute: value.upper() for value in data}
24 | {constant[0]: value.upper() for value in data}
25 | {tokens: token for token in tokens}
| ^^^^^^ RUF011
| |

View file

@ -914,6 +914,27 @@ where
} }
} }
/// A [`Visitor`] to collect all stored [`Expr::Name`] nodes in an AST.
#[derive(Debug, Default)]
pub struct StoredNameFinder<'a> {
/// A map from identifier to defining expression.
pub names: FxHashMap<&'a str, &'a ast::ExprName>,
}
impl<'a, 'b> Visitor<'b> for StoredNameFinder<'a>
where
'b: 'a,
{
fn visit_expr(&mut self, expr: &'a Expr) {
if let Expr::Name(name) = expr {
if name.ctx.is_store() {
self.names.insert(&name.id, name);
}
}
crate::visitor::walk_expr(self, expr);
}
}
/// A [`StatementVisitor`] that collects all `return` statements in a function or method. /// A [`StatementVisitor`] that collects all `return` statements in a function or method.
#[derive(Default)] #[derive(Default)]
pub struct ReturnStatementVisitor<'a> { pub struct ReturnStatementVisitor<'a> {