mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
new inference: show bounds incompatibility error messages detected during inference (IDEA-131882)
This commit is contained in:
+14
-4
@@ -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 ? "<br/>reason: " + XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "<br/>") : ""
|
||||
);
|
||||
}
|
||||
|
||||
@@ -870,7 +874,13 @@ public class HighlightMethodUtil {
|
||||
s += "</tr>";
|
||||
}
|
||||
|
||||
s+= "</table></body></html>";
|
||||
s+= "</table>";
|
||||
final String errorMessage = InferenceSession.getInferenceErrorMessage(list.getParent());
|
||||
if (errorMessage != null) {
|
||||
s+= "reason: ";
|
||||
s += XmlStringUtil.escapeString(errorMessage).replaceAll("\n", "<br/>");
|
||||
}
|
||||
s+= "</body></html>";
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+105
-38
@@ -52,6 +52,11 @@ public class InferenceSession {
|
||||
}
|
||||
};
|
||||
|
||||
private static final Key<Map<PsiTypeParameter, String>> 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<InferenceVariable> myInferenceVariables = new LinkedHashSet<InferenceVariable>();
|
||||
private final List<ConstraintFormula> myConstraints = new ArrayList<ConstraintFormula>();
|
||||
private final Set<ConstraintFormula> myConstraintsCopy = new HashSet<ConstraintFormula>();
|
||||
@@ -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<InferenceVariable> allVars = new ArrayList<InferenceVariable>(myInferenceVariables);
|
||||
while (!allVars.isEmpty()) {
|
||||
final List<InferenceVariable> 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<InferenceVariable> 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<PsiTypeParameter, String> errorMessage = myContext.getUserData(INFERENCE_FAILURE_MESSAGE);
|
||||
if (errorMessage == null) {
|
||||
errorMessage = new LinkedHashMap<PsiTypeParameter, String>();
|
||||
myContext.putUserData(INFERENCE_FAILURE_MESSAGE, errorMessage);
|
||||
}
|
||||
errorMessage.put(parameter, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getInferenceErrorMessage(PsiElement context) {
|
||||
final Map<PsiTypeParameter, String> 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<PsiType, String> typePresentation = new Function<PsiType, String>() {
|
||||
@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<Pair<PsiType, PsiType>, PsiType>() {
|
||||
@Override
|
||||
|
||||
@@ -181,7 +181,8 @@ argument.mismatch.html.tooltip=\
|
||||
<html><body><table border=0>\
|
||||
<tr><td><b>{1}</b></td>{3}<td colspan={0}>in <b>{2}</b>\\ cannot be applied</td></tr>\
|
||||
<tr><td>to</td>{4}</tr>\
|
||||
</table></body></html>
|
||||
</table>\
|
||||
{5}</body></html>
|
||||
|
||||
# {0} - left raw type, {1} - required row, {2} - right raw type, {3} - found row
|
||||
incompatible.types.html.tooltip=\
|
||||
|
||||
Reference in New Issue
Block a user