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 daf3c74db933..54caa8ff8488 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 @@ -162,75 +162,6 @@ public class GuessManagerImpl extends GuessManager { return null; } - private static boolean mayHaveMorePreciseType(PsiExpression expr) { - PsiExpression place = PsiUtil.skipParenthesizedExprDown(expr); - 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) { - return true; - } - } - } - if (place == null) return false; - PsiType type = place.getType(); - if (type == null) return false; - final int start = place.getTextRange().getStartOffset(); - class Visitor extends JavaRecursiveElementWalkingVisitor { - public boolean hasInteresting; - - @Override - public void visitAssignmentExpression(PsiAssignmentExpression expression) { - if (ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY.equals(expression.getLExpression(), place) && - expression.getRExpression() != null && !type.equals(expression.getRExpression().getType())) { - hasInteresting = true; - stopWalking(); - } - super.visitAssignmentExpression(expression); - } - - @Override - public void visitLocalVariable(PsiLocalVariable variable) { - if (variable.getInitializer() != null && ExpressionUtils.isReferenceTo(place, variable) && - !type.equals(variable.getInitializer().getType())) { - hasInteresting = true; - stopWalking(); - } - super.visitLocalVariable(variable); - } - - @Override - public void visitTypeCastExpression(PsiTypeCastExpression expression) { - if (ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY.equals(expression.getOperand(), place)) { - hasInteresting = true; - stopWalking(); - } - super.visitTypeCastExpression(expression); - } - - @Override - public void visitInstanceOfExpression(PsiInstanceOfExpression expression) { - if (ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY.equals(expression.getOperand(), place)) { - hasInteresting = true; - stopWalking(); - } - super.visitInstanceOfExpression(expression); - } - - @Override - public void visitElement(PsiElement element) { - if (element.getTextRange().getStartOffset() > start) { - stopWalking(); - } - super.visitElement(element); - } - } - Visitor visitor = new Visitor(); - getTopmostBlock(place).accept(visitor); - return visitor.hasInteresting; - } - private static PsiElement getTopmostBlock(PsiElement scope) { assert scope.isValid(); PsiElement lastScope = scope; @@ -407,10 +338,28 @@ public class GuessManagerImpl extends GuessManager { @NotNull @Override public List getControlFlowExpressionTypeConjuncts(@NotNull PsiExpression expr) { - if (!mayHaveMorePreciseType(expr)) { - return Collections.emptyList(); //optimization + PsiExpression place = PsiUtil.skipParenthesizedExprDown(expr); + 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) { + return getTypesFromDfa(expr); + } + } } + if (place == null) return Collections.emptyList(); + GuessTypeVisitor visitor = new GuessTypeVisitor(place); + getTopmostBlock(place).accept(visitor); + if (visitor.myNeedDfa) { + return getTypesFromDfa(expr); + } + return visitor.mySpecificType == null ? Collections.emptyList() : Collections.singletonList(tryGenerify(expr, visitor.mySpecificType)); + } + + @NotNull + private static List getTypesFromDfa(@NotNull PsiExpression expr) { MultiMap fromDfa = buildDataflowTypeMap(expr, true); if (fromDfa != null) { Collection conjuncts = fromDfa.get(expr); @@ -419,7 +368,6 @@ public class GuessManagerImpl extends GuessManager { return ContainerUtil.mapNotNull(flatTypes, type -> tryGenerify(expr, type)); } } - return Collections.emptyList(); } @@ -438,6 +386,71 @@ public class GuessManagerImpl extends GuessManager { return GenericsUtil.getExpectedGenericType(expression, psiClass, (PsiClassType)expressionType); } + static class GuessTypeVisitor extends JavaRecursiveElementWalkingVisitor { + private final @NotNull PsiExpression myPlace; + PsiType mySpecificType; + boolean myNeedDfa; + private int myStart; + + GuessTypeVisitor(@NotNull PsiExpression place) { + myPlace = place; + myStart = place.getTextRange().getStartOffset(); + } + + private void handleAssignment(@Nullable PsiExpression expression) { + if (expression == null) return; + PsiType type = expression.getType(); + if (mySpecificType == null) { + mySpecificType = type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type; + } + else if (!mySpecificType.equals(type)) { + myNeedDfa = true; + stopWalking(); + } + } + + @Override + public void visitAssignmentExpression(PsiAssignmentExpression expression) { + if (ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY.equals(expression.getLExpression(), myPlace)) { + handleAssignment(expression.getRExpression()); + } + super.visitAssignmentExpression(expression); + } + + @Override + public void visitLocalVariable(PsiLocalVariable variable) { + if (ExpressionUtils.isReferenceTo(myPlace, variable)) { + handleAssignment(variable.getInitializer()); + } + super.visitLocalVariable(variable); + } + + @Override + public void visitTypeCastExpression(PsiTypeCastExpression expression) { + if (ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY.equals(expression.getOperand(), myPlace)) { + myNeedDfa = true; + stopWalking(); + } + super.visitTypeCastExpression(expression); + } + + @Override + public void visitInstanceOfExpression(PsiInstanceOfExpression expression) { + if (ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY.equals(expression.getOperand(), myPlace)) { + myNeedDfa = true; + stopWalking(); + } + super.visitInstanceOfExpression(expression); + } + + @Override + public void visitElement(PsiElement element) { + if (element.getTextRange().getStartOffset() > myStart) { + stopWalking(); + } + super.visitElement(element); + } + } private static class ExpressionTypeInstructionVisitor extends StandardInstructionVisitor { private MultiMap myResult; private final PsiElement myForPlace; diff --git a/java/java-tests/testData/codeInsight/completion/normal/AssignmentNoPreciseTypeDfa.java b/java/java-tests/testData/codeInsight/completion/normal/AssignmentNoPreciseTypeDfa.java new file mode 100644 index 000000000000..0799ada24cda --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AssignmentNoPreciseTypeDfa.java @@ -0,0 +1,9 @@ +class Foo { + void test(boolean b) { + List list = new ArrayList<>(); + if (b) { + list = Collections.emptyList(); + } + System.out.println(list.trimTo); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/AssignmentNoPreciseTypeDfa_after.java b/java/java-tests/testData/codeInsight/completion/normal/AssignmentNoPreciseTypeDfa_after.java new file mode 100644 index 000000000000..33c105f88e6c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AssignmentNoPreciseTypeDfa_after.java @@ -0,0 +1,9 @@ +class Foo { + void test(boolean b) { + List list = new ArrayList<>(); + if (b) { + list = Collections.emptyList(); + } + System.out.println(list.trimTo); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionDfaTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionDfaTest.groovy index c33447b0d7c3..847f9c596f58 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionDfaTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionDfaTest.groovy @@ -44,6 +44,7 @@ class NormalCompletionDfaTest extends NormalCompletionTestCase { void testCastQualifierForPrivateFieldReference() { doTest() } void testOrAssignmentDfa() { doTest() } void testAssignmentPreciseTypeDfa() { doTest() } + void testAssignmentNoPreciseTypeDfa() { doTest() } void testDeclarationPreciseTypeDfa() { doTest() } void testInstanceOfAssignmentDfa() { doTest() } void testStreamDfa() { doTest() }