[java] [overload resolution] actively prune lambdas with wrong number of parameters (IDEA-279558)

GitOrigin-RevId: e6fdb4b920ec38c8f51cef14c33bc2c26321dd09
This commit is contained in:
Anna Kozlova
2021-10-05 11:59:35 +02:00
committed by intellij-monorepo-bot
parent 6698b10397
commit 1b3bddda3b
4 changed files with 60 additions and 49 deletions

View File

@@ -23,17 +23,26 @@ import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author anna
* Utilities around java 8 functional expressions.
*/
public final class LambdaUtil {
private static final Logger LOG = Logger.getInstance(LambdaUtil.class);
public static @Nullable PsiType getFunctionalInterfaceReturnType(PsiFunctionalExpression expr) {
/**
* @return substituted return type of expression's SAM method
*/
public static @Nullable PsiType getFunctionalInterfaceReturnType(@NotNull PsiFunctionalExpression expr) {
return getFunctionalInterfaceReturnType(expr.getFunctionalInterfaceType());
}
/**
* @return substituted return type of method which corresponds to the {@code functionalInterfaceType} SAM,
* null when {@code functionalInterfaceType} doesn't correspond to functional interface type
*/
public static @Nullable PsiType getFunctionalInterfaceReturnType(@Nullable PsiType functionalInterfaceType) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(normalizeFunctionalType(functionalInterfaceType));
PsiType functionalType = normalizeFunctionalType(functionalInterfaceType);
if (!(functionalType instanceof PsiClassType)) return null;
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalType);
final PsiClass psiClass = resolveResult.getElement();
if (psiClass != null) {
final MethodSignature methodSignature = getFunction(psiClass);
@@ -45,9 +54,26 @@ public final class LambdaUtil {
return null;
}
/**
* @return abstract method of SAM interface which corresponds to {@code functionalInterfaceType}, null otherwise
*/
@Contract("null -> null")
public static @Nullable PsiMethod getFunctionalInterfaceMethod(@Nullable PsiType functionalInterfaceType) {
return getFunctionalInterfaceMethod(PsiUtil.resolveGenericsClassInType(normalizeFunctionalType(functionalInterfaceType)));
return getFunctionalInterfaceMethod(PsiUtil.resolveClassInClassTypeOnly(normalizeFunctionalType(functionalInterfaceType)));
}
public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiElement element) {
return element instanceof PsiFunctionalExpression ? getFunctionalInterfaceMethod(((PsiFunctionalExpression)element).getFunctionalInterfaceType()) : null;
}
public static @Nullable PsiMethod getFunctionalInterfaceMethod(@NotNull PsiClassType.ClassResolveResult result) {
return getFunctionalInterfaceMethod(result.getElement());
}
@Contract("null -> null")
public static @Nullable PsiMethod getFunctionalInterfaceMethod(PsiClass aClass) {
final MethodSignature methodSignature = getFunction(aClass);
return methodSignature != null ? getMethod(aClass, methodSignature) : null;
}
/**
@@ -64,27 +90,6 @@ public final class LambdaUtil {
return functionalInterfaceType;
}
public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiElement element) {
if (element instanceof PsiFunctionalExpression) {
final PsiType samType = ((PsiFunctionalExpression)element).getFunctionalInterfaceType();
return getFunctionalInterfaceMethod(samType);
}
return null;
}
public static @Nullable PsiMethod getFunctionalInterfaceMethod(@NotNull PsiClassType.ClassResolveResult result) {
return getFunctionalInterfaceMethod(result.getElement());
}
@Contract("null -> null")
public static @Nullable PsiMethod getFunctionalInterfaceMethod(PsiClass aClass) {
final MethodSignature methodSignature = getFunction(aClass);
if (methodSignature != null) {
return getMethod(aClass, methodSignature);
}
return null;
}
public static PsiSubstitutor getSubstitutor(@NotNull PsiMethod method, @NotNull PsiClassType.ClassResolveResult resolveResult) {
final PsiClass derivedClass = resolveResult.getElement();
LOG.assertTrue(derivedClass != null);
@@ -106,11 +111,7 @@ public final class LambdaUtil {
@Contract("null -> false")
public static boolean isFunctionalClass(PsiClass aClass) {
if (aClass != null) {
if (aClass instanceof PsiTypeParameter) return false;
return getFunction(aClass) != null;
}
return false;
return getFunction(aClass) != null;
}
@Contract("null -> false")
@@ -173,14 +174,14 @@ public final class LambdaUtil {
}
@Contract("null -> null")
public static @Nullable MethodSignature getFunction(final PsiClass psiClass) {
public static @Nullable MethodSignature getFunction(@Nullable final PsiClass psiClass) {
if (isPlainInterface(psiClass)) {
return CachedValuesManager.getProjectPsiDependentCache(psiClass, LambdaUtil::calcFunction);
}
return null;
}
private static boolean isPlainInterface(PsiClass psiClass) {
private static boolean isPlainInterface(@Nullable PsiClass psiClass) {
return psiClass != null && psiClass.isInterface() && !psiClass.isAnnotationType();
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.infos;
import com.intellij.openapi.project.Project;
@@ -297,7 +297,13 @@ public class MethodCandidateInfo extends CandidateInfo{
return ThreeState.YES;
}
if (!LambdaUtil.isFunctionalType(formalType)) {
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(formalType);
if (interfaceMethod == null) {
return ThreeState.NO;
}
if (expression instanceof PsiLambdaExpression &&
((PsiLambdaExpression)expression).getParameterList().getParametersCount() != interfaceMethod.getParameterList().getParametersCount()) {
return ThreeState.NO;
}

View File

@@ -0,0 +1,17 @@
package p;
import java.util.function.BiFunction;
import java.util.function.Predicate;
class MyTest {
void m(Predicate<String> p) {}
void m(BiFunction<String, Integer, String> f) {}
{
m((s, i) -> {//m should be resolved here
if (s.equals("a")) {
}
<error descr="Missing return statement">}</error>);
}
}

View File

@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.daemon.lambda;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
@@ -281,6 +267,7 @@ public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase {
public void testPotentialCompatibilityWithArrayCreation() { doTest(false);}
public void testOverloadsWithOneNonCompatible() { doTest(false);}
public void testOverloadedConstructors() { doTest(false);}
public void testIncompleteLambdasWithDifferentSignatures() { doTest(false);}
public void testTwoFunctionalInterfacesWithVarargs() { doTest(false);}
public void testSecondSearchOverloadsBoxing() {
IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable());