ExpectedTypesProvider: allow to finish when at most specified number of candidates are found

Fixes IDEA-207951 Ide freeze on showing parameter info
This commit is contained in:
Tagir Valeev
2019-03-01 12:55:58 +07:00
parent 7837be0845
commit 2db44a1e7f
2 changed files with 47 additions and 21 deletions
@@ -104,6 +104,15 @@ public class ExpectedTypesProvider {
return new ExpectedTypeInfoImpl(type, kind, defaultType, tailType, calledMethod, expectedName);
}
@Nullable
public static ExpectedTypeInfo getSingleExpectedTypeForCompletion(@Nullable PsiExpression expr) {
ExpectedTypeInfo[] expectedTypes = getExpectedTypes(expr, true, ourGlobalScopeClassProvider, false, false, 1);
if (expectedTypes.length > 0) {
return expectedTypes[0];
}
return null;
}
@NotNull
public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion) {
return getExpectedTypes(expr, forCompletion, false, false);
@@ -124,6 +133,15 @@ public class ExpectedTypesProvider {
@NotNull
public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr, boolean forCompletion, ExpectedClassProvider classProvider,
final boolean voidable, boolean usedAfter) {
return getExpectedTypes(expr, forCompletion, classProvider, voidable, usedAfter, Integer.MAX_VALUE);
}
@NotNull
public static ExpectedTypeInfo[] getExpectedTypes(@Nullable PsiExpression expr,
boolean forCompletion,
ExpectedClassProvider classProvider,
boolean voidable,
boolean usedAfter, int maxCandidates) {
if (expr == null) return ExpectedTypeInfo.EMPTY_ARRAY;
PsiElement parent = expr.getParent();
PsiFunctionalExpression functionalExpression = extractFunctionalExpression(expr);
@@ -141,7 +159,7 @@ public class ExpectedTypesProvider {
return result;
}
}
MyParentVisitor visitor = new MyParentVisitor(expr, forCompletion, classProvider, voidable, usedAfter);
MyParentVisitor visitor = new MyParentVisitor(expr, forCompletion, classProvider, voidable, usedAfter, maxCandidates);
if (parent != null) {
parent.accept(visitor);
}
@@ -249,6 +267,7 @@ public class ExpectedTypesProvider {
private PsiExpression myExpr;
private final boolean myForCompletion;
private final boolean myUsedAfter;
private final int myMaxCandidates;
private final ExpectedClassProvider myClassProvider;
private final boolean myVoidable;
final List<ExpectedTypeInfo> myResult = ContainerUtil.newArrayList();
@@ -258,16 +277,21 @@ public class ExpectedTypesProvider {
boolean forCompletion,
ExpectedClassProvider classProvider,
boolean voidable,
boolean usedAfter) {
boolean usedAfter,
int maxCandidates) {
myExpr = expr;
myForCompletion = forCompletion;
myClassProvider = classProvider;
myVoidable = voidable;
myUsedAfter = usedAfter;
myMaxCandidates = maxCandidates;
}
@NotNull
public ExpectedTypeInfo[] getResult() {
if (myResult.size() > myMaxCandidates) {
return myResult.subList(0, myMaxCandidates).toArray(ExpectedTypeInfo.EMPTY_ARRAY);
}
return myResult.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
}
@@ -275,7 +299,8 @@ public class ExpectedTypesProvider {
public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
PsiElement parent = expression.getParent();
if (parent != null) {
final MyParentVisitor visitor = new MyParentVisitor(expression, myForCompletion, myClassProvider, myVoidable, myUsedAfter);
final MyParentVisitor visitor = new MyParentVisitor(expression, myForCompletion, myClassProvider, myVoidable, myUsedAfter,
myMaxCandidates);
parent.accept(visitor);
for (final ExpectedTypeInfo info : visitor.myResult) {
myResult.add(createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailTypes.RPARENTH, info.getCalledMethod(),
@@ -297,7 +322,7 @@ public class ExpectedTypesProvider {
@Override
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
if (myForCompletion) {
final MyParentVisitor visitor = new MyParentVisitor(expression, true, myClassProvider, myVoidable, myUsedAfter);
final MyParentVisitor visitor = new MyParentVisitor(expression, true, myClassProvider, myVoidable, myUsedAfter, myMaxCandidates);
expression.getParent().accept(visitor);
myResult.addAll(visitor.myResult);
return;
@@ -647,8 +672,9 @@ public class ExpectedTypesProvider {
if (PsiDiamondType.hasDiamond(newExpr)) {
final JavaResolveResult[] candidates = PsiDiamondTypeImpl.collectStaticFactories(newExpr, DuplicateConflictResolver.INSTANCE);
if (candidates != null) {
final PsiExpressionList argumentList = newExpr.getArgumentList();
Collections.addAll(myResult, getExpectedArgumentTypesForMethodCall(ContainerUtil.map(candidates, candidate -> (CandidateInfo)candidate, CandidateInfo.EMPTY_ARRAY), argumentList, myExpr, myForCompletion,
final PsiExpressionList argumentList = Objects.requireNonNull(newExpr.getArgumentList());
CandidateInfo[] converted = ContainerUtil.map(candidates, candidate -> (CandidateInfo)candidate, CandidateInfo.EMPTY_ARRAY);
Collections.addAll(myResult, getExpectedArgumentTypesForMethodCall(converted, argumentList, myExpr, myForCompletion,
newExpr.resolveMethod()));
}
return;
@@ -698,7 +724,7 @@ public class ExpectedTypesProvider {
if (op == JavaTokenType.EQEQ || op == JavaTokenType.NE) {
ContainerUtil.addIfNotNull(myResult, getEqualsType(anotherExpr));
}
final MyParentVisitor visitor = new MyParentVisitor(expr, true, myClassProvider, myVoidable, myUsedAfter);
final MyParentVisitor visitor = new MyParentVisitor(expr, true, myClassProvider, myVoidable, myUsedAfter, myMaxCandidates);
myExpr = (PsiExpression)myExpr.getParent();
expr.getParent().accept(visitor);
myResult.addAll(visitor.myResult);
@@ -898,7 +924,7 @@ public class ExpectedTypesProvider {
}
PsiElement parent = expr.getParent();
MyParentVisitor visitor = new MyParentVisitor(expr, false, myClassProvider, myVoidable, myUsedAfter);
MyParentVisitor visitor = new MyParentVisitor(expr, false, myClassProvider, myVoidable, myUsedAfter, myMaxCandidates);
myExpr = (PsiExpression)myExpr.getParent();
parent.accept(visitor);
ExpectedTypeInfo[] componentTypeInfo = visitor.getResult();
@@ -926,7 +952,7 @@ public class ExpectedTypesProvider {
myResult.add(createInfoImpl(PsiType.BOOLEAN, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.BOOLEAN, TailType.NONE));
}
else if (myExpr.equals(expr.getThenExpression())) {
ExpectedTypeInfo[] types = getExpectedTypes(expr, myForCompletion);
ExpectedTypeInfo[] types = getExpectedTypes(expr, myForCompletion, ourGlobalScopeClassProvider, false, false, myMaxCandidates);
for (int i = 0; i < types.length; i++) {
final ExpectedTypeInfo info = types[i];
types[i] = createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailType.COND_EXPR_COLON, info.getCalledMethod(),
@@ -938,7 +964,7 @@ public class ExpectedTypesProvider {
if (!myExpr.equals(expr.getElseExpression())) {
LOG.error(Arrays.asList(expr.getChildren()) + "; " + myExpr);
}
Collections.addAll(myResult, getExpectedTypes(expr, myForCompletion));
Collections.addAll(myResult, getExpectedTypes(expr, myForCompletion, ourGlobalScopeClassProvider, false, false, myMaxCandidates));
}
}
@@ -1033,7 +1059,7 @@ public class ExpectedTypesProvider {
ParameterTypeInferencePolicy policy = forCompletion ? CompletionParameterTypeInferencePolicy.INSTANCE : DefaultParameterTypeInferencePolicy.INSTANCE;
Set<ExpectedTypeInfo> array = new LinkedHashSet<>();
Set<ExpectedTypeInfo> set = new LinkedHashSet<>();
for (CandidateInfo candidateInfo : methodCandidates) {
PsiMethod method = (PsiMethod)candidateInfo.getElement();
PsiSubstitutor substitutor;
@@ -1051,7 +1077,8 @@ public class ExpectedTypesProvider {
if (substitutor == null) {
return ExpectedTypeInfo.EMPTY_ARRAY;
}
inferMethodCallArgumentTypes(argument, forCompletion, args, index, method, substitutor, array);
inferMethodCallArgumentTypes(argument, forCompletion, args, index, method, substitutor, set);
if (set.size() >= myMaxCandidates) break;
if (leftArgs != null && candidateInfo instanceof MethodCandidateInfo) {
Computable<PsiSubstitutor> computable = () -> ((MethodCandidateInfo)candidateInfo).inferSubstitutorFromArgs(policy, leftArgs);
@@ -1059,13 +1086,14 @@ public class ExpectedTypesProvider {
? computable.compute()
: MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(argumentList, false, computable);
if (substitutor != null) {
inferMethodCallArgumentTypes(argument, forCompletion, leftArgs, index, method, substitutor, array);
inferMethodCallArgumentTypes(argument, forCompletion, leftArgs, index, method, substitutor, set);
if (set.size() >= myMaxCandidates) break;
}
}
}
// try to find some variants without considering previous argument PRIMITIVE_TYPES
if (forCompletion && array.isEmpty()) {
if (forCompletion && set.isEmpty()) {
for (CandidateInfo candidate : methodCandidates) {
PsiMethod method = (PsiMethod)candidate.getElement();
PsiSubstitutor substitutor = candidate.getSubstitutor();
@@ -1076,11 +1104,12 @@ public class ExpectedTypesProvider {
TailType tailType = getMethodArgumentTailType(argument, index, method, substitutor, params);
ExpectedTypeInfoImpl info = createInfoImpl(paramType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, paramType,
tailType, method, getPropertyName(param));
array.add(info);
set.add(info);
if (set.size() >= myMaxCandidates) break;
}
}
return array.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
return set.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
}
@NotNull
@@ -31,11 +31,8 @@ public class CompletionParameterTypeInferencePolicy extends ProcessCandidatePara
@Override
public PsiType getDefaultExpectedType(PsiCallExpression methodCall) {
ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(methodCall, true);
if (expectedTypes.length > 0) {
return expectedTypes[0].getType();
}
return PsiType.NULL;
ExpectedTypeInfo expectedType = ExpectedTypesProvider.getSingleExpectedTypeForCompletion(methodCall);
return expectedType == null ? PsiType.NULL : expectedType.getType();
}
@Override