[java-inspections] IDEA-328239 Casting with conjunction

- Check if SAM is implemented in conjunction
- check lambda Methods signature with generic from types

GitOrigin-RevId: 981b75d4417e08bb6e392a2ab78d6104de19b84f
This commit is contained in:
Mikhail Pyltsin
2023-08-29 22:10:43 +00:00
committed by intellij-monorepo-bot
parent 320f621750
commit bdf1d8226d
3 changed files with 108 additions and 15 deletions
@@ -7,16 +7,15 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.*;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.intellij.psi.LambdaUtil.getFunction;
public final class LambdaHighlightingUtil {
private static final Logger LOG = Logger.getInstance(LambdaHighlightingUtil.class);
@@ -73,17 +72,43 @@ public final class LambdaHighlightingUtil {
public static @NlsContexts.DetailedDescription String checkInterfaceFunctional(@NotNull PsiType functionalInterfaceType) {
if (functionalInterfaceType instanceof PsiIntersectionType) {
Set<MethodSignature> signatures = new HashSet<>();
MultiMap<MethodSignature, PsiType> signatures = new MultiMap<>();
for (PsiType type : ((PsiIntersectionType)functionalInterfaceType).getConjuncts()) {
if (checkInterfaceFunctional(type) == null) {
MethodSignature signature = LambdaUtil.getFunction(PsiUtil.resolveClassInType(type));
MethodSignature signature = getFunction(PsiUtil.resolveClassInType(type));
LOG.assertTrue(signature != null, type.getCanonicalText());
signatures.add(signature);
signatures.putValue(signature, type);
}
}
if (signatures.size() > 1) {
return JavaErrorBundle.message("multiple.non.overriding.abstract.methods.found.in.0", functionalInterfaceType.getPresentableText());
PsiType baseType = signatures.entrySet().iterator().next().getValue().iterator().next();
MethodSignature baseSignature = getLambdaSignatureWithCommonSubstitutor(baseType);
for (PsiType type : signatures.values()) {
if (baseType == type) {
continue;
}
if (!TypeConversionUtil.isAssignable(baseType, type) &&
!TypeConversionUtil.isAssignable(type, baseType)) {
return JavaErrorBundle.message("multiple.non.overriding.abstract.methods.found.in.0",
functionalInterfaceType.getPresentableText());
}
MethodSignature currentSignature = getLambdaSignatureWithCommonSubstitutor(baseType);
if (baseSignature == null ||
currentSignature == null ||
!MethodSignatureUtil.areSignaturesEqual(baseSignature, currentSignature)) {
return JavaErrorBundle.message("multiple.non.overriding.abstract.methods.found.in.0",
functionalInterfaceType.getPresentableText());
}
}
}
if (signatures.size() == 1) {
PsiType baseType = signatures.entrySet().iterator().next().getValue().iterator().next();
for (PsiType type : ((PsiIntersectionType)functionalInterfaceType).getConjuncts()) {
if (TypeConversionUtil.isAssignable(baseType, type) && getFunction(PsiUtil.resolveClassInType(type)) == null) {
return JavaErrorBundle.message("no.target.method.found");
}
}
}
return null;
}
@@ -91,14 +116,34 @@ public final class LambdaHighlightingUtil {
PsiClass aClass = resolveResult.getElement();
if (aClass != null) {
if (aClass instanceof PsiTypeParameter) return null; //should be logged as cyclic inference
MethodSignature functionalMethod = LambdaUtil.getFunction(aClass);
if (functionalMethod != null && functionalMethod.getTypeParameters().length > 0) return JavaErrorBundle
.message("target.method.is.generic");
MethodSignature functionalMethod = getFunction(aClass);
if (functionalMethod != null && functionalMethod.getTypeParameters().length > 0) {
return JavaErrorBundle
.message("target.method.is.generic");
}
return checkInterfaceFunctional(aClass);
}
return JavaErrorBundle.message("not.a.functional.interface",functionalInterfaceType.getPresentableText());
return JavaErrorBundle.message("not.a.functional.interface", functionalInterfaceType.getPresentableText());
}
@Nullable
private static MethodSignature getLambdaSignature(PsiType psiType) {
PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(psiType);
PsiClass aClass = classResolveResult.getElement();
if (aClass instanceof PsiTypeParameter) return null;
return LambdaUtil.getFunction(aClass);
}
@Nullable
private static MethodSignature getLambdaSignatureWithCommonSubstitutor(PsiType psiType) {
PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(psiType);
MethodSignature signatureFromFunction = getLambdaSignature(psiType);
if (signatureFromFunction == null) return null;
PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(psiType);
if (method == null) return null;
PsiSubstitutor fullSubstitutor = classResolveResult.getSubstitutor().putAll(signatureFromFunction.getSubstitutor());
return method.getSignature(fullSubstitutor);
}
static HighlightInfo.Builder checkConsistentParameterDeclaration(@NotNull PsiLambdaExpression expression) {
PsiParameter[] parameters = expression.getParameterList().getParameters();
if (parameters.length < 2) return null;
@@ -539,7 +539,18 @@ public final class LambdaUtil {
}
conjunct = psiType;
}
if (conjunct == null) {
return null;
}
for (PsiType typeConjunct : type.getConjuncts()) {
if (typeConjunct == conjunct) {
continue;
}
if (TypeConversionUtil.isAssignable(conjunct, typeConjunct) &&
getFunction(PsiUtil.resolveClassInClassTypeOnly(typeConjunct)) == null) {
return null;
}
}
return conjunct;
}
@@ -5,4 +5,41 @@ class Test {
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> foo() {
return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
public static void main(String... args) {
var shouldCompile = (Consumer<Integer> & IntConsumerAdapter1)
i -> System.out.println("Cons3uming dd" + i);
System.out.println(shouldCompile.getClass());
shouldCompile.accept(42);
shouldCompile.accept(Integer.valueOf(52));
var shouldNotCompile = (Consumer<Integer> & IntConsumerAdapter2)
<error descr="No target method found">i -> System.out.println("Consumins2gs " + i)</error>;
shouldNotCompile.accept(42);
shouldNotCompile.accept(Integer.valueOf(52));
}
public interface Consumer<T> {
void accept(T t);
}
public interface IntConsumer {
void accept(int value);
}
@FunctionalInterface
interface IntConsumerAdapter1 extends IntConsumer, Consumer<Integer> {
default void accept(int value) {
accept(Integer.valueOf(value));
}
}
interface IntConsumerAdapter2 extends IntConsumer, Consumer<Integer> {
default void accept(int value) {
accept(Integer.valueOf(value));
}
default void accept(Integer integer) {
}
}
}