mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
move inference error from element to method candidate to avoid overload method pollution; show inference error in the editor if applicability check was successful; register inference error if variable has incompatible upper bounds (IDEA-145106)
This commit is contained in:
+6
-16
@@ -1317,24 +1317,14 @@ public class GenericsHighlightUtil {
|
||||
|
||||
static HighlightInfo checkInferredIntersections(PsiSubstitutor substitutor, TextRange ref) {
|
||||
for (Map.Entry<PsiTypeParameter, PsiType> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-15
@@ -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+= "</table>";
|
||||
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", "<br/>");
|
||||
@@ -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) {
|
||||
|
||||
+9
-2
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+65
-62
@@ -58,7 +58,6 @@ public class InferenceSession {
|
||||
}
|
||||
};
|
||||
|
||||
private static final Key<List<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";
|
||||
@@ -71,6 +70,8 @@ public class InferenceSession {
|
||||
private final PsiManager myManager;
|
||||
private int myConstraintIdx = 0;
|
||||
|
||||
private List<String> 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<ConstraintFormula> additionalConstraints = new LinkedHashSet<ConstraintFormula>();
|
||||
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<PsiTypeParameter, PsiType> 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<ConstraintFormula> additionalConstraints = new LinkedHashSet<ConstraintFormula>();
|
||||
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<PsiTypeParameter, PsiType> 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<String> errorMessage = myContext.getUserData(INFERENCE_FAILURE_MESSAGE);
|
||||
if (errorMessage == null) {
|
||||
errorMessage = Collections.synchronizedList(new ArrayList<String>());
|
||||
myContext.putUserData(INFERENCE_FAILURE_MESSAGE, errorMessage);
|
||||
}
|
||||
if (!errorMessage.contains(value)) {
|
||||
errorMessage.add(value);
|
||||
}
|
||||
if (myErrorMessages == null) {
|
||||
myErrorMessages = new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getInferenceErrorMessage(@NotNull PsiElement context) {
|
||||
final List<String> 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,
|
||||
|
||||
+14
@@ -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 extends Exception> E exception() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+4
@@ -335,6 +335,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testDoNotIgnoreConflictingUpperBounds() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user