mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
intersection in cast type: repeat interface, different parameterization checks
This commit is contained in:
+50
-8
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+8
-7
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -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>;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
+4
@@ -746,6 +746,10 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testCastToIntersection() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testIDEA122401() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user