mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-25 22:29:02 +00:00
[flake8-bultins] Detect class-scope builtin shadowing in decorators, default args, and attribute initializers (A003) (#20178)
## Summary Fix #20171 --------- Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
This commit is contained in:
parent
32d00cd569
commit
094bf70a60
5 changed files with 112 additions and 6 deletions
|
|
@ -19,3 +19,18 @@ class MyClass:
|
|||
|
||||
def attribute_usage(self) -> id:
|
||||
pass
|
||||
|
||||
|
||||
class C:
|
||||
@staticmethod
|
||||
def property(f):
|
||||
return f
|
||||
|
||||
id = 1
|
||||
|
||||
@[property][0]
|
||||
def f(self, x=[id]):
|
||||
return x
|
||||
|
||||
bin = 2
|
||||
foo = [bin]
|
||||
|
|
|
|||
|
|
@ -228,3 +228,10 @@ pub(crate) const fn is_sim910_expanded_key_support_enabled(settings: &LinterSett
|
|||
pub(crate) const fn is_fix_builtin_open_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/20178
|
||||
pub(crate) const fn is_a003_class_scope_shadowing_expansion_enabled(
|
||||
settings: &LinterSettings,
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ mod tests {
|
|||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_builtins;
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::{test_path, test_resource_path};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
|
|
@ -63,6 +64,28 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
rule_code.noqa_code(),
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_builtins").join(path).as_path(),
|
||||
&LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
flake8_builtins: flake8_builtins::settings::Settings {
|
||||
strict_checking: true,
|
||||
..Default::default()
|
||||
},
|
||||
..LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(
|
||||
Rule::StdlibModuleShadowing,
|
||||
Path::new("A005/modules/utils/logging.py"),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::Violation;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_a003_class_scope_shadowing_expansion_enabled;
|
||||
use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
||||
|
||||
/// ## What it does
|
||||
|
|
@ -123,16 +124,26 @@ pub(crate) fn builtin_attribute_shadowing(
|
|||
// def repeat(value: int, times: int) -> list[int]:
|
||||
// return [value] * times
|
||||
// ```
|
||||
// In stable, only consider references whose first non-type parent scope is the class
|
||||
// scope (e.g., decorators, default args, and attribute initializers).
|
||||
// In preview, also consider references from within the class scope.
|
||||
let consider_reference = |reference_scope_id: ScopeId| {
|
||||
if is_a003_class_scope_shadowing_expansion_enabled(checker.settings()) {
|
||||
if reference_scope_id == scope_id {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
checker
|
||||
.semantic()
|
||||
.first_non_type_parent_scope_id(reference_scope_id)
|
||||
== Some(scope_id)
|
||||
};
|
||||
|
||||
for reference in binding
|
||||
.references
|
||||
.iter()
|
||||
.map(|reference_id| checker.semantic().reference(*reference_id))
|
||||
.filter(|reference| {
|
||||
checker
|
||||
.semantic()
|
||||
.first_non_type_parent_scope_id(reference.scope_id())
|
||||
== Some(scope_id)
|
||||
})
|
||||
.filter(|reference| consider_reference(reference.scope_id()))
|
||||
{
|
||||
checker.report_diagnostic(
|
||||
BuiltinAttributeShadowing {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
|
||||
---
|
||||
A003 Python builtin is shadowed by method `str` from line 14
|
||||
--> A003.py:17:31
|
||||
|
|
||||
15 | pass
|
||||
16 |
|
||||
17 | def method_usage(self) -> str:
|
||||
| ^^^
|
||||
18 | pass
|
||||
|
|
||||
|
||||
A003 Python builtin is shadowed by class attribute `id` from line 3
|
||||
--> A003.py:20:34
|
||||
|
|
||||
18 | pass
|
||||
19 |
|
||||
20 | def attribute_usage(self) -> id:
|
||||
| ^^
|
||||
21 | pass
|
||||
|
|
||||
|
||||
A003 Python builtin is shadowed by method `property` from line 26
|
||||
--> A003.py:31:7
|
||||
|
|
||||
29 | id = 1
|
||||
30 |
|
||||
31 | @[property][0]
|
||||
| ^^^^^^^^
|
||||
32 | def f(self, x=[id]):
|
||||
33 | return x
|
||||
|
|
||||
|
||||
A003 Python builtin is shadowed by class attribute `id` from line 29
|
||||
--> A003.py:32:20
|
||||
|
|
||||
31 | @[property][0]
|
||||
32 | def f(self, x=[id]):
|
||||
| ^^
|
||||
33 | return x
|
||||
|
|
||||
|
||||
A003 Python builtin is shadowed by class attribute `bin` from line 35
|
||||
--> A003.py:36:12
|
||||
|
|
||||
35 | bin = 2
|
||||
36 | foo = [bin]
|
||||
| ^^^
|
||||
|
|
||||
Loading…
Add table
Add a link
Reference in a new issue