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 83afbd7bbe05..deacd9209c0f 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,6 +35,7 @@ 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.*; @@ -207,7 +208,8 @@ public class HighlightMethodUtil { @NotNull String detailMessage, @NotNull TextRange textRange) { String description = MessageFormat.format("{0}; {1}", createClashMethodMessage(method, superMethod, true), detailMessage); - HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create(); + HighlightInfo errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip( + description).create(); QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createMethodReturnFix(method, substitutedSuperReturnType, false)); QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createSuperMethodReturnFix(superMethod, returnType)); @@ -690,7 +692,7 @@ public class HighlightMethodUtil { QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, action); } QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createReplaceAddAllArrayToCollectionFix(methodCall)); - QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createSurroundWithArrayFix(methodCall,null)); + QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createSurroundWithArrayFix(methodCall, null)); QualifyThisArgumentFix.registerQuickFixAction(methodCandidates, methodCall, highlightInfo, fixRange); CandidateInfo[] candidates = resolveHelper.getReferencedMethodCandidates(methodCall, true); @@ -764,12 +766,14 @@ public class HighlightMethodUtil { @Language("HTML") @NonNls String parensizedName = methodName + (parameters.length == 0 ? "( ) " : ""); + final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent()); return JavaErrorMessages.message( "argument.mismatch.html.tooltip", Integer.valueOf(cols - parameters.length + 1), parensizedName, HighlightUtil.formatClass(aClass, false), createMismatchedArgsHtmlTooltipParamsRow(parameters, substitutor, expressions), - createMismatchedArgsHtmlTooltipArgumentsRow(expressions, parameters, substitutor, cols) + createMismatchedArgsHtmlTooltipArgumentsRow(expressions, parameters, substitutor, cols), + errorMessage != null ? "
reason: " + XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "
") : "" ); } @@ -870,7 +874,13 @@ public class HighlightMethodUtil { s += ""; } - s+= ""; + s+= ""; + final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent()); + if (errorMessage != null) { + s+= "reason: "; + s += XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "
"); + } + s+= ""; return s; } 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 d24c1018b520..19d4ddc2738f 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 @@ -52,6 +52,11 @@ 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"; + private final Set myInferenceVariables = new LinkedHashSet(); private final List myConstraints = new ArrayList(); private final Set myConstraintsCopy = new HashSet(); @@ -428,14 +433,20 @@ public class InferenceSession { } private PsiSubstitutor prepareSubstitution() { - for (InferenceVariable inferenceVariable : myInferenceVariables) { - final PsiTypeParameter typeParameter = inferenceVariable.getParameter(); - PsiType instantiation = inferenceVariable.getInstantiation(); - if (instantiation == PsiType.NULL) { + ArrayList allVars = new ArrayList(myInferenceVariables); + while (!allVars.isEmpty()) { + final List variables = InferenceVariablesOrder.resolveOrder(allVars, this); + for (InferenceVariable inferenceVariable : variables) { + final PsiTypeParameter typeParameter = inferenceVariable.getParameter(); + PsiType instantiation = inferenceVariable.getInstantiation(); //failed inference - mySiteSubstitutor = mySiteSubstitutor - .put(typeParameter, JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(typeParameter)); + if (instantiation == PsiType.NULL) { + checkBoundsConsistency(mySiteSubstitutor, inferenceVariable); + mySiteSubstitutor = mySiteSubstitutor + .put(typeParameter, JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(typeParameter)); + } } + allVars.removeAll(variables); } return mySiteSubstitutor; } @@ -864,7 +875,6 @@ public class InferenceSession { } private PsiSubstitutor resolveSubset(Collection vars, PsiSubstitutor substitutor) { - nextVar: for (InferenceVariable var : vars) { LOG.assertTrue(var.getInstantiation() == PsiType.NULL); final PsiTypeParameter typeParameter = var.getParameter(); @@ -872,43 +882,100 @@ public class InferenceSession { continue;//todo } - final PsiType eqBound = getEqualsBound(var, substitutor); - if (eqBound != PsiType.NULL && eqBound instanceof PsiPrimitiveType) continue; - final PsiType lowerBound = getLowerBound(var, substitutor); - final PsiType upperBound = getUpperBound(var, substitutor); - PsiType type; - if (eqBound != PsiType.NULL && (myErased || eqBound != null)) { - if (lowerBound != PsiType.NULL && !TypeConversionUtil.isAssignable(eqBound, lowerBound)) { - continue; - } else { - type = eqBound; - } + final PsiType type = checkBoundsConsistency(substitutor, var); + if (type != PsiType.NULL) { + substitutor = substitutor.put(typeParameter, type); } - else { - type = lowerBound; - } - if (type == PsiType.NULL) { - if (var.isThrownBound() && isThrowable(var.getBounds(InferenceBound.UPPER))) { - type = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject())); - } - else { - if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().get(typeParameter) != null) continue; - type = myErased ? null : upperBound; - } - } - else { - for (PsiType upperType : var.getBounds(InferenceBound.UPPER)) { - if (isProperType(upperType) && !TypeConversionUtil.isAssignable(substitutor.substitute(upperType), lowerBound)) { - continue nextVar; - } - } - } - substitutor = substitutor.put(typeParameter, type); } return substitutor; } + private PsiType checkBoundsConsistency(PsiSubstitutor substitutor, InferenceVariable var) { + final PsiType eqBound = getEqualsBound(var, substitutor); + if (eqBound != PsiType.NULL && eqBound instanceof PsiPrimitiveType) return PsiType.NULL; + final PsiType lowerBound = getLowerBound(var, substitutor); + final PsiType upperBound = getUpperBound(var, substitutor); + PsiType type; + if (eqBound != PsiType.NULL && (myErased || eqBound != null)) { + if (lowerBound != PsiType.NULL && !TypeConversionUtil.isAssignable(eqBound, lowerBound)) { + registerIncompatibleErrorMessage( + incompatibleBoundsMessage(var, substitutor, InferenceBound.EQ, EQUALITY_CONSTRAINTS_PRESENTATION, InferenceBound.LOWER, LOWER_BOUNDS_PRESENTATION), + var.getParameter()); + return PsiType.NULL; + } else { + type = eqBound; + } + } + else { + type = lowerBound; + } + + if (type == PsiType.NULL) { + if (var.isThrownBound() && isThrowable(var.getBounds(InferenceBound.UPPER))) { + type = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject())); + } + else { + if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().get(var.getParameter()) != null) return PsiType.NULL; + type = myErased ? null : upperBound; + } + } + else { + for (PsiType upperType : var.getBounds(InferenceBound.UPPER)) { + if (isProperType(upperType) && !TypeConversionUtil.isAssignable(substitutor.substitute(upperType), lowerBound)) { + final String incompatibleBoundsMessage; + if (type != lowerBound) { + incompatibleBoundsMessage = incompatibleBoundsMessage(var, substitutor, InferenceBound.EQ, EQUALITY_CONSTRAINTS_PRESENTATION, InferenceBound.UPPER, UPPER_BOUNDS_PRESENTATION); + } + else { + incompatibleBoundsMessage = incompatibleBoundsMessage(var, substitutor, InferenceBound.LOWER, LOWER_BOUNDS_PRESENTATION, InferenceBound.UPPER, UPPER_BOUNDS_PRESENTATION); + } + registerIncompatibleErrorMessage(incompatibleBoundsMessage, var.getParameter()); + return PsiType.NULL; + } + } + } + return type; + } + + private void registerIncompatibleErrorMessage(String value, PsiTypeParameter parameter) { + if (myContext != null) { + Map errorMessage = myContext.getUserData(INFERENCE_FAILURE_MESSAGE); + if (errorMessage == null) { + errorMessage = new LinkedHashMap(); + myContext.putUserData(INFERENCE_FAILURE_MESSAGE, errorMessage); + } + errorMessage.put(parameter, value); + } + } + + @Nullable + public static String getInferenceErrorMessage(PsiElement context) { + final Map errorsMap = context.getUserData(INFERENCE_FAILURE_MESSAGE); + if (errorsMap != null) { + return StringUtil.join(errorsMap.values(), "\n"); + } + return null; + } + + private String incompatibleBoundsMessage(final InferenceVariable var, + final PsiSubstitutor substitutor, + final InferenceBound lowBound, + final String lowBoundName, + final InferenceBound upperBound, + final String upperBoundName) { + final Function typePresentation = new Function() { + @Override + public String fun(PsiType type) { + final PsiType substituted = substituteNonProperBound(type, substitutor); + return (substituted != null ? substituted : type).getPresentableText(); + } + }; + return "inference variable " + var.getName() + " has incompatible bounds:\n " + + lowBoundName + ": " + StringUtil.join(var.getBounds(lowBound), typePresentation, ", ") + "\n" + + upperBoundName + ": " + StringUtil.join(var.getBounds(upperBound), typePresentation, ", "); + } + private PsiType getLowerBound(InferenceVariable var, PsiSubstitutor substitutor) { return composeBound(var, InferenceBound.LOWER, new Function, PsiType>() { @Override diff --git a/java/java-psi-impl/src/messages/JavaErrorMessages.properties b/java/java-psi-impl/src/messages/JavaErrorMessages.properties index 8f5469dbf0eb..ffee2225b402 100644 --- a/java/java-psi-impl/src/messages/JavaErrorMessages.properties +++ b/java/java-psi-impl/src/messages/JavaErrorMessages.properties @@ -181,7 +181,8 @@ argument.mismatch.html.tooltip=\ \ {3}\ {4}\ -
{1}in {2}\\ cannot be applied
to
+ \ + {5} # {0} - left raw type, {1} - required row, {2} - right raw type, {3} - found row incompatible.types.html.tooltip=\