PY-25832 Consider only class attributes for TypeVars wrapped in typing.Type

GitOrigin-RevId: 7cbdabfcbb5acad68e801c0c755a84acae7e8310
This commit is contained in:
Mikhail Golubev
2020-09-11 18:54:26 +03:00
committed by intellij-monorepo-bot
parent 69aae259b1
commit 2d4cada4b3
5 changed files with 70 additions and 4 deletions

View File

@@ -1869,6 +1869,15 @@ public abstract class PythonCommonCompletionTest extends PythonCommonTestCase {
});
}
// PY-25832
public void testTypeVarClassObjectBoundAttributes() {
runWithLanguageLevel(LanguageLevel.getLatest(), () -> {
List<String> variants = doTestByFile();
assertContainsElements(variants, "class_attr");
assertDoesntContain(variants, "inst_attr");
});
}
private void doTestHasattrContributor(String[] inList, String[] notInList) {
doTestHasattrContributor("hasattrCompletion/" + getTestName(true) + ".py", inList, notInList);
}

View File

@@ -20,8 +20,8 @@ import java.util.List;
public class PyGenericType implements PyType, PyInstantiableType<PyGenericType> {
@NotNull private final String myName;
@Nullable private final PyType myBound;
private boolean myIsDefinition = false;
private final PyTargetExpression myTargetExpression;
private final boolean myIsDefinition;
@Nullable private final PyTargetExpression myTargetExpression;
public PyGenericType(@NotNull String name, @Nullable PyType bound) {
this(name, bound, false);
@@ -50,12 +50,30 @@ public class PyGenericType implements PyType, PyInstantiableType<PyGenericType>
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext resolveContext) {
return myBound != null ? myBound.resolveMember(name, location, direction, resolveContext) : null;
PyType bound = getBoundPromotedToClassObjectTypesIfNeeded();
if (bound != null) {
return bound.resolveMember(name, location, direction, resolveContext);
}
return null;
}
@Override
public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) {
return myBound != null ? myBound.getCompletionVariants(completionPrefix, location, context) : ArrayUtilRt.EMPTY_OBJECT_ARRAY;
PyType bound = getBoundPromotedToClassObjectTypesIfNeeded();
if (bound != null) {
return bound.getCompletionVariants(completionPrefix, location, context);
}
return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
}
@Nullable
private PyType getBoundPromotedToClassObjectTypesIfNeeded() {
if (myIsDefinition) {
return PyTypeUtil.toStream(myBound)
.map(t -> t instanceof PyInstantiableType ? ((PyInstantiableType<?>)t).toClass() : t)
.collect(PyTypeUtil.toUnion());
}
return myBound;
}
@NotNull

View File

@@ -0,0 +1,15 @@
from typing import Type, TypeVar
class MyClass:
class_attr = 42
def __init__(self, attr):
self.inst_attr = attr
T = TypeVar('T', bound=MyClass)
def func(x: Type[T]):
x.<caret>

View File

@@ -0,0 +1,17 @@
from typing import Type, TypeVar
class MyClass:
class_attr = 42
def __init__(self, attr):
self.inst_attr = attr
T = TypeVar('T', bound=MyClass)
def func(x: Type[T]):
# It will be resolved on "Go to Declaration" in the editor due to the "implicit" resolve context.
x.inst_attr
# <ref>

View File

@@ -854,4 +854,11 @@ public class Py3ResolveTest extends PyResolveTestCase {
assertResolvesTo(PyFunction.class, "bit_length", "builtins.pyi");
});
}
// PY-25832
public void testTypeVarClassObjectBoundAttribute() {
runWithLanguageLevel(LanguageLevel.getLatest(), () -> {
assertNull(doResolve());
});
}
}