mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-18 09:30:35 +00:00
Restrict builtin-attribute-shadowing
to actual shadowed references (#9462)
## Summary This PR attempts to improve `builtin-attribute-shadowing` (`A003`), a rule which has been repeatedly criticized, but _does_ have value (just not in the current form). Historically, this rule would flag cases like: ```python class Class: id: int ``` This led to an increasing number of exceptions and special-cases to the rule over time to try and improve it's specificity (e.g., ignore `TypedDict`, ignore `@override`). The crux of the issue is that given the above, referencing `id` will never resolve to `Class.id`, so the shadowing is actually fine. There's one exception, however: ```python class Class: id: int def do_thing() -> id: pass ``` Here, `id` actually resolves to the `id` attribute on the class, not the `id` builtin. So this PR completely reworks the rule around this _much_ more targeted case, which will almost always be a mistake: when you reference a class member from within the class, and that member shadows a builtin. Closes https://github.com/astral-sh/ruff/issues/6524. Closes https://github.com/astral-sh/ruff/issues/7806.
This commit is contained in:
parent
7fc51d29c5
commit
25bafd2d66
8 changed files with 167 additions and 272 deletions
|
@ -984,6 +984,11 @@ impl<'a> SemanticModel<'a> {
|
|||
scope.parent.map(|scope_id| &self.scopes[scope_id])
|
||||
}
|
||||
|
||||
/// Returns the ID of the parent of the given [`ScopeId`], if any.
|
||||
pub fn parent_scope_id(&self, scope_id: ScopeId) -> Option<ScopeId> {
|
||||
self.scopes[scope_id].parent
|
||||
}
|
||||
|
||||
/// Returns the first parent of the given [`Scope`] that is not of [`ScopeKind::Type`], if any.
|
||||
pub fn first_non_type_parent_scope(&self, scope: &Scope) -> Option<&Scope<'a>> {
|
||||
let mut current_scope = scope;
|
||||
|
@ -997,6 +1002,19 @@ impl<'a> SemanticModel<'a> {
|
|||
None
|
||||
}
|
||||
|
||||
/// Returns the first parent of the given [`ScopeId`] that is not of [`ScopeKind::Type`], if any.
|
||||
pub fn first_non_type_parent_scope_id(&self, scope_id: ScopeId) -> Option<ScopeId> {
|
||||
let mut current_scope_id = scope_id;
|
||||
while let Some(parent_id) = self.parent_scope_id(current_scope_id) {
|
||||
if self.scopes[parent_id].kind.is_type() {
|
||||
current_scope_id = parent_id;
|
||||
} else {
|
||||
return Some(parent_id);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Return the [`Stmt`] corresponding to the given [`NodeId`].
|
||||
#[inline]
|
||||
pub fn node(&self, node_id: NodeId) -> &NodeRef<'a> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue