mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-10 10:22:14 +00:00
Add a ScopeKind
for the __class__
cell (#20048)
Summary -- This PR aims to resolve (or help to resolve) #18442 and #19357 by encoding the CPython semantics around the `__class__` cell in our semantic model. Namely, > `__class__` is an implicit closure reference created by the compiler if any methods in a class body refer to either `__class__` or super. from the Python [docs](https://docs.python.org/3/reference/datamodel.html#creating-the-class-object). As noted in the variant docs by @AlexWaygood, we don't fully model this behavior, opting always to create the `__class__` cell binding in a new `ScopeKind::DunderClassCell` around each method definition, without checking if any method in the class body actually refers to `__class__` or `super`. As such, this PR fixes #18442 but not #19357. Test Plan -- Existing tests, plus the tests from #19783, which now pass without any rule-specific code. Note that we opted not to alter the behavior of F841 here because flagging `__class__` in these cases still seems helpful. See the discussion in https://github.com/astral-sh/ruff/pull/20048#discussion_r2296252395 and in the test comments for more information. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Mikko Leppänen <mleppan23@gmail.com>
This commit is contained in:
parent
911d5cc973
commit
bc7274d148
11 changed files with 287 additions and 37 deletions
|
@ -166,9 +166,49 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, is_macro::Is)]
|
||||
#[derive(Clone, Copy, Debug, is_macro::Is)]
|
||||
pub enum ScopeKind<'a> {
|
||||
Class(&'a ast::StmtClassDef),
|
||||
/// The implicit `__class__` scope surrounding a method which allows code in the
|
||||
/// method to access `__class__` at runtime. The closure sits in between the class
|
||||
/// scope and the function scope.
|
||||
///
|
||||
/// Parameter defaults in methods cannot access `__class__`:
|
||||
///
|
||||
/// ```pycon
|
||||
/// >>> class Bar:
|
||||
/// ... def method(self, x=__class__): ...
|
||||
/// ...
|
||||
/// Traceback (most recent call last):
|
||||
/// File "<python-input-6>", line 1, in <module>
|
||||
/// class Bar:
|
||||
/// def method(self, x=__class__): ...
|
||||
/// File "<python-input-6>", line 2, in Bar
|
||||
/// def method(self, x=__class__): ...
|
||||
/// ^^^^^^^^^
|
||||
/// NameError: name '__class__' is not defined
|
||||
/// ```
|
||||
///
|
||||
/// However, type parameters in methods *can* access `__class__`:
|
||||
///
|
||||
/// ```pycon
|
||||
/// >>> class Foo:
|
||||
/// ... def bar[T: __class__](): ...
|
||||
/// ...
|
||||
/// >>> Foo.bar.__type_params__[0].__bound__
|
||||
/// <class '__main__.Foo'>
|
||||
/// ```
|
||||
///
|
||||
/// Note that this is still not 100% accurate! At runtime, the implicit `__class__`
|
||||
/// closure is only added if the name `super` (has to be a name -- `builtins.super`
|
||||
/// and similar don't count!) or the name `__class__` is used in any method of the
|
||||
/// class. However, accurately emulating that would be both complex and probably
|
||||
/// quite expensive unless we moved to a double-traversal of each scope similar to
|
||||
/// ty. It would also only matter in extreme and unlikely edge cases. So we ignore
|
||||
/// that subtlety for now.
|
||||
///
|
||||
/// See <https://docs.python.org/3/reference/datamodel.html#creating-the-class-object>.
|
||||
DunderClassCell,
|
||||
Function(&'a ast::StmtFunctionDef),
|
||||
Generator {
|
||||
kind: GeneratorKind,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue