[groovy] dfa: join mixins with least upper bound (IDEA-205853)

This commit is contained in:
Daniil Ovchinnikov
2019-01-23 12:57:50 +03:00
parent e28873298c
commit e23ca69828
4 changed files with 53 additions and 7 deletions
@@ -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<Mixin> mixins) {
List<PsiType> 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 + "}";
@@ -104,6 +104,8 @@ class ControlFlowTest extends LightCodeInsightFixtureTestCase {
void testIfNegatedInstanceofElse() { doTest() }
void testIfInstanceofOr() { doTest() }
void testReturnMapFromClosure() { doTest() }
void testSwitchInTryWithThrows() { doTest() }
@@ -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) {
<caret>a
}
}
''', 'A'
}
void 'test enum constant'() {
doTest('''\
import static MyEnum.*
@@ -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