PY-17627 PY-17733 Resolve class attributes defined in @classmethods

The logic is similar to that for instance attributes. Top-level class
attributes and methods defined in the class body get the precedence,
followed by class attributes defined with assignments in @classmethods
unless the latter would resolve to the same assignments as in

cls.attr = cls.attr + 1

finally, we scan through all other class methods resolving the name
to the first definition inside one of them.

So far, I intentionally didn't expose such attributes in findClassAttribute()
or getClassAttributes() because users of these methods assume that
this API considers only attributes defined immediately in the class body.
Adding extra definitions from class methods might break these usages.

I had to update the inspection about typing.Final, because it relied
on the fact that resolve() on assignment targets on class objects can
lead only to those top-level class attributes, where type hints are normally
located, but now it can lead to assignments to a qualified attribute inside
a containing class method.

GitOrigin-RevId: 0ca5bdaa4efca127ac187e822a49df6795e1028a
This commit is contained in:
Mikhail Golubev
2024-03-19 11:24:18 +02:00
committed by intellij-monorepo-bot
parent c0c2bd301b
commit cef42660a3
20 changed files with 245 additions and 3 deletions

View File

@@ -28,6 +28,7 @@ import com.jetbrains.python.psi.stubs.PyClassStub;
import com.jetbrains.python.psi.types.PyClassLikeType;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -294,8 +295,22 @@ public interface PyClass extends PyAstClass, PsiNameIdentifierOwner, PyCompoundS
@Nullable
List<String> getSlots(@Nullable TypeEvalContext context);
/**
* Process all declarations appearing at the syntactic level of this class' body, in particular class attributes, both
* assignments and type declarations, methods, and nested classes.
*/
boolean processClassLevelDeclarations(@NotNull PsiScopeProcessor processor);
/**
* Process all definitions that can be accessed as attributes of this class.
* These include immediate class-level declarations scanned by {@link #processClassLevelDeclarations(PsiScopeProcessor)} and class
* object's attributes initialized in methods decorated with {@code @classmethod}.
*/
@ApiStatus.Internal
default boolean processClassObjectAttributes(@NotNull PsiScopeProcessor processor, @Nullable PsiElement location) {
return processClassLevelDeclarations(processor);
}
boolean processInstanceLevelDeclarations(@NotNull PsiScopeProcessor processor, @Nullable PsiElement location);
//TODO: Add "addMetaClass" or move methods out of here