diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index b7900b73461b..9ec2fe8ac819 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -36,6 +36,7 @@ import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeA import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeAnnotationFile; import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyParameterTypeList; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyExpressionCodeFragmentImpl; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.PyResolveImportUtil; @@ -63,6 +64,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { public static final String NAMEDTUPLE = "typing.NamedTuple"; public static final String GENERIC = "typing.Generic"; public static final String TYPE = "typing.Type"; + public static final String ANY = "typing.Any"; public static final Pattern TYPE_COMMENT_PATTERN = Pattern.compile("# *type: *(.*)"); @@ -471,9 +473,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (callableType != null) { return Ref.create(callableType); } - final PyType classObjType = getClassObjectType(resolved, context); + final Ref classObjType = getClassObjectType(resolved, context); if (classObjType != null) { - return Ref.create(classObjType); + return classObjType; } final PyType parameterizedType = getParameterizedType(resolved, context); if (parameterizedType != null) { @@ -507,7 +509,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType getClassObjectType(@NotNull PsiElement resolved, @NotNull Context context) { + private static Ref getClassObjectType(@NotNull PsiElement resolved, @NotNull Context context) { if (resolved instanceof PySubscriptionExpression) { final PySubscriptionExpression subsExpr = (PySubscriptionExpression)resolved; final PyExpression operand = subsExpr.getOperand(); @@ -515,32 +517,33 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (operandNames.contains(TYPE)) { final PyExpression indexExpr = subsExpr.getIndexExpression(); if (indexExpr != null) { + if (resolveToQualifiedNames(indexExpr, context.getTypeContext()).contains(ANY)) { + return Ref.create(PyBuiltinCache.getInstance(resolved).getTypeType()); + } final PyType type = Ref.deref(getType(indexExpr, context)); final PyClassType classType = as(type, PyClassType.class); if (classType != null && !classType.isDefinition()) { - return new PyClassTypeImpl(classType.getPyClass(), true); + return Ref.create(new PyClassTypeImpl(classType.getPyClass(), true)); } final PyGenericType typeVar = as(type, PyGenericType.class); if (typeVar != null && !typeVar.isDefinition()) { - return new PyGenericType(typeVar.getName(), typeVar.getBound(), true); + return Ref.create(new PyGenericType(typeVar.getName(), typeVar.getBound(), true)); } } + // Map Type[Something] with unsupported type parameter to Any, instead of generic type for the class "type" + return Ref.create(); } } // Replace plain non-parametrized Type with its builtin counterpart else if (TYPE.equals(getQualifiedName(resolved))) { - return PyTypeParser.getTypeByName(resolved, PyNames.TYPE); + return Ref.create(PyBuiltinCache.getInstance(resolved).getTypeType()); } return null; } @Nullable private static Ref getAnyType(@NotNull PsiElement element) { - final PyQualifiedNameOwner qualifiedNameOwner = as(element, PyQualifiedNameOwner.class); - if (qualifiedNameOwner != null && "typing.Any".equals(qualifiedNameOwner.getQualifiedName())) { - return Ref.create(); - } - return null; + return ANY.equals(getQualifiedName(element)) ? Ref.create() : null; } @Nullable diff --git a/python/testData/inspections/PyTypeCheckerInspection/TypeAndClassObjectTypesCompatibility.py b/python/testData/inspections/PyTypeCheckerInspection/TypeAndClassObjectTypesCompatibility.py index b484b262720f..cc0be1130066 100644 --- a/python/testData/inspections/PyTypeCheckerInspection/TypeAndClassObjectTypesCompatibility.py +++ b/python/testData/inspections/PyTypeCheckerInspection/TypeAndClassObjectTypesCompatibility.py @@ -12,6 +12,10 @@ def expects_typing_type(x: Type): expects_type(x) +def expects_typing_type_any(x: Type[Any]): + expects_type(x) + + def expects_any_type_via_type_var(x: Type[T]): expects_type(x) @@ -31,6 +35,7 @@ def expects_object(x: object): expects_type(type) expects_type(object) expects_typing_type(type) +expects_typing_type_any(type) expects_typing_type(object) expects_str_class(type) expects_any_type_via_type_var(type) diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index d33fa8a7e5ea..84ac1a202199 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -840,6 +840,24 @@ public class PyTypingTest extends PyTestCase { "def f(x: Type):\n" + " expr = x"); } + + // PY-20057 + public void testTypingTypeOfAnyMapsToBuiltinType() { + doTest("type", + "from typing import Type, Any\n" + + "\n" + + "def f(x: Type[Any]):\n" + + " expr = x"); + } + + // PY-20057 + public void testIllegalTypingTypeFormat() { + doTest("Tuple[Any, Any, Any]", + "from typing import Type, Tuple\n" + + "\n" + + "def f(x: Tuple[Type[42], Type[], Type[unresolved]]):\n" + + " expr = x"); + } private void doTestNoInjectedText(@NotNull String text) { myFixture.configureByText(PythonFileType.INSTANCE, text);