From 571fbe96754e6c762fe8f7f8532ae2665bfbae61 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 16 Apr 2018 11:08:05 +0700 Subject: [PATCH] GuessManagerImpl: more precise slow-path selection on lambda parameters Fixes IDEA-190133 Slow completion of lambda parameter --- .../guess/impl/GuessManagerImpl.java | 24 +++++---------- .../dataFlow/ControlFlowAnalyzer.java | 8 +++++ .../dataFlow/inliner/CallInliner.java | 9 ++++++ .../dataFlow/inliner/InlinerUtil.java | 29 +++++++++++++++++++ .../inliner/OptionalChainInliner.java | 6 ++++ .../dataFlow/inliner/StreamChainInliner.java | 14 ++++++--- 6 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/InlinerUtil.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java index 4216a763b32e..a796d71c54d5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java @@ -343,32 +343,24 @@ public class GuessManagerImpl extends GuessManager { if (expr.getType() instanceof PsiPrimitiveType) { return Collections.emptyList(); } - List 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 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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index b658607ea96f..af5f6d9c683f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/CallInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/CallInliner.java index d31ff96315b0..f9043a8c2fc7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/CallInliner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/CallInliner.java @@ -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; + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/InlinerUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/InlinerUtil.java new file mode 100644 index 000000000000..20899a51419e --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/InlinerUtil.java @@ -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 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); + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java index 18d6dc58e2a6..7063ebd9723b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/OptionalChainInliner.java @@ -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); + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java index 17dbfe6b54a3..4ae1e2780032 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inliner/StreamChainInliner.java @@ -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)); + } }