pertinent to applicability: reject nested lambdas if they target non proper type

This commit is contained in:
Anna Kozlova
2015-02-13 13:55:55 +01:00
parent 4c109a6316
commit 925e9d1610
8 changed files with 42 additions and 21 deletions

View File

@@ -45,7 +45,7 @@ public class LambdaHighlightingUtil {
if (signatures.size() == 1) {
return null;
}
return "Multiple non-overriding abstract methods found";
return "Multiple non-overriding abstract methods found in interface " + HighlightUtil.formatClass(psiClass);
}
@Nullable

View File

@@ -165,16 +165,20 @@ public class InferenceSession {
7) A conditional expression (15.25) whose second or third operand is not pertinent to applicability.
*/
public static boolean isPertinentToApplicability(PsiExpression expr, PsiMethod method) {
return isPertinentToApplicability(expr, method, null);
}
private static boolean isPertinentToApplicability(PsiExpression expr, PsiMethod method, PsiType expectedReturnType) {
if (expr instanceof PsiLambdaExpression && ((PsiLambdaExpression)expr).hasFormalParameterTypes() ||
expr instanceof PsiMethodReferenceExpression && ((PsiMethodReferenceExpression)expr).isExact()) {
if (method != null && method.getTypeParameters().length > 0) {
final PsiElement parent = PsiUtil.skipParenthesizedExprUp(expr.getParent());
PsiType paramType = null;
if (parent instanceof PsiExpressionList) {
final PsiElement gParent = parent.getParent();
if (gParent instanceof PsiCallExpression && ((PsiCallExpression)gParent).getTypeArgumentList().getTypeParameterElements().length == 0) {
final int idx = LambdaUtil.getLambdaIdx(((PsiExpressionList)parent), expr);
final PsiParameter[] parameters = method.getParameterList().getParameters();
PsiType paramType;
if (idx > parameters.length - 1) {
final PsiType lastParamType = parameters[parameters.length - 1].getType();
paramType = parameters[parameters.length - 1].isVarArgs() ? ((PsiEllipsisType)lastParamType).getComponentType() : lastParamType;
@@ -182,20 +186,24 @@ public class InferenceSession {
else {
paramType = parameters[idx].getType();
}
final PsiClass psiClass = PsiUtil.resolveClassInType(paramType); //accept ellipsis here
if (psiClass instanceof PsiTypeParameter && ((PsiTypeParameter)psiClass).getOwner() == method) return false;
if (isTypeParameterType(method, paramType)) return false;
}
}
else if (expectedReturnType != null && parent instanceof PsiLambdaExpression) {
if (isTypeParameterType(method, expectedReturnType)) return false;
paramType = expectedReturnType;
}
if (expr instanceof PsiLambdaExpression) {
for (PsiExpression expression : LambdaUtil.getReturnExpressions((PsiLambdaExpression)expr)) {
if (!isPertinentToApplicability(expression, method, LambdaUtil.getFunctionalInterfaceReturnType(paramType))) return false;
}
return true;
}
}
}
if (expr instanceof PsiLambdaExpression) {
if (!((PsiLambdaExpression)expr).hasFormalParameterTypes()) {
return false;
}
for (PsiExpression expression : LambdaUtil.getReturnExpressions((PsiLambdaExpression)expr)) {
if (!isPertinentToApplicability(expression, method)) return false;
}
return true;
return ((PsiLambdaExpression)expr).hasFormalParameterTypes();
}
if (expr instanceof PsiMethodReferenceExpression) {
return ((PsiMethodReferenceExpression)expr).isExact();
@@ -212,6 +220,12 @@ public class InferenceSession {
return true;
}
private static boolean isTypeParameterType(PsiMethod method, PsiType paramType) {
final PsiClass psiClass = PsiUtil.resolveClassInType(paramType); //accept ellipsis here
if (psiClass instanceof PsiTypeParameter && ((PsiTypeParameter)psiClass).getOwner() == method) return true;
return false;
}
private static PsiType getParameterType(PsiParameter[] parameters, int i, @Nullable PsiSubstitutor substitutor, boolean varargs) {
if (substitutor == null) return null;
PsiType parameterType = substitutor.substitute(parameters[i < parameters.length ? i : parameters.length - 1].getType());

View File

@@ -22,9 +22,6 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
@Override
public boolean reduce(InferenceSession session, List<ConstraintFormula> constraints) {
if (session.getInferenceVariable(myT) != null) {
return true;
}
if (!LambdaUtil.isFunctionalType(myT)) {
return false;
}

View File

@@ -1,4 +1,4 @@
<error descr="Multiple non-overriding abstract methods found">@FunctionalInterface</error>
<error descr="Multiple non-overriding abstract methods found in interface Test">@FunctionalInterface</error>
interface Test {
void foo();
void bar();

View File

@@ -36,7 +36,7 @@ class Test2 {
}
{
F f = <error descr="Multiple non-overriding abstract methods found">() -> g()</error>;
F f = <error descr="Multiple non-overriding abstract methods found in interface Test2.F">() -> g()</error>;
}
void g() {}

View File

@@ -8,7 +8,7 @@ public class NotAFIT {
}
void bar() {
foo(<error descr="Multiple non-overriding abstract methods found">() ->{}</error>);
foo(<error descr="Multiple non-overriding abstract methods found in interface NotAFIT.First.A">() ->{}</error>);
}
}
@@ -25,7 +25,7 @@ public class NotAFIT {
}
void bar() {
foo(<error descr="Multiple non-overriding abstract methods found">()->{}</error>);
foo(<error descr="Multiple non-overriding abstract methods found in interface NotAFIT.WithInheritance.B">()->{}</error>);
}
}

View File

@@ -9,3 +9,13 @@ class Test {
return null;
}
}
class Test1 {
{
Supplier<Runnable> x = foo(() -> <error descr="Multiple non-overriding abstract methods found in interface java.util.List">() -> null</error>);
}
static <T> Supplier<T> foo(Supplier<java.util.List<T>> delegate) {
return null;
}
}

View File

@@ -52,7 +52,7 @@ public class FunctionalInterfaceTest extends LightDaemonAnalyzerTestCase {
}
public void testClone() throws Exception {
doTestFunctionalInterface("Multiple non-overriding abstract methods found");
doTestFunctionalInterface("Multiple non-overriding abstract methods found in interface Foo");
}
public void testTwoMethodsSameSignature() throws Exception {
@@ -64,11 +64,11 @@ public class FunctionalInterfaceTest extends LightDaemonAnalyzerTestCase {
}
public void testTwoMethodsNoSubSignature() throws Exception {
doTestFunctionalInterface("Multiple non-overriding abstract methods found");
doTestFunctionalInterface("Multiple non-overriding abstract methods found in interface Foo");
}
public void testTwoMethodsNoSubSignature1() throws Exception {
doTestFunctionalInterface("Multiple non-overriding abstract methods found");
doTestFunctionalInterface("Multiple non-overriding abstract methods found in interface Foo");
}
public void testTwoMethodsSameSubstSignature() throws Exception {