PY-20057 Support Type[Any] and handle illegal typing.Type annotations

This commit is contained in:
Mikhail Golubev
2017-03-06 15:20:44 +03:00
parent 1bce2d9bf5
commit 0a2c73161d
3 changed files with 37 additions and 11 deletions
@@ -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<PyType> 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<PyType> 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<PyType> 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
@@ -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(<warning descr="Expected type 'Type[str]', got 'type' instead">type</warning>)
expects_any_type_via_type_var(type)
@@ -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);