intersection in cast type: repeat interface, different parameterization checks

This commit is contained in:
Anna Kozlova
2015-04-20 10:19:22 +02:00
parent 2318a3a58c
commit f5cec5e24a
9 changed files with 79 additions and 32 deletions
@@ -36,15 +36,13 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.javadoc.PsiDocComment;
@@ -55,11 +53,10 @@ import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.*;
import com.intellij.util.containers.hash.HashSet;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.util.XmlStringUtil;
import gnu.trove.THashMap;
@@ -296,6 +293,13 @@ public class HighlightUtil extends HighlightUtilBase {
}
/**
* 15.16 Cast Expressions
* ( ReferenceType {AdditionalBound} ) expression, where AdditionalBound: & InterfaceType then all must be true
* • ReferenceType must denote a class or interface type.
* • The erasures of all the listed types must be pairwise different.
* • No two listed types may be subtypes of different parameterizations of the same generic interface.
*/
@Nullable
static HighlightInfo checkIntersectionInTypeCast(@NotNull PsiTypeCastExpression expression, @NotNull LanguageLevel languageLevel) {
final PsiTypeElement castTypeElement = expression.getCastType();
@@ -305,6 +309,8 @@ public class HighlightUtil extends HighlightUtilBase {
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
final PsiTypeElement[] conjuncts = PsiTreeUtil.getChildrenOfType(castTypeElement, PsiTypeElement.class);
if (conjuncts != null) {
final Set<PsiType> erasures = new HashSet<PsiType>(conjuncts.length);
erasures.add(TypeConversionUtil.erasure(conjuncts[0].getType()));
final List<PsiTypeElement> conjList = new ArrayList<PsiTypeElement>(Arrays.asList(conjuncts));
for (int i = 1; i < conjuncts.length; i++) {
final PsiTypeElement conjunct = conjuncts[i];
@@ -319,6 +325,42 @@ public class HighlightUtil extends HighlightUtilBase {
return errorResult;
}
}
else {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip("Unexpected type: class is expected").create();
}
if (!erasures.add(TypeConversionUtil.erasure(conjType))) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(conjunct)
.descriptionAndTooltip("Repeated interface").create();
}
}
final List<PsiType> typeList = ContainerUtil.map(conjList, new Function<PsiTypeElement, PsiType>() {
@Override
public PsiType fun(PsiTypeElement element) {
return element.getType();
}
});
final Ref<String> differentArgumentsMessage = new Ref<String>();
final PsiClass sameGenericParameterization =
InferenceSession.findParameterizationOfTheSameGenericClass(typeList, new Processor<Pair<PsiType, PsiType>>() {
@Override
public boolean process(Pair<PsiType, PsiType> pair) {
if (!TypesDistinctProver.provablyDistinct(pair.first, pair.second)) {
return true;
}
differentArgumentsMessage.set(pair.first.getPresentableText() + " and " + pair.second.getPresentableText());
return false;
}
});
if (sameGenericParameterization != null) {
final String message = formatClass(sameGenericParameterization) + " cannot be inherited with different arguments: " +
differentArgumentsMessage.get();
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(expression)
.descriptionAndTooltip(message).create();
}
}
} else {
@@ -44,8 +44,15 @@ public class PsiIntersectionType extends PsiType.Stub {
@NotNull
public static PsiType createIntersection(PsiType... conjuncts) {
return createIntersection(true, conjuncts);
}
@NotNull
public static PsiType createIntersection(boolean flatten, PsiType... conjuncts) {
assert conjuncts.length > 0;
conjuncts = flattenAndRemoveDuplicates(conjuncts);
if (flatten) {
conjuncts = flattenAndRemoveDuplicates(conjuncts);
}
if (conjuncts.length == 1) return conjuncts[0];
return new PsiIntersectionType(conjuncts);
}
@@ -727,18 +727,7 @@ public class RedundantCastUtil {
if (operand instanceof PsiLambdaExpression || operand instanceof PsiMethodReferenceExpression) {
if (castType instanceof PsiClassType && InheritanceUtil.isInheritor(PsiUtil.resolveClassInType(castType), CommonClassNames.JAVA_IO_SERIALIZABLE)) return true;
if (castType instanceof PsiIntersectionType) {
boolean redundant = false;
final PsiType[] conjuncts = ((PsiIntersectionType)castType).getConjuncts();
for (int i = 1; i < conjuncts.length; i++) {
PsiType conjunct = conjuncts[i];
if (TypeConversionUtil.isAssignable(conjuncts[0], conjunct)) {
redundant = true;
break;
}
}
if (!redundant) {
return true;
}
return true;
}
}
@@ -145,7 +145,7 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
if (PsiUtil.isJavaToken(child, JavaTokenType.AND)) {
List<PsiType> types = collectTypes();
assert !types.isEmpty() : this;
type = PsiIntersectionType.createIntersection(types);
type = PsiIntersectionType.createIntersection(false, types.toArray(PsiType.createArray(types.size())));
break;
}
@@ -280,7 +280,7 @@ public class InferenceIncorporationPhase {
* then for all i, 1 ≤ i ≤ n, if Si and Ti are types (not wildcards), the constraint ⟨Si = Ti⟩ is implied.
*/
private boolean upUp(List<PsiType> upperBounds) {
return mySession.findParameterizationOfTheSameGenericClass(upperBounds, new Processor<Pair<PsiType, PsiType>>() {
return InferenceSession.findParameterizationOfTheSameGenericClass(upperBounds, new Processor<Pair<PsiType, PsiType>>() {
@Override
public boolean process(Pair<PsiType, PsiType> pair) {
final PsiType sType = pair.first;
@@ -290,7 +290,7 @@ public class InferenceIncorporationPhase {
}
return true;
}
});
}) != null;
}
private void addConstraint(ConstraintFormula constraint) {
@@ -569,7 +569,7 @@ public class InferenceSession {
return false;
}
private boolean hasWildcardParameterization(InferenceVariable inferenceVariable, PsiClassType targetType) {
private static boolean hasWildcardParameterization(InferenceVariable inferenceVariable, PsiClassType targetType) {
if (!FunctionalInterfaceParameterizationUtil.isWildcardParameterized(targetType)) {
final List<PsiType> bounds = inferenceVariable.getBounds(InferenceBound.LOWER);
final Processor<Pair<PsiType, PsiType>> differentParameterizationProcessor = new Processor<Pair<PsiType, PsiType>>() {
@@ -578,7 +578,7 @@ public class InferenceSession {
return pair.first == null || pair.second == null || !TypesDistinctProver.provablyDistinct(pair.first, pair.second);
}
};
if (findParameterizationOfTheSameGenericClass(bounds, differentParameterizationProcessor)) return true;
if (findParameterizationOfTheSameGenericClass(bounds, differentParameterizationProcessor) != null) return true;
final List<PsiType> eqBounds = inferenceVariable.getBounds(InferenceBound.EQ);
for (PsiType lowBound : bounds) {
if (FunctionalInterfaceParameterizationUtil.isWildcardParameterized(lowBound)) {
@@ -1582,8 +1582,8 @@ public class InferenceSession {
return originalContext != null && originalContext == p2.getUserData(ORIGINAL_CONTEXT);
}
public boolean findParameterizationOfTheSameGenericClass(List<PsiType> upperBounds,
Processor<Pair<PsiType, PsiType>> processor) {
public static PsiClass findParameterizationOfTheSameGenericClass(List<PsiType> upperBounds,
Processor<Pair<PsiType, PsiType>> processor) {
for (int i = 0; i < upperBounds.size(); i++) {
final PsiType sBound = upperBounds.get(i);
final PsiClass sClass = PsiUtil.resolveClassInClassTypeOnly(sBound);
@@ -1605,14 +1605,15 @@ public class InferenceSession {
for (PsiTypeParameter typeParameter : gClass.getTypeParameters()) {
final PsiType sType = sSubstitutor.substitute(typeParameter);
final PsiType tType = tSubstitutor.substitute(typeParameter);
if (!processor.process(Pair.create(sType, tType))) {
return true;
final Pair<PsiType, PsiType> typePair = Pair.create(sType, tType);
if (!processor.process(typePair)) {
return gClass;
}
}
}
}
}
}
return false;
return null;
}
}
@@ -1,4 +1,5 @@
import java.io.Serializable;
import java.util.*;
class FooBar1 {
{
@@ -7,5 +8,8 @@ class FooBar1 {
Object y2 = (CharSequence & <error descr="Interface expected here">Integer</error>) x;
Object y3 = (Integer & CharSequence) x;
<error descr="Incompatible types. Found: 'java.lang.CharSequence & java.io.Serializable', required: 'int'">int y1 = (CharSequence & Serializable) x;</error>
Object z0 = (Cloneable & <error descr="Unexpected type: class is expected">Runnable[]</error>) x;
Object z1 = (Cloneable & <error descr="Repeated interface">Cloneable</error>) x;
Object z2 = <error descr="java.util.Collection cannot be inherited with different arguments: String and Integer">(List<String> & Set<Integer>) x</error>;
}
}
@@ -9,6 +9,6 @@ class Test {
{
Predicate<String> mh1 = (Predicate<String> & Serializable)Test::test;
Predicate<String> mh0 = (<warning descr="Casting 'Test::test' to 'Predicate<String> & Predicate<String>' is redundant">Predicate<String> & Predicate<String></warning>)Test::test;
Predicate<String> mh0 = (Predicate<String> & <error descr="Repeated interface">Predicate<String></error>)Test::test;
}
}
@@ -746,6 +746,10 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testCastToIntersection() throws Exception {
doTest();
}
public void testIDEA122401() throws Exception {
doTest();
}