From d0f2bde3a87617874576bf7f0c8929067d52af31 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 29 Jan 2018 16:40:51 +0700 Subject: [PATCH] GuessManagerImpl: type inference fixes --- .../guess/impl/GuessManagerImpl.java | 34 +++++++++++++++---- .../normal/AssignmentParameterDfa.java | 8 +++++ .../normal/AssignmentParameterDfa_after.java | 8 +++++ .../normal/AssignmentTwicePreciseTypeDfa.java | 10 ++++++ .../AssignmentTwicePreciseTypeDfa_after.java | 10 ++++++ .../completion/NormalCompletionDfaTest.groovy | 2 ++ 6 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa_after.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 54caa8ff8488..314a28645c8c 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 @@ -137,6 +137,8 @@ public class GuessManagerImpl extends GuessManager { @Nullable private static MultiMap buildDataflowTypeMap(PsiExpression forPlace, boolean onlyForPlace) { + PsiType type = forPlace.getType(); + if (type == null) return null; PsiElement scope = DfaPsiUtil.getTopmostBlockInSameClass(forPlace); if (scope == null) { PsiFile file = forPlace.getContainingFile(); @@ -155,7 +157,8 @@ public class GuessManagerImpl extends GuessManager { } }; - final ExpressionTypeInstructionVisitor visitor = new ExpressionTypeInstructionVisitor(forPlace, onlyForPlace); + TypeConstraint initial = TypeConstraint.EMPTY.withInstanceofValue(runner.getFactory().createDfaType(type)); + final ExpressionTypeInstructionVisitor visitor = new ExpressionTypeInstructionVisitor(forPlace, onlyForPlace, initial); if (runner.analyzeMethodWithInlining(scope, visitor) == RunnerResult.OK) { return visitor.getResult(); } @@ -352,7 +355,7 @@ public class GuessManagerImpl extends GuessManager { GuessTypeVisitor visitor = new GuessTypeVisitor(place); getTopmostBlock(place).accept(visitor); - if (visitor.myNeedDfa) { + if (visitor.isDfaNeeded()) { return getTypesFromDfa(expr); } return visitor.mySpecificType == null ? Collections.emptyList() : Collections.singletonList(tryGenerify(expr, visitor.mySpecificType)); @@ -389,7 +392,8 @@ public class GuessManagerImpl extends GuessManager { static class GuessTypeVisitor extends JavaRecursiveElementWalkingVisitor { private final @NotNull PsiExpression myPlace; PsiType mySpecificType; - boolean myNeedDfa; + private boolean myNeedDfa; + private boolean myDeclared; private int myStart; GuessTypeVisitor(@NotNull PsiExpression place) { @@ -400,10 +404,12 @@ public class GuessManagerImpl extends GuessManager { private void handleAssignment(@Nullable PsiExpression expression) { if (expression == null) return; PsiType type = expression.getType(); + PsiType rawType = type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type; + if (rawType == null) return; if (mySpecificType == null) { - mySpecificType = type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type; + mySpecificType = rawType; } - else if (!mySpecificType.equals(type)) { + else if (!mySpecificType.equals(rawType)) { myNeedDfa = true; stopWalking(); } @@ -420,6 +426,7 @@ public class GuessManagerImpl extends GuessManager { @Override public void visitLocalVariable(PsiLocalVariable variable) { if (ExpressionUtils.isReferenceTo(myPlace, variable)) { + myDeclared = true; handleAssignment(variable.getInitializer()); } super.visitLocalVariable(variable); @@ -450,16 +457,28 @@ public class GuessManagerImpl extends GuessManager { } super.visitElement(element); } + + public boolean isDfaNeeded() { + if (myNeedDfa) return true; + if (myDeclared || mySpecificType == null) return true; + PsiType type = myPlace.getType(); + PsiType rawType = type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type; + return !mySpecificType.equals(rawType); + } } private static class ExpressionTypeInstructionVisitor extends StandardInstructionVisitor { + private final TypeConstraint myInitial; private MultiMap myResult; private final PsiElement myForPlace; private TypeConstraint myConstraint = null; private final boolean myOnlyForPlace; - private ExpressionTypeInstructionVisitor(@NotNull PsiElement forPlace, boolean onlyForPlace) { + private ExpressionTypeInstructionVisitor(@NotNull PsiElement forPlace, + boolean onlyForPlace, + TypeConstraint initial) { myOnlyForPlace = onlyForPlace; myForPlace = PsiUtil.skipParenthesizedExprUp(forPlace); + myInitial = initial; } MultiMap getResult() { @@ -544,6 +563,9 @@ public class GuessManagerImpl extends GuessManager { DfaMemoryState memoryState = state.getMemoryState(); if (myConstraint == TypeConstraint.EMPTY) return; TypeConstraint constraint = memoryState.getValueFact(memoryState.peek(), DfaFactType.TYPE_CONSTRAINT); + if (constraint == null) { + constraint = myInitial; + } if (constraint != null) { myConstraint = myConstraint == null ? constraint : myConstraint.union(constraint); if (myConstraint == null) { diff --git a/java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa.java b/java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa.java new file mode 100644 index 000000000000..092f48635c9c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa.java @@ -0,0 +1,8 @@ +class Foo { + void test(List list, boolean b) { + if (b) { + list = new ArrayList<>(); + } + System.out.println(list.trimTo); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa_after.java b/java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa_after.java new file mode 100644 index 000000000000..f6f96c96b4c7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AssignmentParameterDfa_after.java @@ -0,0 +1,8 @@ +class Foo { + void test(List list, boolean b) { + if (b) { + list = new ArrayList<>(); + } + System.out.println(list.trimTo); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa.java b/java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa.java new file mode 100644 index 000000000000..93ff9cd57377 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa.java @@ -0,0 +1,10 @@ +class Foo { + void test(boolean b) { + List list = new ArrayList<>(); + list.add("foo"); + if (b) { + list = new ArrayList<>(); + } + System.out.println(list.trimTo); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa_after.java b/java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa_after.java new file mode 100644 index 000000000000..9b52ea62a119 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AssignmentTwicePreciseTypeDfa_after.java @@ -0,0 +1,10 @@ +class Foo { + void test(boolean b) { + List list = new ArrayList<>(); + list.add("foo"); + if (b) { + list = new ArrayList<>(); + } + System.out.println(((ArrayList) list).trimToSize();); + } +} 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 847f9c596f58..e3f2d41469b1 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,8 @@ class NormalCompletionDfaTest extends NormalCompletionTestCase { void testCastQualifierForPrivateFieldReference() { doTest() } void testOrAssignmentDfa() { doTest() } void testAssignmentPreciseTypeDfa() { doTest() } + void testAssignmentTwicePreciseTypeDfa() { doTest() } + void testAssignmentParameterDfa() { doTest() } void testAssignmentNoPreciseTypeDfa() { doTest() } void testDeclarationPreciseTypeDfa() { doTest() } void testInstanceOfAssignmentDfa() { doTest() }