Minor followups to RUF052 (#14755)

## Summary

Just some minor followups to the recently merged RUF052 rule, that was
added in bf0fd04:
- Some small tweaks to the docs
- A minor code-style nit
- Some more tests for my peace of mind, just to check that the new
methods on the semantic model are working correctly

I'm adding the "internal" label as this doesn't deserve a changelog
entry. RUF052 is a new rule that hasn't been released yet.

## Test Plan

`cargo test -p ruff_linter`
This commit is contained in:
Alex Waygood 2024-12-03 13:33:29 +00:00 committed by GitHub
parent 74309008fd
commit 81bfcc9899
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 173 additions and 29 deletions

View file

@ -104,3 +104,28 @@ def fun():
x = "local"
_x = "shadows local" # [RUF052]
return _x
GLOBAL_1 = "global 1"
GLOBAL_1_ = "global 1 with trailing underscore"
def unfixables():
_GLOBAL_1 = "foo"
# unfixable because the rename would shadow a global variable
print(_GLOBAL_1) # [RUF052]
local = "local"
local_ = "local2"
# unfixable because the rename would shadow a local variable
_local = "local3" # [RUF052]
print(_local)
def nested():
_GLOBAL_1 = "foo"
# unfixable because the rename would shadow a global variable
print(_GLOBAL_1) # [RUF052]
# unfixable because the rename would shadow a variable from the outer function
_local = "local4"
print(_local)

View file

@ -10,16 +10,19 @@ use ruff_text_size::Ranged;
use crate::{checkers::ast::Checker, renamer::Renamer};
/// ## What it does
/// Checks for accesses of local dummy variables, excluding `_` and dunder variables.
/// Checks for "dummy variables" (variables that are named as if to indicate they are unused)
/// that are in fact used.
///
/// By default, "dummy variables" are any variables with names that start with leading
/// underscores. However, this is customisable using the `dummy-variable-rgx` setting).
/// underscores. However, this is customisable using the [`lint.dummy-variable-rgx`] setting).
///
/// Dunder variables are ignored by this rule, as are variables named `_`.
///
/// ## Why is this bad?
/// Marking a variable with a leading underscore conveys that it is intentionally unused within the function or method.
/// When these variables are later referenced in the code, it causes confusion and potential misunderstandings about
/// the code's intention. A variable marked as "unused" being subsequently used suggests oversight or unintentional use.
/// This detracts from the clarity and maintainability of the codebase.
/// the code's intention. If a variable marked as "unused" is subsequently used, it suggests that either the variable
/// could be given a clearer name, or that the code is accidentally making use of the wrong variable.
///
/// Sometimes leading underscores are used to avoid variables shadowing other variables, Python builtins, or Python
/// keywords. However, [PEP 8] recommends to use trailing underscores for this rather than leading underscores.
@ -39,12 +42,14 @@ use crate::{checkers::ast::Checker, renamer::Renamer};
/// ```
///
/// ## Fix availability
/// An fix is only available for variables that start with leading underscores.
/// The rule's fix is only available for variables that start with leading underscores.
/// It will also only be available if the "obvious" new name for the variable
/// would not shadow any other known variables already accessible from the scope
/// in which the variable is defined.
///
/// ## Options
/// - [`lint.dummy-variable-rgx`]
///
///
/// [PEP 8]: https://peps.python.org/pep-0008/
#[derive(ViolationMetadata)]
pub(crate) struct UsedDummyVariable {
@ -61,8 +66,7 @@ impl Violation for UsedDummyVariable {
}
fn fix_title(&self) -> Option<String> {
if let Some(shadowed_kind) = self.shadowed_kind {
return Some(match shadowed_kind {
self.shadowed_kind.map(|kind| match kind {
ShadowedKind::BuiltIn => {
"Prefer using trailing underscores to avoid shadowing a built-in".to_string()
}
@ -73,9 +77,7 @@ impl Violation for UsedDummyVariable {
"Prefer using trailing underscores to avoid shadowing a variable".to_string()
}
ShadowedKind::None => "Remove leading underscores".to_string(),
});
}
None
})
}
}

View file

@ -1,6 +1,5 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
---
RUF052.py:77:9: RUF052 [*] Local dummy variable `_var` is accessed
|
@ -130,3 +129,44 @@ RUF052.py:105:5: RUF052 [*] Local dummy variable `_x` is accessed
106 |- return _x
105 |+ x_ = "shadows local" # [RUF052]
106 |+ return x_
107 107 |
108 108 |
109 109 | GLOBAL_1 = "global 1"
RUF052.py:113:5: RUF052 Local dummy variable `_GLOBAL_1` is accessed
|
112 | def unfixables():
113 | _GLOBAL_1 = "foo"
| ^^^^^^^^^ RUF052
114 | # unfixable because the rename would shadow a global variable
115 | print(_GLOBAL_1) # [RUF052]
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:121:5: RUF052 Local dummy variable `_local` is accessed
|
120 | # unfixable because the rename would shadow a local variable
121 | _local = "local3" # [RUF052]
| ^^^^^^ RUF052
122 | print(_local)
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:125:9: RUF052 Local dummy variable `_GLOBAL_1` is accessed
|
124 | def nested():
125 | _GLOBAL_1 = "foo"
| ^^^^^^^^^ RUF052
126 | # unfixable because the rename would shadow a global variable
127 | print(_GLOBAL_1) # [RUF052]
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:130:9: RUF052 Local dummy variable `_local` is accessed
|
129 | # unfixable because the rename would shadow a variable from the outer function
130 | _local = "local4"
| ^^^^^^ RUF052
131 | print(_local)
|
= help: Prefer using trailing underscores to avoid shadowing a variable

View file

@ -1,6 +1,5 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
---
RUF052.py:77:9: RUF052 [*] Local dummy variable `_var` is accessed
|
@ -130,3 +129,44 @@ RUF052.py:105:5: RUF052 [*] Local dummy variable `_x` is accessed
106 |- return _x
105 |+ x_ = "shadows local" # [RUF052]
106 |+ return x_
107 107 |
108 108 |
109 109 | GLOBAL_1 = "global 1"
RUF052.py:113:5: RUF052 Local dummy variable `_GLOBAL_1` is accessed
|
112 | def unfixables():
113 | _GLOBAL_1 = "foo"
| ^^^^^^^^^ RUF052
114 | # unfixable because the rename would shadow a global variable
115 | print(_GLOBAL_1) # [RUF052]
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:121:5: RUF052 Local dummy variable `_local` is accessed
|
120 | # unfixable because the rename would shadow a local variable
121 | _local = "local3" # [RUF052]
| ^^^^^^ RUF052
122 | print(_local)
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:125:9: RUF052 Local dummy variable `_GLOBAL_1` is accessed
|
124 | def nested():
125 | _GLOBAL_1 = "foo"
| ^^^^^^^^^ RUF052
126 | # unfixable because the rename would shadow a global variable
127 | print(_GLOBAL_1) # [RUF052]
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:130:9: RUF052 Local dummy variable `_local` is accessed
|
129 | # unfixable because the rename would shadow a variable from the outer function
130 | _local = "local4"
| ^^^^^^ RUF052
131 | print(_local)
|
= help: Prefer using trailing underscores to avoid shadowing a variable

View file

@ -1,6 +1,5 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
---
RUF052.py:21:9: RUF052 Local dummy variable `arg` is accessed
|
@ -119,3 +118,41 @@ RUF052.py:105:5: RUF052 Local dummy variable `_x` is accessed
106 | return _x
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:113:5: RUF052 Local dummy variable `_GLOBAL_1` is accessed
|
112 | def unfixables():
113 | _GLOBAL_1 = "foo"
| ^^^^^^^^^ RUF052
114 | # unfixable because the rename would shadow a global variable
115 | print(_GLOBAL_1) # [RUF052]
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:121:5: RUF052 Local dummy variable `_local` is accessed
|
120 | # unfixable because the rename would shadow a local variable
121 | _local = "local3" # [RUF052]
| ^^^^^^ RUF052
122 | print(_local)
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:125:9: RUF052 Local dummy variable `_GLOBAL_1` is accessed
|
124 | def nested():
125 | _GLOBAL_1 = "foo"
| ^^^^^^^^^ RUF052
126 | # unfixable because the rename would shadow a global variable
127 | print(_GLOBAL_1) # [RUF052]
|
= help: Prefer using trailing underscores to avoid shadowing a variable
RUF052.py:130:9: RUF052 Local dummy variable `_local` is accessed
|
129 | # unfixable because the rename would shadow a variable from the outer function
130 | _local = "local4"
| ^^^^^^ RUF052
131 | print(_local)
|
= help: Prefer using trailing underscores to avoid shadowing a variable