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.
This commit is contained in:
Semyon Proshev
2018-02-05 17:19:47 +03:00
parent d647125a63
commit d431a156cd
6 changed files with 42 additions and 21 deletions
@@ -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<? extends RatedResolveResult> 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<? extends RatedResolveResult> results = typeToMimic.toInstance().resolveMember(
name, location, direction, resolveContext, inherited
);
final List<? extends RatedResolveResult> 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<? extends RatedResolveResult> resolveMember(@NotNull final String name,
@Nullable final PyExpression location,
@@ -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)) {
@@ -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);
@@ -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;
}
@@ -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])
@@ -220,6 +220,11 @@ public class Py3UnresolvedReferencesInspectionTest extends PyInspectionTestCase
doTest();
}
// PY-28177
public void testTypingOpaqueNameDunderGetItem() {
doTest();
}
// PY-21655
public void testUsageOfFunctionDecoratedWithAsyncioCoroutine() {
doMultiFileTest("a.py");