new inference: provide diagnostics on failed inference (checked exceptions)

This commit is contained in:
Anna Kozlova
2015-11-24 10:30:21 +01:00
parent cf9c60e06b
commit e7d095d85f
4 changed files with 42 additions and 11 deletions
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceVariable;
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
@@ -67,10 +68,15 @@ public class CheckedExceptionCompatibilityConstraint extends InputOutputConstrai
}
if (myExpression instanceof PsiLambdaExpression || myExpression instanceof PsiMethodReferenceExpression) {
if (!LambdaUtil.isFunctionalType(myT)) {
session.registerIncompatibleErrorMessage(myT.getPresentableText() + " is not a functional interface");
return false;
}
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(myT);
final PsiType groundTargetType = myExpression instanceof PsiLambdaExpression ? FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT, (PsiLambdaExpression)myExpression, false)
: FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(groundTargetType);
if (interfaceMethod == null) {
session.registerIncompatibleErrorMessage("No valid function type can be found for " + myT.getPresentableText());
return false;
}
@@ -78,20 +84,28 @@ public class CheckedExceptionCompatibilityConstraint extends InputOutputConstrai
if (myExpression instanceof PsiLambdaExpression && !((PsiLambdaExpression)myExpression).hasFormalParameterTypes() ||
myExpression instanceof PsiMethodReferenceExpression && !((PsiMethodReferenceExpression)myExpression).isExact()) {
for (PsiParameter parameter : interfaceMethod.getParameterList().getParameters()) {
if (!session.isProperType(substitutor.substitute(parameter.getType()))) return false;
final PsiType type = session.substituteWithInferenceVariables(substitutor.substitute(parameter.getType()));
if (!session.isProperType(type)) {
session.registerIncompatibleErrorMessage("Parameter type is not yet inferred: " + type.getPresentableText());
return false;
}
}
}
final PsiType returnType = interfaceMethod.getReturnType();
if (myExpression instanceof PsiLambdaExpression || !((PsiMethodReferenceExpression)myExpression).isExact()) {
if (!session.isProperType(substitutor.substitute(returnType))) return false;
final PsiType type = session.substituteWithInferenceVariables(substitutor.substitute(returnType));
if (!session.isProperType(type)) {
session.registerIncompatibleErrorMessage("Return type is not yet inferred: " + type.getPresentableText());
return false;
}
}
final List<PsiType>
expectedThrownTypes = ContainerUtil.map(interfaceMethod.getThrowsList().getReferencedTypes(), new Function<PsiType, PsiType>() {
@Override
public PsiType fun(PsiType type) {
return substitutor.substitute(type);
return session.substituteWithInferenceVariables(substitutor.substitute(type));
}
});
final List<PsiType> expectedNonProperThrownTypes = new ArrayList<PsiType>();
@@ -1,5 +1,6 @@
package com.intellij.psi.impl.source.resolve.graphInference.constraints;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
@@ -13,6 +14,7 @@ import java.util.List;
* User: anna
*/
public class LambdaExpressionCompatibilityConstraint implements ConstraintFormula {
private static final Logger LOG = Logger.getInstance("#" + LambdaExpressionCompatibilityConstraint.class.getName());
private final PsiLambdaExpression myExpression;
private PsiType myT;
@@ -32,6 +34,7 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(groundTargetType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) {
session.registerIncompatibleErrorMessage("No valid function type can be found for " + myT.getPresentableText());
return false;
}
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, resolveResult);
@@ -39,16 +42,20 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
final PsiParameter[] lambdaParameters = myExpression.getParameterList().getParameters();
if (lambdaParameters.length != parameters.length) {
session.registerIncompatibleErrorMessage("Incompatible parameter types in lambda expression");
return false;
}
if (myExpression.hasFormalParameterTypes()) {
for (int i = 0; i < lambdaParameters.length; i++) {
constraints.add(new TypeEqualityConstraint(lambdaParameters[i].getType(), substitutor.substitute(parameters[i].getType())));
constraints.add(new TypeEqualityConstraint(lambdaParameters[i].getType(), session.substituteWithInferenceVariables(substitutor.substitute(parameters[i].getType()))));
}
constraints.add(new StrictSubtypingConstraint(myT, groundTargetType));
} else {
}
else {
for (PsiParameter parameter : parameters) {
if (!session.isProperType(session.substituteWithInferenceVariables(substitutor.substitute(parameter.getType())))) {
final PsiType type = session.substituteWithInferenceVariables(substitutor.substitute(parameter.getType()));
if (!session.isProperType(type)) {
//session.registerIncompatibleErrorMessage("Parameter type in not yet inferred: " + type.getPresentableText());
return false;
}
}
@@ -60,11 +67,13 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
final PsiElement lambdaBody = myExpression.getBody();
if (returnType.equals(PsiType.VOID)) {
if (!(lambdaBody instanceof PsiCodeBlock && myExpression.isVoidCompatible()) && !LambdaUtil.isExpressionStatementExpression(lambdaBody)) {
session.registerIncompatibleErrorMessage("Incompatible types: expected void but the lambda body is neither a statement expression nor a void-compatible block");
return false;
}
}
else {
if (lambdaBody instanceof PsiCodeBlock && !myExpression.isValueCompatible()) {
session.registerIncompatibleErrorMessage("Incompatible types: expected not void but the lambda body is a block that is not value-compatible");
return false;
}
InferenceSession callsession = session.getInferenceSessionContainer().findNestedCallSession(myExpression, session);
@@ -57,6 +57,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
final PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(groundTargetType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(classResolveResult);
if (interfaceMethod == null) {
session.registerIncompatibleErrorMessage("No valid function type can be found for " + myT.getPresentableText());
return false;
}
@@ -97,16 +98,20 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
constraints.add(new TypeCompatibilityConstraint(session.substituteWithInferenceVariables(psiSubstitutor.substitute(parameters[i - 1].getType())),
signature.getParameterTypes()[i]));
}
} else if (targetParameters.length == parameters.length) {
}
else if (targetParameters.length == parameters.length) {
for (int i = 0; i < targetParameters.length; i++) {
constraints.add(new TypeCompatibilityConstraint(session.substituteWithInferenceVariables(psiSubstitutor.substitute(parameters[i].getType())),
signature.getParameterTypes()[i]));
}
} else {
}
else {
session.registerIncompatibleErrorMessage("Incompatible parameter types in method reference expression");
return false;
}
if (!PsiType.VOID.equals(returnType) && returnType != null) {
if (PsiType.VOID.equals(applicableMethodReturnType)) {
session.registerIncompatibleErrorMessage("Incompatible types: expected not void but compile-time declaration for the method reference has void return type");
return false;
}
@@ -129,6 +134,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
for (PsiType paramType : signature.getParameterTypes()) {
if (!session.isProperType(paramType)) {
//session.registerIncompatibleErrorMessage("Parameter type in not yet inferred: " + type.getPresentableText());
return false;
}
}
@@ -146,6 +152,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
final PsiElement element = resolve.getElement();
if (element == null) {
session.registerIncompatibleErrorMessage("No compile-time declaration for the method reference is found");
return false;
}
@@ -187,6 +194,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
if (PsiType.VOID.equals(referencedMethodReturnType)) {
session.registerIncompatibleErrorMessage("Incompatible types: expected not void but compile-time declaration for the method reference has void return type");
return false;
}
@@ -20,7 +20,7 @@ class Test5 {
static <T> void bar(I<T> i){}
{
bar(() -> <error descr="Bad return type in lambda expression: <null> cannot be converted to void">null</error>);
bar(<error descr="Incompatible parameter types in lambda expression">() -> null</error>);
}
}
class Test6 {
@@ -31,7 +31,7 @@ class Test6 {
static <T> void bar(I<T> i){}
{
bar(() -> <error descr="Bad return type in lambda expression: <null> cannot be converted to void">null</error>);
bar(<error descr="Incompatible types: expected void but the lambda body is neither a statement expression nor a void-compatible block">() -> null</error>);
bar(() -> {});
}
}