mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-20057 Support Type[Any] and handle illegal typing.Type annotations
This commit is contained in:
@@ -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
|
||||
|
||||
+5
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user