IDEA-86242 Code completion do not suggest members of known common supertype

This commit is contained in:
Tagir Valeev
2018-03-28 10:30:16 +07:00
parent 00d01b6ab8
commit c7f7093a96
8 changed files with 78 additions and 44 deletions

View File

@@ -169,9 +169,9 @@ public final class TypeConstraint {
if (that.myNotInstanceofValues.containsAll(myNotInstanceofValues) && that.myInstanceofValues.containsAll(myInstanceofValues)) {
return true;
}
if (this.myNotInstanceofValues.isEmpty() && that.myNotInstanceofValues.isEmpty() && this.myInstanceofValues.size() == 1) {
DfaPsiType type = this.myInstanceofValues.iterator().next();
return that.myInstanceofValues.stream().allMatch(type::isAssignableFrom);
if (this.myNotInstanceofValues.isEmpty() && that.myNotInstanceofValues.isEmpty()) {
return that.myInstanceofValues.stream().allMatch(
thatType -> this.myInstanceofValues.stream().allMatch(thisType -> thisType.isAssignableFrom(thatType)));
}
return false;
}
@@ -180,29 +180,25 @@ public final class TypeConstraint {
public TypeConstraint union(@NotNull TypeConstraint other) {
if(isSuperStateOf(other)) return this;
if(other.isSuperStateOf(this)) return other;
Set<DfaPsiType> leftTypes = new HashSet<>(this.myInstanceofValues);
Set<DfaPsiType> leftNotTypes = new HashSet<>(this.myNotInstanceofValues);
Set<DfaPsiType> rightTypes = new HashSet<>(other.myInstanceofValues);
Set<DfaPsiType> rightNotTypes = new HashSet<>(other.myNotInstanceofValues);
filter(leftTypes, rightTypes, rightNotTypes);
filter(rightTypes, leftTypes, leftNotTypes);
TypeConstraint left = create(leftTypes, leftNotTypes);
TypeConstraint right = create(rightTypes, rightNotTypes);
if(left.isSuperStateOf(right)) return left;
if(right.isSuperStateOf(left)) return right;
return null;
Set<DfaPsiType> notTypes = new HashSet<>(this.myNotInstanceofValues);
notTypes.retainAll(other.myNotInstanceofValues);
Set<DfaPsiType> instanceOfTypes;
if (this.myInstanceofValues.containsAll(other.myInstanceofValues)) {
instanceOfTypes = other.myInstanceofValues;
} else if (other.myInstanceofValues.containsAll(this.myInstanceofValues)) {
instanceOfTypes = this.myInstanceofValues;
} else {
instanceOfTypes = withSuper(this.myInstanceofValues);
instanceOfTypes.retainAll(withSuper(other.myInstanceofValues));
}
TypeConstraint constraint = StreamEx.of(instanceOfTypes).foldLeft(EMPTY, TypeConstraint::withInstanceofValue);
return StreamEx.of(notTypes).foldLeft(constraint, TypeConstraint::withNotInstanceofValue);
}
private static void filter(Set<DfaPsiType> leftTypes, Set<DfaPsiType> rightTypes, Set<DfaPsiType> rightNotTypes) {
Set<DfaPsiType> addTypes = new HashSet<>();
for (Iterator<DfaPsiType> iterator = leftTypes.iterator(); iterator.hasNext(); ) {
DfaPsiType type = iterator.next();
if(rightNotTypes.remove(type)) {
iterator.remove();
StreamEx.of(rightTypes).filter(t -> t.isAssignableFrom(type)).into(addTypes);
}
}
leftTypes.addAll(addTypes);
private static Set<DfaPsiType> withSuper(Set<DfaPsiType> instanceofValues) {
return StreamEx.of(instanceofValues)
.flatMap(type -> StreamEx.of(type.getPsiType().getSuperTypes()).map(type.getFactory()::createDfaType).append(type))
.toSet();
}
@NotNull

View File

@@ -20,22 +20,18 @@ import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* @author peter
*/
public class DfaPsiType {
private final PsiType myPsiType;
private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myAssignableCache;
private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache;
private final DfaValueFactory myFactory;
private final int myID;
DfaPsiType(int id, @NotNull PsiType psiType, Map<Pair<DfaPsiType, DfaPsiType>, Boolean> assignableCache, Map<Pair<DfaPsiType, DfaPsiType>, Boolean> convertibleCache) {
DfaPsiType(int id, @NotNull PsiType psiType, DfaValueFactory factory) {
myID = id;
myPsiType = psiType;
myAssignableCache = assignableCache;
myConvertibleCache = convertibleCache;
myFactory = factory;
}
@NotNull
@@ -46,21 +42,17 @@ public class DfaPsiType {
public boolean isAssignableFrom(DfaPsiType other) {
if (other == this) return true;
Pair<DfaPsiType, DfaPsiType> key = Pair.create(this, other);
Boolean result = myAssignableCache.get(key);
if (result == null) {
myAssignableCache.put(key, result = myPsiType.isAssignableFrom(other.myPsiType));
}
return result;
return myFactory.myAssignableCache.computeIfAbsent(key, k -> myPsiType.isAssignableFrom(other.myPsiType));
}
public boolean isConvertibleFrom(DfaPsiType other) {
if (other == this) return true;
Pair<DfaPsiType, DfaPsiType> key = Pair.create(this, other);
Boolean result = myConvertibleCache.get(key);
if (result == null) {
myConvertibleCache.put(key, result = myPsiType.isConvertibleFrom(other.myPsiType));
}
return result;
return myFactory.myConvertibleCache.computeIfAbsent(key, k -> myPsiType.isConvertibleFrom(other.myPsiType));
}
public DfaValueFactory getFactory() {
return myFactory;
}
@Override

View File

@@ -29,8 +29,8 @@ import static com.intellij.patterns.StandardPatterns.or;
public class DfaValueFactory {
private final List<DfaValue> myValues = ContainerUtil.newArrayList();
private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myAssignableCache = ContainerUtil.newHashMap();
private final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache = ContainerUtil.newHashMap();
final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myAssignableCache = ContainerUtil.newHashMap();
final Map<Pair<DfaPsiType, DfaPsiType>, Boolean> myConvertibleCache = ContainerUtil.newHashMap();
private final Map<PsiType, DfaPsiType> myDfaTypes = ContainerUtil.newHashMap();
private final boolean myUnknownMembersAreNullable;
private final FieldChecker myFieldChecker;
@@ -98,7 +98,7 @@ public class DfaValueFactory {
psiType = DfaPsiType.normalizeType(psiType);
DfaPsiType dfaType = myDfaTypes.get(psiType);
if (dfaType == null) {
myDfaTypes.put(psiType, dfaType = new DfaPsiType(myDfaTypes.size() + 1, psiType, myAssignableCache, myConvertibleCache));
myDfaTypes.put(psiType, dfaType = new DfaPsiType(myDfaTypes.size() + 1, psiType, this));
}
return dfaType;
}

View File

@@ -0,0 +1,11 @@
import java.math.BigInteger;
import java.util.function.Function;
class Test {
long test(Object obj) {
if (obj instanceof Integer || obj instanceof Long) {
obj.long<caret>
}
return -1;
}
}

View File

@@ -0,0 +1,11 @@
import java.math.BigInteger;
import java.util.function.Function;
class Test {
long test(Object obj) {
if (obj instanceof Integer || obj instanceof Long || obj instanceof String) {
obj.compa<caret>
}
return -1;
}
}

View File

@@ -0,0 +1,11 @@
import java.math.BigInteger;
import java.util.function.Function;
class Test {
long test(Object obj) {
if (obj instanceof Integer || obj instanceof Long || obj instanceof String) {
((Comparable) obj).compareTo()
}
return -1;
}
}

View File

@@ -0,0 +1,11 @@
import java.math.BigInteger;
import java.util.function.Function;
class Test {
long test(Object obj) {
if (obj instanceof Integer || obj instanceof Long) {
((Number) obj).longValue()
}
return -1;
}
}

View File

@@ -61,6 +61,8 @@ class NormalCompletionDfaTest extends NormalCompletionTestCase {
void testNoUnnecessaryCastRawDfa() { doTest() }
void testNoUnnecessaryCastDeepHierarchy() { doTest() }
void testInstanceOfAfterFunction() { doTest() }
void testInstanceOfDisjunction() { doTest() }
void testInstanceOfDisjunction2() { doTest() }
void testComplexInstanceOfDfa() {
configureByTestName()
myFixture.assertPreferredCompletionItems 0, 'methodFromX', 'methodFromX2', 'methodFromY', 'methodFromY2'