From d431a156cdcf50d30e4f96aabede0f9db1075d96 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 31 Jan 2018 20:06:55 +0300 Subject: [PATCH] Update provided type for `typing.Callable` (PY-28177) Use custom type inheriting class-like type to suppress `Callable[...]` warnings. Remove some checks against `Callable` in type checker because they are processed below when matching callable types. Update custom type to detemine ability to call from the name as well. --- .../com/jetbrains/python/PyCustomType.java | 12 +++++----- .../controlflow/PyTypeAssertionEvaluator.java | 3 ++- .../typing/PyTypingTypeProvider.java | 10 ++++---- .../python/psi/types/PyTypeChecker.java | 9 ------- .../typingOpaqueNameDunderGetItem.py | 24 +++++++++++++++++++ ...Py3UnresolvedReferencesInspectionTest.java | 5 ++++ 6 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/typingOpaqueNameDunderGetItem.py diff --git a/python/src/com/jetbrains/python/PyCustomType.java b/python/src/com/jetbrains/python/PyCustomType.java index 978fd46434b4..772e9c136693 100644 --- a/python/src/com/jetbrains/python/PyCustomType.java +++ b/python/src/com/jetbrains/python/PyCustomType.java @@ -25,6 +25,7 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.Processor; import com.jetbrains.NotNullPredicate; +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.RatedResolveResult; @@ -117,7 +118,7 @@ public class PyCustomType implements PyClassLikeType { return Collections.emptyList(); } - @Nullable + @NotNull @Override public final List resolveMember(@NotNull final String name, @Nullable final PyExpression location, @@ -128,9 +129,8 @@ public class PyCustomType implements PyClassLikeType { // Delegate calls to classes, we mimic but filter if filter is set. for (final PyClassLikeType typeToMimic : myTypesToMimic) { - final List results = typeToMimic.toInstance().resolveMember( - name, location, direction, resolveContext, inherited - ); + final List results = + typeToMimic.toInstance().resolveMember(name, location, direction, resolveContext, inherited); if (results != null) { globalResult.addAll(Collections2.filter(results, new ResolveFilter())); @@ -158,7 +158,7 @@ public class PyCustomType implements PyClassLikeType { @Override public final boolean isCallable() { - if (!myInstanceType) { + if (!myInstanceType || PyTypingTypeProvider.CALLABLE.equals(myQualifiedName)) { return true; // Due to ctor } for (final PyClassLikeType typeToMimic : myTypesToMimic) { @@ -182,7 +182,7 @@ public class PyCustomType implements PyClassLikeType { return getReturnType(context); } - @Nullable + @NotNull @Override public final List resolveMember(@NotNull final String name, @Nullable final PyExpression location, diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java index f40f894b32b7..4c359e426b05 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyTypeAssertionEvaluator.java @@ -6,6 +6,7 @@ import com.intellij.psi.PsiElement; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyConstantExpressionEvaluator; import com.jetbrains.python.psi.types.*; @@ -65,7 +66,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor { if (args.length == 1 && args[0] instanceof PyReferenceExpression) { final PyReferenceExpression target = (PyReferenceExpression)args[0]; - pushAssertion(target, myPositive, false, context -> new PyCallableTypeImpl(null, null)); + pushAssertion(target, myPositive, false, context -> PyTypingTypeProvider.createTypingCallableType(node)); } } else if (node.isCalleeText(PyNames.ISSUBCLASS)) { diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index bf47a9810999..6d146d9b63b3 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -65,7 +65,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { public static final String PROTOCOL = "typing.Protocol"; public static final String TYPE = "typing.Type"; public static final String ANY = "typing.Any"; - private static final String CALLABLE = "typing.Callable"; + public static final String CALLABLE = "typing.Callable"; private static final String LIST = "typing.List"; private static final String DICT = "typing.Dict"; private static final String DEFAULT_DICT = "typing.DefaultDict"; @@ -169,7 +169,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { // Check for the exact name in advance for performance reasons if ("Callable".equals(referenceExpression.getName())) { if (resolveToQualifiedNames(referenceExpression, context).contains(CALLABLE)) { - return createTypingCallableType(); + return createTypingCallableType(referenceExpression); } } return null; @@ -357,8 +357,8 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } @NotNull - private static PyType createTypingCallableType() { - return new PyCallableTypeImpl(null, null); + public static PyType createTypingCallableType(@NotNull PsiElement anchor) { + return new PyCustomType(CALLABLE, null, false, PyBuiltinCache.getInstance(anchor).getObjectType()); } private static boolean omitFirstParamInTypeComment(@NotNull PyFunction func, @NotNull PyFunctionTypeAnnotation annotation) { @@ -476,7 +476,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } // Depends on typing.Callable defined as a target expression if (CALLABLE.equals(target.getQualifiedName())) { - return createTypingCallableType(); + return createTypingCallableType(referenceTarget); } final PyType collection = getCollection(target, context); diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 40767851c35b..5243f9492ed0 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -216,19 +216,10 @@ public class PyTypeChecker { else if (matchClasses(superClass, subClass, context)) { return true; } - else if (actualClassType.isDefinition() && PyNames.CALLABLE.equals(expected.getName())) { - return true; - } if (expected.equals(actual)) { return true; } } - if (actual instanceof PyFunctionTypeImpl && expectedClassType != null) { - final PyClass superClass = expectedClassType.getPyClass(); - if (PyNames.CALLABLE.equals(superClass.getName())) { - return true; - } - } if (actual instanceof PyStructuralType && ((PyStructuralType)actual).isInferredFromUsages()) { return true; } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingOpaqueNameDunderGetItem.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingOpaqueNameDunderGetItem.py new file mode 100644 index 000000000000..af1adbc331c7 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingOpaqueNameDunderGetItem.py @@ -0,0 +1,24 @@ +from typing import TypeVar, Tuple, Generic, Callable, Type, ClassVar, Union, Optional, List, Dict, \ + DefaultDict, Set, FrozenSet, Counter, Deque, ChainMap, Protocol + +T = TypeVar("T") + +# special forms +print(Tuple[T]) +print(Generic[T]) +print(Protocol[T]) +print(Callable[[T], T]) +print(Type[T]) +print(ClassVar[T]) + +# aliases +print(Union[T]) +print(Optional[T]) +print(List[T]) +print(Dict[T, T]) +print(DefaultDict[T, T]) +print(Set[T]) +print(FrozenSet[T]) +print(Counter[T]) +print(Deque[T]) +print(ChainMap[T, T]) diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java index e3d3a7763ce7..9df913c07586 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -220,6 +220,11 @@ public class Py3UnresolvedReferencesInspectionTest extends PyInspectionTestCase doTest(); } + // PY-28177 + public void testTypingOpaqueNameDunderGetItem() { + doTest(); + } + // PY-21655 public void testUsageOfFunctionDecoratedWithAsyncioCoroutine() { doMultiFileTest("a.py");