GuessManagerImpl: more precise slow-path selection on lambda parameters

Fixes IDEA-190133 Slow completion of lambda parameter
This commit is contained in:
Tagir Valeev
2018-04-16 11:16:59 +07:00
parent 7ebc4ff2eb
commit 571fbe9675
6 changed files with 70 additions and 20 deletions
@@ -343,32 +343,24 @@ public class GuessManagerImpl extends GuessManager {
if (expr.getType() instanceof PsiPrimitiveType) {
return Collections.emptyList();
}
List<PsiType> result = null;
PsiExpression place = PsiUtil.skipParenthesizedExprDown(expr);
if (place == null) return Collections.emptyList();
if (place instanceof PsiReferenceExpression) {
PsiElement target = ((PsiReferenceExpression)place).resolve();
if (target instanceof PsiParameter) {
PsiElement parent = target.getParent();
if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression) {
result = getTypesFromDfa(expr);
}
}
}
if (result == null) {
List<PsiType> result = null;
if (!ControlFlowAnalyzer.inlinerMayInferPreciseType(place)) {
GuessTypeVisitor visitor = new GuessTypeVisitor(place);
getTopmostBlock(place).accept(visitor);
if (visitor.isDfaNeeded()) {
result = getTypesFromDfa(expr);
}
else {
if (!visitor.isDfaNeeded()) {
result = visitor.mySpecificType == null ?
Collections.emptyList() : Collections.singletonList(tryGenerify(expr, visitor.mySpecificType));
}
}
if (result == null) {
result = getTypesFromDfa(expr);
}
if (result.equals(Collections.singletonList(expr.getType()))) {
result = Collections.emptyList();
return Collections.emptyList();
}
return result;
}
@@ -2029,6 +2029,14 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
return variable.getSource() instanceof Synthetic;
}
/**
* @param expression expression to test
* @return true if some inliner may add constraints on the precise type of given expression
*/
public static boolean inlinerMayInferPreciseType(PsiExpression expression) {
return Arrays.stream(INLINERS).anyMatch(inliner -> inliner.mayInferPreciseType(expression));
}
private static final class Synthetic implements DfaVariableSource {
private final int myLocation;
@@ -16,6 +16,7 @@
package com.intellij.codeInspection.dataFlow.inliner;
import com.intellij.codeInspection.dataFlow.CFGBuilder;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethodCallExpression;
import org.jetbrains.annotations.NotNull;
@@ -33,4 +34,12 @@ public interface CallInliner {
* If false is returned, inliner must not emit any instructions via builder.
*/
boolean tryInlineCall(@NotNull CFGBuilder builder, @NotNull PsiMethodCallExpression call);
/**
* @param expression expression to test
* @return true if this inliner may add constraints on the precise type of given expression
*/
default boolean mayInferPreciseType(@NotNull PsiExpression expression) {
return false;
}
}
@@ -0,0 +1,29 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.dataFlow.inliner;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.MethodCallUtils;
import java.util.function.Predicate;
import static com.intellij.util.ObjectUtils.tryCast;
class InlinerUtil {
static boolean isLambdaChainParameterReference(PsiExpression expression, Predicate<PsiType> chainTypePredicate) {
if(!(expression instanceof PsiReferenceExpression)) return false;
PsiParameter target = tryCast(((PsiReferenceExpression)expression).resolve(), PsiParameter.class);
if (target == null) return false;
if (!(target.getParent() instanceof PsiParameterList)) return false;
PsiLambdaExpression lambda = tryCast(target.getParent().getParent(), PsiLambdaExpression.class);
if (lambda == null) return false;
PsiExpressionList list = tryCast(PsiUtil.skipParenthesizedExprUp(lambda.getParent()), PsiExpressionList.class);
if (list == null) return false;
PsiMethodCallExpression call = tryCast(list.getParent(), PsiMethodCallExpression.class);
if (call == null) return false;
PsiMethodCallExpression qualifierCall = MethodCallUtils.getQualifierMethodCall(call);
if (qualifierCall == null) return false;
PsiType type = qualifierCall.getType();
return type != null && chainTypePredicate.test(type);
}
}
@@ -28,6 +28,7 @@ import com.intellij.util.ObjectUtils;
import com.siyeh.ig.callMatcher.CallMapper;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -265,4 +266,9 @@ public class OptionalChainInliner implements CallInliner {
builder.checkNotNull(argument, NullabilityProblemKind.passingNullableToNotNullParameter);
}
}
@Override
public boolean mayInferPreciseType(@NotNull PsiExpression expression) {
return InlinerUtil.isLambdaChainParameterReference(expression, TypeUtils::isOptional);
}
}
@@ -19,9 +19,9 @@ import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.callMatcher.CallMapper;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.MethodCallUtils;
@@ -33,6 +33,7 @@ import java.util.Objects;
import java.util.function.UnaryOperator;
import static com.intellij.psi.CommonClassNames.*;
import static com.intellij.util.ObjectUtils.tryCast;
import static com.siyeh.ig.callMatcher.CallMatcher.*;
public class StreamChainInliner implements CallInliner {
@@ -359,7 +360,7 @@ public class StreamChainInliner implements CallInliner {
super(call, next, null);
// Try to inline smoothly .flatMap(x -> stream().call().chain())
PsiLambdaExpression lambda =
ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(myCall.getArgumentList().getExpressions()[0]), PsiLambdaExpression.class);
tryCast(PsiUtil.skipParenthesizedExprDown(myCall.getArgumentList().getExpressions()[0]), PsiLambdaExpression.class);
Step chain = null;
PsiParameter parameter = null;
PsiExpression streamSource = null;
@@ -639,7 +640,7 @@ public class StreamChainInliner implements CallInliner {
static void buildStreamCFG(CFGBuilder builder, Step firstStep, PsiExpression originalQualifier) {
PsiType inType = StreamApiUtil.getStreamElementType(originalQualifier.getType());
PsiMethodCallExpression sourceCall = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(originalQualifier), PsiMethodCallExpression.class);
PsiMethodCallExpression sourceCall = tryCast(PsiUtil.skipParenthesizedExprDown(originalQualifier), PsiMethodCallExpression.class);
if(STREAM_GENERATE.test(sourceCall)) {
PsiExpression fn = sourceCall.getArgumentList().getExpressions()[0];
builder
@@ -765,7 +766,7 @@ public class StreamChainInliner implements CallInliner {
private static Step createTerminalFromCollector(PsiMethodCallExpression call) {
PsiMethodCallExpression collectorCall =
ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]), PsiMethodCallExpression.class);
tryCast(PsiUtil.skipParenthesizedExprDown(call.getArgumentList().getExpressions()[0]), PsiMethodCallExpression.class);
if (COUNTING_COLLECTOR.matches(collectorCall)) {
return new SumTerminalStep(call);
}
@@ -787,4 +788,9 @@ public class StreamChainInliner implements CallInliner {
}
return new UnknownTerminalStep(call);
}
@Override
public boolean mayInferPreciseType(@NotNull PsiExpression expression) {
return InlinerUtil.isLambdaChainParameterReference(expression, type -> InheritanceUtil.isInheritor(type, JAVA_UTIL_STREAM_STREAM));
}
}