From e23ca69828db72b82a1ccf4252cbecc6d9ec5e41 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 22 Jan 2019 20:14:29 +0300 Subject: [PATCH] [groovy] dfa: join mixins with least upper bound (IDEA-205853) --- .../groovy/lang/psi/dataFlow/DFAType.java | 23 +++++++++++++------ .../lang/controlFlow/ControlFlowTest.groovy | 2 ++ .../lang/resolve/TypeInferenceTest.groovy | 13 +++++++++++ .../groovy/controlFlow/ifInstanceofOr.test | 22 ++++++++++++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 plugins/groovy/testdata/groovy/controlFlow/ifInstanceofOr.test diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java index acea0c9b38b2..7117949876fe 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/DFAType.java @@ -2,6 +2,7 @@ package org.jetbrains.plugins.groovy.lang.psi.dataFlow; import com.intellij.openapi.util.Comparing; +import com.intellij.psi.GenericsUtil; import com.intellij.psi.PsiIntersectionType; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiType; @@ -15,6 +16,8 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt import java.util.*; +import static java.util.stream.Collectors.toList; + /** * @author Max Medvedev */ @@ -157,17 +160,23 @@ public class DFAType { final PsiType primary = TypesUtil.getLeastUpperBoundNullable(t1.primary, t2.primary, manager); final DFAType type = new DFAType(primary); - - for (Mixin mixin1 : t1.mixins) { - for (Mixin mixin2 : t2.mixins) { - if (mixin1.equals(mixin2) && mixin1.myNegated == mixin2.myNegated) { - type.mixins.add(mixin1); - } - } + final PsiType type1 = reduce(t1.mixins); + final PsiType type2 = reduce(t2.mixins); + if (type1 != null && type2 != null) { + type.addMixin(GenericsUtil.getLeastUpperBound(type1, type2, manager), null); } + return type; } + private static PsiType reduce(List mixins) { + List types = mixins.stream() + .filter(it -> !it.myNegated) + .map(it -> it.myType) + .collect(toList()); + return types.isEmpty() ? null : PsiIntersectionType.createIntersection(types); + } + @Override public String toString() { return "{" + primary + " : " + mixins + "}"; diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.groovy index c3e5c158dcc7..f009ddac046e 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.groovy @@ -104,6 +104,8 @@ class ControlFlowTest extends LightCodeInsightFixtureTestCase { void testIfNegatedInstanceofElse() { doTest() } + void testIfInstanceofOr() { doTest() } + void testReturnMapFromClosure() { doTest() } void testSwitchInTryWithThrows() { doTest() } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy index 9ade83fd6c9a..493f088a61c7 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy @@ -558,6 +558,19 @@ def foo(bar) { }''', 'java.lang.Runnable') } + void 'test instanceof or instanceof'() { + doTest '''\ +class A {} +class B extends A {} +class C extends A {} +def foo(a) { + if (a instanceof B || a instanceof C) { + a + } +} +''', 'A' + } + void 'test enum constant'() { doTest('''\ import static MyEnum.* diff --git a/plugins/groovy/testdata/groovy/controlFlow/ifInstanceofOr.test b/plugins/groovy/testdata/groovy/controlFlow/ifInstanceofOr.test new file mode 100644 index 000000000000..4e57896f3be7 --- /dev/null +++ b/plugins/groovy/testdata/groovy/controlFlow/ifInstanceofOr.test @@ -0,0 +1,22 @@ +if (a instanceof Integer || a instanceof Double) { + a +} +----- +0(1) element: null +1(2) element: IF statement +2(3) Condition Logical expression +3(4) READ a +4(5,7) Condition Instanceof expression, dependent: 2 +5(6) instanceof: a instanceof Integer +6(9) Negating goto instruction, condition=4Instanceof expression +7(8) instanceof: a instanceof Integer +8(14) element: Logical expression +9(10) READ a +10(11,13) Condition Instanceof expression +11(12) instanceof: a instanceof Double +12(16) Negating goto instruction, condition=10Instanceof expression +13(14) instanceof: a instanceof Double +14(15) READ a +15(16) element: Reference expression MAYBE_RETURN +16(17) End element: IF statement +17() element: null \ No newline at end of file