From 3072396ff6502d1c41a98f7eeeda4d8347ba9347 Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Mon, 28 Aug 2023 18:28:31 +0200 Subject: [PATCH] [java-inspections] IDEA-328239 Casting with conjunction - Check if SAM is implemented in conjunction - Created lambda Methods signature with generic from types GitOrigin-RevId: 97e21dbacccd9382e48387295a911b127882e625 --- .../impl/analysis/LambdaHighlightingUtil.java | 12 +++- .../src/com/intellij/psi/LambdaUtil.java | 64 +++++++++++++++++-- .../highlighting/IntersectionTypeInCast.java | 37 +++++++++++ 3 files changed, 103 insertions(+), 10 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/LambdaHighlightingUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/LambdaHighlightingUtil.java index 2eb307a6cb44..1cf64fca73c2 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/LambdaHighlightingUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/LambdaHighlightingUtil.java @@ -75,17 +75,23 @@ public final class LambdaHighlightingUtil { if (functionalInterfaceType instanceof PsiIntersectionType) { Set signatures = new HashSet<>(); for (PsiType type : ((PsiIntersectionType)functionalInterfaceType).getConjuncts()) { - if (checkInterfaceFunctional(type) == null) { - MethodSignature signature = LambdaUtil.getFunction(PsiUtil.resolveClassInType(type)); + String error = checkInterfaceFunctional(type); + if (error == null) { + MethodSignature signature = LambdaUtil.getLambdaSignatureWithCommonSubstitutor(type); LOG.assertTrue(signature != null, type.getCanonicalText()); signatures.add(signature); } + else { + if (!error.equals(JavaErrorBundle.message("target.method.is.generic"))) { + return error; + } + } } if (signatures.size() > 1) { return JavaErrorBundle.message("multiple.non.overriding.abstract.methods.found.in.0", functionalInterfaceType.getPresentableText()); } - return null; + return signatures.isEmpty() ? JavaErrorBundle.message("not.a.functional.interface",functionalInterfaceType.getPresentableText()) : null; } PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); PsiClass aClass = resolveResult.getElement(); diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index b93d0d67eef4..c68d6d2ad96c 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -526,16 +526,48 @@ public final class LambdaUtil { private static @Nullable PsiType extractFunctionalConjunct(PsiIntersectionType type) { PsiType conjunct = null; MethodSignature commonSignature = null; + PsiType commonSignatureType = null; + for (PsiType psiType : type.getConjuncts()) { - PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(psiType); - if (aClass instanceof PsiTypeParameter) continue; - MethodSignature signature = getFunction(aClass); + MethodSignature signature = getLambdaSignature(psiType); if (signature == null) continue; - if (commonSignature == null) { - commonSignature = signature; + commonSignature = signature; + commonSignatureType = psiType; + conjunct = psiType; + break; + } + if (commonSignature == null) { + return null; + } + for (PsiType psiType : type.getConjuncts()) { + if (psiType == commonSignatureType) continue; + PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(psiType); + PsiClass aClass = classResolveResult.getElement(); + if (aClass instanceof PsiTypeParameter) continue; + MethodSignature signature = getLambdaSignature(psiType); + if (signature == null) { + //psiType is child and doesn't have function methods, so the method is implemented + if (TypeConversionUtil.isAssignable(commonSignatureType, psiType)) { + return null; + } + else { + continue; + } } - else if (!MethodSignatureUtil.areSignaturesEqual(commonSignature, signature)) { - return null; + if (!MethodSignatureUtil.areSignaturesEqual(commonSignature, signature)) { + if (TypeConversionUtil.isAssignable(commonSignatureType, psiType) || + TypeConversionUtil.isAssignable(psiType, commonSignatureType)) { + MethodSignature signatureWithInheritance = getLambdaSignatureWithCommonSubstitutor(psiType); + MethodSignature commonSignatureWithInheritance = getLambdaSignatureWithCommonSubstitutor(commonSignatureType); + if (commonSignatureWithInheritance == null || + signatureWithInheritance == null || + !MethodSignatureUtil.areSignaturesEqual(commonSignatureWithInheritance, signatureWithInheritance)) { + return null; + } + } + else { + return null; + } } conjunct = psiType; } @@ -543,6 +575,24 @@ public final class LambdaUtil { return conjunct; } + @Nullable + public static MethodSignature getLambdaSignatureWithCommonSubstitutor(PsiType psiType) { + PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(psiType); + MethodSignature signatureFromFunction = getLambdaSignature(psiType); + if (signatureFromFunction == null) return null; + PsiMethod method = getFunctionalInterfaceMethod(psiType); + if (method == null) return null; + PsiSubstitutor fullSubstitutor = classResolveResult.getSubstitutor().putAll(signatureFromFunction.getSubstitutor()); + return method.getSignature(fullSubstitutor); + } + @Nullable + public static MethodSignature getLambdaSignature(PsiType psiType) { + PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(psiType); + PsiClass aClass = classResolveResult.getElement(); + if (aClass instanceof PsiTypeParameter) return null; + return getFunction(aClass); + } + private static PsiType getFunctionalInterfaceTypeByContainingLambda(@NotNull PsiLambdaExpression parentLambda) { final PsiType parentInterfaceType = parentLambda.getFunctionalInterfaceType(); return parentInterfaceType != null ? getFunctionalInterfaceReturnType(parentInterfaceType) : null; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IntersectionTypeInCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IntersectionTypeInCast.java index 49a261c8b8de..e45bd9f2abba 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IntersectionTypeInCast.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/IntersectionTypeInCast.java @@ -5,4 +5,41 @@ class Test { public static , V> Comparator> foo() { return (Comparator> & Serializable)(c1, c2) -> c1.getKey().compareTo(c2.getKey()); } + public static void main(String... args) { + var shouldCompile = (Consumer & IntConsumerAdapter1) + i -> System.out.println("Cons3uming dd" + i); + System.out.println(shouldCompile.getClass()); + shouldCompile.accept(42); + shouldCompile.accept(Integer.valueOf(52)); + + var shouldNotCompile = (Consumer & IntConsumerAdapter2) + i -> System.out.println("Consumins2gs " + i); + shouldNotCompile.accept(42); + shouldNotCompile.accept(Integer.valueOf(52)); + } + + public interface Consumer { + void accept(T t); + } + + public interface IntConsumer { + + void accept(int value); + } + + @FunctionalInterface + interface IntConsumerAdapter1 extends IntConsumer, Consumer { + default void accept(int value) { + accept(Integer.valueOf(value)); + } + } + + interface IntConsumerAdapter2 extends IntConsumer, Consumer { + default void accept(int value) { + accept(Integer.valueOf(value)); + } + + default void accept(Integer integer) { + } + } }