diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index c24803d73a41..4ce421733107 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -1317,24 +1317,14 @@ public class GenericsHighlightUtil { static HighlightInfo checkInferredIntersections(PsiSubstitutor substitutor, TextRange ref) { for (Map.Entry typeEntry : substitutor.getSubstitutionMap().entrySet()) { + final String parameterName = typeEntry.getKey().getName(); 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(); - } - } - } - } + final String conflictingConjunctsMessage = ((PsiIntersectionType)type).getConflictingConjunctsMessage(); + if (conflictingConjunctsMessage != null) { + return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .descriptionAndTooltip("Type parameter " + parameterName + " has incompatible upper bounds: " + conflictingConjunctsMessage) + .range(ref).create(); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 0dc6c8fca8eb..426170abe4aa 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -35,7 +35,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.PsiSuperMethodImplUtil; -import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; @@ -363,6 +362,13 @@ public class HighlightMethodUtil { if (highlightInfo == null) { highlightInfo = checkVarargParameterErasureToBeAccessible((MethodCandidateInfo)resolveResult, methodCall); } + + if (highlightInfo == null && resolveResult instanceof MethodCandidateInfo) { + final String errorMessage = ((MethodCandidateInfo)resolveResult).getInferenceErrorMessage(); + if (errorMessage != null) { + highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(errorMessage).range(fixRange).create(); + } + } } } else { @@ -781,20 +787,21 @@ public class HighlightMethodUtil { PsiClass aClass = method.getContainingClass(); PsiParameter[] parameters = method.getParameterList().getParameters(); String methodName = method.getName(); - return createMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); + return createMismatchedArgumentsHtmlTooltip(list, info, parameters, methodName, substitutor, aClass); } private static String createShortMismatchedArgumentsHtmlTooltip(PsiExpressionList list, - PsiParameter[] parameters, - String methodName, - PsiSubstitutor substitutor, - PsiClass aClass) { + @Nullable MethodCandidateInfo info, + PsiParameter[] parameters, + String methodName, + PsiSubstitutor substitutor, + PsiClass aClass) { PsiExpression[] expressions = list.getExpressions(); int cols = Math.max(parameters.length, expressions.length); @Language("HTML") @NonNls String parensizedName = methodName + (parameters.length == 0 ? "( ) " : ""); - final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent()); + final String errorMessage = info != null ? info.getInferenceErrorMessage() : null; return JavaErrorMessages.message( "argument.mismatch.html.tooltip", Integer.valueOf(cols - parameters.length + 1), parensizedName, @@ -833,22 +840,24 @@ public class HighlightMethodUtil { } private static String createMismatchedArgumentsHtmlTooltip(PsiExpressionList list, + MethodCandidateInfo info, PsiParameter[] parameters, String methodName, PsiSubstitutor substitutor, PsiClass aClass) { return Math.max(parameters.length, list.getExpressions().length) <= 2 - ? createShortMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass) - : createLongMismatchedArgumentsHtmlTooltip(list, parameters, methodName, substitutor, aClass); + ? createShortMismatchedArgumentsHtmlTooltip(list, info, parameters, methodName, substitutor, aClass) + : createLongMismatchedArgumentsHtmlTooltip(list, info, parameters, methodName, substitutor, aClass); } @SuppressWarnings("StringContatenationInLoop") @Language("HTML") private static String createLongMismatchedArgumentsHtmlTooltip(PsiExpressionList list, - PsiParameter[] parameters, - String methodName, - PsiSubstitutor substitutor, - PsiClass aClass) { + @Nullable MethodCandidateInfo info, + PsiParameter[] parameters, + String methodName, + PsiSubstitutor substitutor, + PsiClass aClass) { PsiExpression[] expressions = list.getExpressions(); @SuppressWarnings("NonConstantStringShouldBeStringBuffer") @NonNls @@ -904,7 +913,7 @@ public class HighlightMethodUtil { } s+= ""; - final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent()); + final String errorMessage = info != null ? info.getInferenceErrorMessage() : null; if (errorMessage != null) { s+= "reason: "; s += XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "
"); @@ -1459,7 +1468,7 @@ public class HighlightMethodUtil { String constructorName = aClass.getName(); String argTypes = buildArgTypesList(list); String description = JavaErrorMessages.message("wrong.constructor.arguments", constructorName+"()", argTypes); - String tooltip = createMismatchedArgumentsHtmlTooltip(list, PsiParameter.EMPTY_ARRAY, constructorName, PsiSubstitutor.EMPTY, aClass); + String tooltip = createMismatchedArgumentsHtmlTooltip(list, null, PsiParameter.EMPTY_ARRAY, constructorName, PsiSubstitutor.EMPTY, aClass); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).description(description).escapedToolTip(tooltip).navigationShift(+1).create(); QuickFixAction.registerQuickFixAction(info, constructorCall.getTextRange(), QUICK_FIX_FACTORY.createCreateConstructorFromCallFix(constructorCall)); if (classReference != null) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 2d3c1218afb0..c6569c11c06e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -38,9 +38,9 @@ import com.intellij.psi.*; import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef; import com.intellij.psi.impl.source.resolve.JavaResolveUtil; -import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; +import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTagValue; import com.intellij.psi.util.*; @@ -324,7 +324,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh else { if (!LambdaUtil.isLambdaFullyInferred(expression, functionalInterfaceType) && !expression.hasFormalParameterTypes()) { final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(expression, PsiCallExpression.class); - String description = callExpression != null ? InferenceSession.getInferenceErrorMessage(callExpression) : null; + String description; + if (callExpression != null) { + final JavaResolveResult result = callExpression.resolveMethodGenerics(); + description = result instanceof MethodCandidateInfo ? ((MethodCandidateInfo)result).getInferenceErrorMessage() : null; + } + else { + description = null; + } if (description == null) { description = "Cyclic inference"; } diff --git a/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java b/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java index 3f184ea50640..cf5cf7a8b555 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java @@ -17,6 +17,7 @@ package com.intellij.psi; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -193,4 +194,22 @@ public class PsiIntersectionType extends PsiType.Stub { } return sb.toString(); } + + public String getConflictingConjunctsMessage() { + final PsiType[] conjuncts = 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 conjuncts[i].getPresentableText() + " and " + conjuncts[i1].getPresentableText(); + } + } + } + } + } + return null; + } } diff --git a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java index f13054cd1190..f61f0e9e6505 100644 --- a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java +++ b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java @@ -45,6 +45,8 @@ public class MethodCandidateInfo extends CandidateInfo{ private final PsiType[] myArgumentTypes; private final PsiType[] myTypeArguments; private PsiSubstitutor myCalcedSubstitutor; // benign race + + private String myInferenceError; private final LanguageLevel myLanguageLevel; public MethodCandidateInfo(@NotNull PsiElement candidate, @@ -353,6 +355,14 @@ public class MethodCandidateInfo extends CandidateInfo{ return 31 * super.hashCode() + (isVarargs() ? 1 : 0); } + public void setInferenceError(String inferenceError) { + myInferenceError = inferenceError; + } + + public String getInferenceErrorMessage() { + return myInferenceError; + } + public static class CurrentCandidateProperties { private final MethodCandidateInfo myMethod; private PsiSubstitutor mySubstitutor; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index 210345c4f19e..ae2a91451889 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -58,7 +58,6 @@ public class InferenceSession { } }; - private static final Key> INFERENCE_FAILURE_MESSAGE = Key.create("FAILURE_MESSAGE"); private static final String EQUALITY_CONSTRAINTS_PRESENTATION = "equality constraints"; private static final String UPPER_BOUNDS_PRESENTATION = "upper bounds"; private static final String LOWER_BOUNDS_PRESENTATION = "lower bounds"; @@ -71,6 +70,8 @@ public class InferenceSession { private final PsiManager myManager; private int myConstraintIdx = 0; + private List myErrorMessages; + private boolean myErased = false; private final InferenceIncorporationPhase myIncorporationPhase = new InferenceIncorporationPhase(this); @@ -287,56 +288,63 @@ public class InferenceSession { @Nullable PsiExpression[] args, @Nullable PsiElement parent) { final MethodCandidateInfo.CurrentCandidateProperties properties = getCurrentProperties(parent); - if (!repeatInferencePhases(true)) { - //inferred result would be checked as candidate won't be applicable - return resolveSubset(myInferenceVariables, mySiteSubstitutor); - } - - if (properties != null && !properties.isApplicabilityCheck()) { - initReturnTypeConstraint(properties.getMethod(), (PsiCall)parent); + try { if (!repeatInferencePhases(true)) { + //inferred result would be checked as candidate won't be applicable + return resolveSubset(myInferenceVariables, mySiteSubstitutor); + } + + if (properties != null && !properties.isApplicabilityCheck()) { + initReturnTypeConstraint(properties.getMethod(), (PsiCall)parent); + if (!repeatInferencePhases(true)) { + return prepareSubstitution(); + } + + if (parameters != null && args != null) { + final Set additionalConstraints = new LinkedHashSet(); + if (parameters.length > 0) { + collectAdditionalConstraints(parameters, args, properties.getMethod(), mySiteSubstitutor, additionalConstraints, properties.isVarargs()); + } + + if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints)) { + return prepareSubstitution().putAll(retrieveNonPrimitiveEqualsBounds(myInferenceVariables)); + } + } + } + + final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, PsiSubstitutor.EMPTY); + if (substitutor != null) { + if (myContext != null) { + myContext.putUserData(ERASED, myErased); + } + final Map map = substitutor.getSubstitutionMap(); + for (PsiTypeParameter parameter : map.keySet()) { + final PsiType mapping = map.get(parameter); + PsiTypeParameter param; + if (parameter instanceof InferenceVariable) { + ((InferenceVariable)parameter).setInstantiation(mapping); + if (((InferenceVariable)parameter).getCallContext() != myContext) { + //don't include in result substitutor foreign inference variables + continue; + } + param = ((InferenceVariable)parameter).getParameter(); + } + else { + param = parameter; + } + mySiteSubstitutor = mySiteSubstitutor.put(param, mapping); + } + } else { return prepareSubstitution(); } - if (parameters != null && args != null) { - final Set additionalConstraints = new LinkedHashSet(); - if (parameters.length > 0) { - collectAdditionalConstraints(parameters, args, properties.getMethod(), mySiteSubstitutor, additionalConstraints, properties.isVarargs()); - } - - if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints)) { - return prepareSubstitution().putAll(retrieveNonPrimitiveEqualsBounds(myInferenceVariables)); - } - } - } - - final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, PsiSubstitutor.EMPTY); - if (substitutor != null) { - if (myContext != null) { - myContext.putUserData(ERASED, myErased); - } - final Map map = substitutor.getSubstitutionMap(); - for (PsiTypeParameter parameter : map.keySet()) { - final PsiType mapping = map.get(parameter); - PsiTypeParameter param; - if (parameter instanceof InferenceVariable) { - ((InferenceVariable)parameter).setInstantiation(mapping); - if (((InferenceVariable)parameter).getCallContext() != myContext) { - //don't include in result substitutor foreign inference variables - continue; - } - param = ((InferenceVariable)parameter).getParameter(); - } - else { - param = parameter; - } - mySiteSubstitutor = mySiteSubstitutor.put(param, mapping); - } - } else { return prepareSubstitution(); } - - return prepareSubstitution(); + finally { + if (properties != null && myErrorMessages != null) { + properties.getInfo().setInferenceError(StringUtil.join(myErrorMessages, "\n")); + } + } } private void collectAdditionalConstraints(PsiParameter[] parameters, @@ -1029,6 +1037,14 @@ public class InferenceSession { else { type = myErased ? null : upperBound; } + + if (type instanceof PsiIntersectionType) { + final String conflictingConjunctsMessage = ((PsiIntersectionType)type).getConflictingConjunctsMessage(); + if (conflictingConjunctsMessage != null) { + registerIncompatibleErrorMessage("Type parameter " + var.getName() + " has incompatible upper bounds: " + conflictingConjunctsMessage); + return PsiType.NULL; + } + } } else { for (PsiType upperType : var.getBounds(InferenceBound.UPPER)) { @@ -1051,25 +1067,12 @@ public class InferenceSession { } private void registerIncompatibleErrorMessage(String value) { - if (myContext != null) { - List errorMessage = myContext.getUserData(INFERENCE_FAILURE_MESSAGE); - if (errorMessage == null) { - errorMessage = Collections.synchronizedList(new ArrayList()); - myContext.putUserData(INFERENCE_FAILURE_MESSAGE, errorMessage); - } - if (!errorMessage.contains(value)) { - errorMessage.add(value); - } + if (myErrorMessages == null) { + myErrorMessages = new ArrayList(); } - } - - @Nullable - public static String getInferenceErrorMessage(@NotNull PsiElement context) { - final List errors = context.getUserData(INFERENCE_FAILURE_MESSAGE); - if (errors != null) { - return StringUtil.join(errors, "\n"); + if (!myErrorMessages.contains(value)) { + myErrorMessages.add(value); } - return null; } private String incompatibleBoundsMessage(final InferenceVariable var, diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/DoNotIgnoreConflictingUpperBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/DoNotIgnoreConflictingUpperBounds.java new file mode 100644 index 000000000000..4233035834c4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/DoNotIgnoreConflictingUpperBounds.java @@ -0,0 +1,14 @@ +class Test { + + public void testConsume() { + consume(exception()); + } + + public static void consume(Throwable t) {} + + public static void consume(String s) {} + + public static E exception() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index ecb4270d735c..87a5cd6013bd 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -335,6 +335,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase doTest(); } + public void testDoNotIgnoreConflictingUpperBounds() throws Exception { + doTest(); + } + private void doTest() throws Exception { doTest(false); }