new inference: register incompatible message if inference fails (IDEA-131882; to be extended); filter out candidates if inference had failed for them

This commit is contained in:
Anna Kozlova
2015-11-19 19:57:14 +01:00
parent 9eda05cba4
commit b4396c8f98
9 changed files with 82 additions and 19 deletions

View File

@@ -372,10 +372,7 @@ public class InferenceSession {
}
mySiteSubstitutor = mySiteSubstitutor.put(param, mapping);
}
} else {
return prepareSubstitution();
}
return prepareSubstitution();
}
finally {
@@ -1111,16 +1108,30 @@ public class InferenceSession {
private PsiType registerIncompatibleErrorMessage(InferenceVariable var, @NotNull String incompatibleBoundsMessage) {
if (var.getCallContext() == myContext) {
if (myErrorMessages == null) {
myErrorMessages = new ArrayList<String>();
}
if (!myErrorMessages.contains(incompatibleBoundsMessage)) {
myErrorMessages.add(incompatibleBoundsMessage);
}
registerIncompatibleErrorMessage(incompatibleBoundsMessage);
}
return PsiType.NULL;
}
public void registerIncompatibleErrorMessage(Collection<InferenceVariable> variables, String incompatibleTypesMessage) {
final String variablesEnumeration = StringUtil.join(variables, new Function<InferenceVariable, String>() {
@Override
public String fun(InferenceVariable variable) {
return variable.getName();
}
}, ", ");
registerIncompatibleErrorMessage("no instance(s) of type variable(s) " + variablesEnumeration + " exist so that " + incompatibleTypesMessage);
}
public void registerIncompatibleErrorMessage(@NotNull String incompatibleBoundsMessage) {
if (myErrorMessages == null) {
myErrorMessages = new ArrayList<String>();
}
if (!myErrorMessages.contains(incompatibleBoundsMessage)) {
myErrorMessages.add(incompatibleBoundsMessage);
}
}
private String incompatibleBoundsMessage(final InferenceVariable var,
final PsiSubstitutor substitutor,
final InferenceBound lowBound,
@@ -1778,4 +1789,8 @@ public class InferenceSession {
public void registerSiteSubstitutor(PsiSubstitutor substitutor) {
mySiteSubstitutor = mySiteSubstitutor.putAll(substitutor);
}
public List<String> getIncompatibleErrorMessages() {
return myErrorMessages;
}
}

View File

@@ -142,17 +142,26 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
callSession.initExpressionConstraints(parameters, args, expression, method, InferenceSession
.chooseVarargsMode(candidateProperties, resolveResult));
}
final boolean accepted = callSession.repeatInferencePhases(true);
if (!accepted) {
return null;
}
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), targetType);
if (callSession.repeatInferencePhases(true)) {
return callSession;
if (PsiType.VOID.equals(targetType)) {
return callSession;
}
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), targetType);
if (callSession.repeatInferencePhases(true)) {
return callSession;
}
}
else {
return null;
//copy incompatible message if any
final List<String> messages = callSession.getIncompatibleErrorMessages();
if (messages != null) {
for (String message : messages) {
session.registerIncompatibleErrorMessage(message);
}
}
return null;
}
}
return session;

View File

@@ -22,6 +22,7 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
@Override
public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) {
if (!LambdaUtil.isFunctionalType(myT)) {
session.registerIncompatibleErrorMessage(myT.getPresentableText() + " is not a functional interface");
return false;
}

View File

@@ -49,6 +49,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
@Override
public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) {
if (!LambdaUtil.isFunctionalType(myT)) {
session.registerIncompatibleErrorMessage(myT.getPresentableText() + " is not a functional interface");
return false;
}

View File

@@ -22,6 +22,7 @@ import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.TypeConversionUtil;
import java.util.HashSet;
import java.util.List;
/**
@@ -45,12 +46,21 @@ public class StrictSubtypingConstraint implements ConstraintFormula {
@Override
public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) {
if (session.isProperType(myS) && session.isProperType(myT)) {
final HashSet<InferenceVariable> dependencies = new HashSet<InferenceVariable>();
if (!session.collectDependencies(myS, dependencies) && !session.collectDependencies(myT, dependencies)) {
if (myT == null) return myS == null || myS.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
if (myS == null) return true;
return TypeConversionUtil.isAssignable(myT, myS);
}
final boolean reduceResult = nonProperReduce(session, constraints);
if (!reduceResult) {
session.registerIncompatibleErrorMessage(dependencies, myS.getPresentableText() + " conforms to " + myT.getPresentableText());
}
return reduceResult;
}
private boolean nonProperReduce(InferenceSession session, List<ConstraintFormula> constraints) {
if (PsiType.NULL.equals(myT) || myT == null) return false;
if (PsiType.NULL.equals(myS) || myS == null || myT.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return true;

View File

@@ -63,6 +63,14 @@ public class SubtypingConstraint implements ConstraintFormula {
@Override
public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) {
final boolean reduceResult = doReduce(constraints);
if (!reduceResult) {
session.registerIncompatibleErrorMessage(session.getInferenceVariables(), myS.getPresentableText() + " can be converted to " + myT.getPresentableText());
}
return reduceResult;
}
private boolean doReduce(List<ConstraintFormula> constraints) {
if (myT instanceof PsiWildcardType) {
PsiType tBound = ((PsiWildcardType)myT).getBound();
if (tBound == null) {

View File

@@ -109,6 +109,7 @@ public class TypeEqualityConstraint implements ConstraintFormula {
return true;
}
session.registerIncompatibleErrorMessage(session.getInferenceVariables(), myS.getPresentableText() + " conforms to " + myT.getPresentableText());
return false;
}

View File

@@ -103,6 +103,10 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
return key.getSubstitutor(false);
}
};
checkInvocationApplicabilityInference(conflicts, map);
if (conflicts.size() == 1) return conflicts.get(0);
checkSameSignatures(conflicts, map);
if (conflicts.size() == 1) return conflicts.get(0);
@@ -136,6 +140,20 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
return null;
}
private static void checkInvocationApplicabilityInference(@NotNull List<CandidateInfo> conflicts,
FactoryMap<MethodCandidateInfo, PsiSubstitutor> map) {
for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext(); ) {
CandidateInfo conflict = iterator.next();
if (conflict instanceof MethodCandidateInfo) {
getSubstitutor((MethodCandidateInfo)conflict, map);
final String errorMessage = ((MethodCandidateInfo)conflict).getInferenceErrorMessage();
if (errorMessage != null) {
iterator.remove();
}
}
}
}
public void checkSpecifics(@NotNull List<CandidateInfo> conflicts,
@MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel,
@NotNull LanguageLevel languageLevel) {

View File

@@ -1,6 +1,6 @@
class Test {
{
asList(<error descr="Cyclic inference">o -> {}</error>, 1, 2, 3);
asList(<error descr="Integer is not a functional interface">o -> {}</error>, 1, 2, 3);
asList(<error descr="T is not a functional interface">Test::foo</error>, 1, 2, 3);
}