new inference: highlighting for contradictory inferred intersection types; sequential same method calls context (IDEA-128101)

This commit is contained in:
Anna Kozlova
2014-08-05 11:59:06 +02:00
parent 188175b38c
commit 28569c671e
7 changed files with 94 additions and 13 deletions
@@ -1292,5 +1292,31 @@ public class GenericsHighlightUtil {
}
}
}
public static HighlightInfo checkInferredIntersections(PsiSubstitutor substitutor, TextRange ref) {
for (Map.Entry<PsiTypeParameter, PsiType> typeEntry : substitutor.getSubstitutionMap().entrySet()) {
final PsiType type = typeEntry.getValue();
if (type instanceof PsiIntersectionType) {
final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts();
for (int i = 0; i < conjuncts.length; i++) {
PsiClass conjunct = PsiUtil.resolveClassInClassTypeOnly(conjuncts[i]);
if (conjunct != null && !conjunct.isInterface()) {
for (int i1 = i + 1; i1 < conjuncts.length; i1++) {
PsiClass oppositeConjunct = PsiUtil.resolveClassInClassTypeOnly(conjuncts[i1]);
if (oppositeConjunct != null && !oppositeConjunct.isInterface()) {
if (!conjunct.isInheritor(oppositeConjunct, true) && !oppositeConjunct.isInheritor(conjunct, true)) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.descriptionAndTooltip("Type parameter " + typeEntry.getKey().getName() + " has incompatible upper bounds: " +
conjunct.getName() + " and " + oppositeConjunct.getName())
.range(ref).create();
}
}
}
}
}
}
}
return null;
}
}
@@ -344,8 +344,12 @@ public class HighlightMethodUtil {
if (resolved instanceof PsiMethod && resolveResult.isValidResult()) {
TextRange fixRange = getFixRange(methodCall);
highlightInfo = HighlightUtil.checkUnhandledExceptions(methodCall, fixRange);
if (highlightInfo == null && !LambdaUtil.isValidQualifier4InterfaceStaticMethodCall((PsiMethod)resolved, methodCall.getMethodExpression(), resolveResult.getCurrentFileResolveScope(), languageLevel)) {
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip("Static method may be invoked on containing interface class only").range(fixRange).create();
if (highlightInfo == null) {
if (!LambdaUtil.isValidQualifier4InterfaceStaticMethodCall((PsiMethod)resolved, methodCall.getMethodExpression(), resolveResult.getCurrentFileResolveScope(), languageLevel)) {
highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip("Static method may be invoked on containing interface class only").range(fixRange).create();
} else {
highlightInfo = GenericsHighlightUtil.checkInferredIntersections(substitutor, fixRange);
}
}
}
else {
@@ -349,13 +349,17 @@ public class InferenceSession {
}
public boolean initBounds(PsiTypeParameter... typeParameters) {
return initBounds(myContext, typeParameters);
}
public boolean initBounds(PsiElement context, PsiTypeParameter... typeParameters) {
boolean sameMethodCall = false;
for (PsiTypeParameter parameter : typeParameters) {
if (myInferenceVariables.containsKey(parameter)) {
sameMethodCall = true;
continue;
}
InferenceVariable variable = new InferenceVariable(parameter);
InferenceVariable variable = new InferenceVariable(context, parameter);
boolean added = false;
final PsiClassType[] extendsListTypes = parameter.getExtendsListTypes();
for (PsiType classType : extendsListTypes) {
@@ -701,7 +705,7 @@ public class InferenceSession {
final PsiTypeParameter copy = elementFactory.createTypeParameterFromText("z" + parameter.getName(), null);
final PsiType lub = getLowerBound(var, substitutor);
final PsiType glb = getUpperBound(var, substitutor);
final InferenceVariable zVariable = new InferenceVariable(copy);
final InferenceVariable zVariable = new InferenceVariable(var.getCallContext(), copy);
zVariable.addBound(glb, InferenceBound.UPPER);
if (lub != PsiType.NULL) {
if (!TypeConversionUtil.isAssignable(glb, lub)) {
@@ -1250,14 +1254,17 @@ public class InferenceSession {
return myIncorporationPhase.hasCaptureConstraints(Arrays.asList(inferenceVariable));
}
public void liftBounds(Collection<InferenceVariable> variables) {
public void liftBounds(PsiElement context, Collection<InferenceVariable> variables) {
for (InferenceVariable variable : variables) {
final PsiTypeParameter parameter = variable.getParameter();
final InferenceVariable inferenceVariable = getInferenceVariable(parameter);
if (inferenceVariable != null) {
for (InferenceBound boundType : InferenceBound.values()) {
for (PsiType bound : variable.getBounds(boundType)) {
inferenceVariable.addBound(bound, boundType);
final PsiElement callContext = inferenceVariable.getCallContext();
if (context.equals(callContext) || myContext.equals(callContext)) {
for (InferenceBound boundType : InferenceBound.values()) {
for (PsiType bound : variable.getBounds(boundType)) {
inferenceVariable.addBound(bound, boundType);
}
}
}
} else {
@@ -1270,4 +1277,8 @@ public class InferenceSession {
final Boolean erased = call.getUserData(ERASED);
return erased != null && erased.booleanValue();
}
public PsiElement getContext() {
return myContext;
}
}
@@ -26,6 +26,8 @@ import java.util.*;
* User: anna
*/
public class InferenceVariable extends LightTypeParameter {
private PsiElement myContext;
public PsiTypeParameter getParameter() {
return getDelegate();
}
@@ -35,8 +37,9 @@ public class InferenceVariable extends LightTypeParameter {
private PsiType myInstantiation = PsiType.NULL;
InferenceVariable(PsiTypeParameter parameter) {
InferenceVariable(PsiElement context, PsiTypeParameter parameter) {
super(parameter);
myContext = context;
}
public PsiType getInstantiation() {
@@ -129,4 +132,8 @@ public class InferenceVariable extends LightTypeParameter {
public String toString() {
return getDelegate().toString();
}
public PsiElement getCallContext() {
return myContext;
}
}
@@ -110,7 +110,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
if (typeParams != null) {
final Set<PsiTypeParameter> oldBounds = ContainerUtil.newHashSet(session.getParamsToInfer());
final boolean sameMethodCall = session.initBounds(typeParams);
final boolean sameMethodCall = session.initBounds(myExpression, typeParams);
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final HashSet<InferenceVariable> variables = new HashSet<InferenceVariable>();
session.collectDependencies(returnType, variables);
@@ -138,8 +138,8 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
}
final Collection<PsiTypeParameter> params1 = session.getTypeParams();
final InferenceSession callSession = new InferenceSession(params1.toArray(new PsiTypeParameter[params1.size()]), substitutor, myExpression.getManager(), myExpression);
callSession.initBounds(params);
final InferenceSession callSession = new InferenceSession(params, substitutor, myExpression.getManager(), myExpression);
callSession.initBounds(session.getContext(), params1.toArray(new PsiTypeParameter[params1.size()]));
if (method != null) {
final PsiExpression[] args = argumentList.getExpressions();
final PsiParameter[] parameters = method.getParameterList().getParameters();
@@ -163,7 +163,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
}
}
}
session.liftBounds(inferenceVariables);
session.liftBounds(myExpression, inferenceVariables);
} else {
return false;
}
@@ -0,0 +1,29 @@
class TestIDEA128101 {
static class Attribute<Y> {};
static class Path<X> {};
static Attribute<Integer> integerAttribute;
static Attribute<String> stringAttribute;
static <Y> Path<Y> createPath(Attribute<Y> attribute) {
return new Path<>();
}
static <Y> Path<Y> createPath1(Attribute<Y> attribute) {
return new Path<>();
}
static <T> void construct(Class<T> aClass, Path<?>... paths) {}
static <T, K> void construct1(Class<T> aClass, Path<K>... paths) {}
static <T, K> void construct2(Class<T> aClass, Path<? extends K>... paths) {}
static <T, K> void construct3(Class<T> aClass, Path<? super K>... paths) {}
static <T, K> void construct4(Class<T> aClass, Path<? super K> path1, Path<? super K> path2) {}
public static void test() {
construct(String.class, createPath(integerAttribute), createPath(stringAttribute));
construct1<error descr="Cannot resolve method 'construct1(java.lang.Class<java.lang.String>, TestIDEA128101.Path<java.lang.Integer>, TestIDEA128101.Path<java.lang.String>)'">(String.class, createPath(integerAttribute), createPath(stringAttribute))</error>;
construct2(String.class, createPath(integerAttribute), createPath(stringAttribute));
<error descr="Type parameter K has incompatible upper bounds: Integer and String">construct3(String.class, createPath(integerAttribute), createPath(stringAttribute));</error>
<error descr="Type parameter K has incompatible upper bounds: Integer and String">construct4(String.class, createPath(integerAttribute), createPath(stringAttribute));</error>
}
}
@@ -200,6 +200,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
doTest();
}
public void testIDEA128101() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(false);
}