mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
new inference: highlighting for contradictory inferred intersection types; sequential same method calls context (IDEA-128101)
This commit is contained in:
+26
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -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 {
|
||||
|
||||
+17
-6
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -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;
|
||||
}
|
||||
|
||||
+29
@@ -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>
|
||||
}
|
||||
|
||||
}
|
||||
+4
@@ -200,6 +200,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testIDEA128101() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user