mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-28 15:27:43 +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:
|
def attribute_usage(self) -> id:
|
||||||
pass
|
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 {
|
pub(crate) const fn is_fix_builtin_open_enabled(settings: &LinterSettings) -> bool {
|
||||||
settings.preview.is_enabled()
|
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::registry::Rule;
|
||||||
use crate::rules::flake8_builtins;
|
use crate::rules::flake8_builtins;
|
||||||
use crate::settings::LinterSettings;
|
use crate::settings::LinterSettings;
|
||||||
|
use crate::settings::types::PreviewMode;
|
||||||
use crate::test::{test_path, test_resource_path};
|
use crate::test::{test_path, test_resource_path};
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
|
|
||||||
|
|
@ -63,6 +64,28 @@ mod tests {
|
||||||
Ok(())
|
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(
|
#[test_case(
|
||||||
Rule::StdlibModuleShadowing,
|
Rule::StdlibModuleShadowing,
|
||||||
Path::new("A005/modules/utils/logging.py"),
|
Path::new("A005/modules/utils/logging.py"),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::Violation;
|
use crate::Violation;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::preview::is_a003_class_scope_shadowing_expansion_enabled;
|
||||||
use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
use crate::rules::flake8_builtins::helpers::shadows_builtin;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
|
|
@ -123,16 +124,26 @@ pub(crate) fn builtin_attribute_shadowing(
|
||||||
// def repeat(value: int, times: int) -> list[int]:
|
// def repeat(value: int, times: int) -> list[int]:
|
||||||
// return [value] * times
|
// 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
|
for reference in binding
|
||||||
.references
|
.references
|
||||||
.iter()
|
.iter()
|
||||||
.map(|reference_id| checker.semantic().reference(*reference_id))
|
.map(|reference_id| checker.semantic().reference(*reference_id))
|
||||||
.filter(|reference| {
|
.filter(|reference| consider_reference(reference.scope_id()))
|
||||||
checker
|
|
||||||
.semantic()
|
|
||||||
.first_non_type_parent_scope_id(reference.scope_id())
|
|
||||||
== Some(scope_id)
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
checker.report_diagnostic(
|
checker.report_diagnostic(
|
||||||
BuiltinAttributeShadowing {
|
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