mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-16412 Properly import TypeVars from other files for type hints
This commit is contained in:
committed by
Andrey Vlasovskikh
parent
af64e90b62
commit
294a18a2ac
@@ -349,6 +349,12 @@ public class PyTypeHintGenerationUtil {
|
||||
}
|
||||
collectImportTargetsFromType(callableType.getReturnType(context), context, symbols, typingTypes);
|
||||
}
|
||||
else if (type instanceof PyGenericType) {
|
||||
final PyTargetExpression target = as(type.getDeclarationElement(), PyTargetExpression.class);
|
||||
if (target != null) {
|
||||
symbols.add(target);
|
||||
}
|
||||
}
|
||||
if (type instanceof PyInstantiableType && ((PyInstantiableType)type).isDefinition()) {
|
||||
typingTypes.add("Type");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -721,8 +722,8 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
private static Ref<PyType> getType(@NotNull PyExpression expression, @NotNull Context context) {
|
||||
final List<PyType> members = Lists.newArrayList();
|
||||
boolean foundAny = false;
|
||||
for (PsiElement resolved : tryResolving(expression, context.getTypeContext())) {
|
||||
final Ref<PyType> typeRef = getTypeForResolvedElement(resolved, context);
|
||||
for (Pair<PyTargetExpression, PsiElement> pair : tryResolvingWithAliases(expression, context.getTypeContext())) {
|
||||
final Ref<PyType> typeRef = getTypeForResolvedElement(pair.getFirst(), pair.getSecond(), context);
|
||||
if (typeRef != null) {
|
||||
final PyType type = typeRef.get();
|
||||
if (type == null) {
|
||||
@@ -736,7 +737,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Ref<PyType> getTypeForResolvedElement(@NotNull PsiElement resolved, @NotNull Context context) {
|
||||
private static Ref<PyType> getTypeForResolvedElement(@Nullable PyTargetExpression alias,
|
||||
@NotNull PsiElement resolved,
|
||||
@NotNull Context context) {
|
||||
if (context.getExpressionCache().contains(resolved)) {
|
||||
// Recursive types are not yet supported
|
||||
return null;
|
||||
@@ -758,7 +761,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
final Ref<PyType> classObjType = getClassObjectType(resolved, context);
|
||||
if (classObjType != null) {
|
||||
return classObjType;
|
||||
return Ref.create(addTypeVarAlias(classObjType.get(), alias));
|
||||
}
|
||||
final PyType parameterizedType = getParameterizedType(resolved, context);
|
||||
if (parameterizedType != null) {
|
||||
@@ -770,7 +773,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
final PyType genericType = getGenericTypeFromTypeVar(resolved, context);
|
||||
if (genericType != null) {
|
||||
return Ref.create(genericType);
|
||||
return Ref.create(addTypeVarAlias(genericType, alias));
|
||||
}
|
||||
final PyType stringBasedType = getStringLiteralType(resolved, context);
|
||||
if (stringBasedType != null) {
|
||||
@@ -791,6 +794,15 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType addTypeVarAlias(@Nullable PyType type, @Nullable PyTargetExpression alias) {
|
||||
final PyGenericType typeVar = as(type, PyGenericType.class);
|
||||
if (typeVar != null) {
|
||||
return new PyGenericType(typeVar.getName(), typeVar.getBound(), typeVar.isDefinition(), alias);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Ref<PyType> getClassObjectType(@NotNull PsiElement resolved, @NotNull Context context) {
|
||||
if (resolved instanceof PySubscriptionExpression) {
|
||||
@@ -1074,7 +1086,13 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
|
||||
@NotNull
|
||||
private static List<PsiElement> tryResolving(@NotNull PyExpression expression, @NotNull TypeEvalContext context) {
|
||||
final List<PsiElement> elements = Lists.newArrayList();
|
||||
return ContainerUtil.map(tryResolvingWithAliases(expression, context), x -> x.getSecond());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Pair<PyTargetExpression, PsiElement>> tryResolvingWithAliases(@NotNull PyExpression expression,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final List<Pair<PyTargetExpression, PsiElement>> elements = Lists.newArrayList();
|
||||
if (expression instanceof PyReferenceExpression) {
|
||||
final List<PsiElement> results;
|
||||
if (context.maySwitchToAST(expression)) {
|
||||
@@ -1090,14 +1108,14 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
if (PyUtil.isInit(function)) {
|
||||
final PyClass cls = function.getContainingClass();
|
||||
if (cls != null) {
|
||||
elements.add(cls);
|
||||
elements.add(Pair.create(null, cls));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
final String name = element != null ? getQualifiedName(element) : null;
|
||||
if (name != null && OPAQUE_NAMES.contains(name)) {
|
||||
elements.add(element);
|
||||
elements.add(Pair.create(null, element));
|
||||
continue;
|
||||
}
|
||||
// Presumably, a TypeVar definition or a type alias
|
||||
@@ -1111,7 +1129,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
assignedValue = PyTypingAliasStubType.getAssignedValueStubLike(targetExpr);
|
||||
}
|
||||
if (assignedValue != null) {
|
||||
elements.add(assignedValue);
|
||||
elements.add(Pair.create(targetExpr, assignedValue));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1121,16 +1139,16 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
final QualifiedName osPathLikeQName = QualifiedName.fromComponents("os", PyNames.PATH_LIKE);
|
||||
final PsiElement osPathLike = PyResolveImportUtil.resolveTopLevelMember(osPathLikeQName, PyResolveImportUtil.fromFoothold(element));
|
||||
if (osPathLike != null) {
|
||||
elements.add(osPathLike);
|
||||
elements.add(Pair.create(null, osPathLike));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (element != null) {
|
||||
elements.add(element);
|
||||
elements.add(Pair.create(null, element));
|
||||
}
|
||||
}
|
||||
}
|
||||
return !elements.isEmpty() ? elements : Collections.singletonList(expression);
|
||||
return !elements.isEmpty() ? elements : Collections.singletonList(Pair.create(null, expression));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -123,13 +123,13 @@ public class PyGenericType implements PyType, PyInstantiableType<PyGenericType>
|
||||
@NotNull
|
||||
@Override
|
||||
public PyGenericType toInstance() {
|
||||
return myIsDefinition ? new PyGenericType(myName, myBound, false) : this;
|
||||
return myIsDefinition ? new PyGenericType(myName, myBound, false, myTargetExpression) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyGenericType toClass() {
|
||||
return myIsDefinition ? this : new PyGenericType(myName, myBound, true);
|
||||
return myIsDefinition ? this : new PyGenericType(myName, myBound, true, myTargetExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
from typing import TypeVar
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
target: T = 42
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
from lib import target
|
||||
|
||||
va<caret>r = target
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
from lib import target, T
|
||||
|
||||
var: [T] = target
|
||||
+4
@@ -243,6 +243,10 @@ public class PyAnnotateVariableTypeIntentionTest extends PyIntentionTestCase {
|
||||
doMultiFileAnnotationTest();
|
||||
}
|
||||
|
||||
public void testAnnotationTypeVarInOtherFile() {
|
||||
doMultiFileAnnotationTest();
|
||||
}
|
||||
|
||||
public void testAnnotationCollectionsNamedTupleInOtherFile() {
|
||||
doMultiFileAnnotationTest();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user