[java-inspections] AnonymousCanBeLambdaInspection: suggest conversion if annotation on sub-method duplicates the super-method

In this case, we can assume that annotation-processing code is smart enough to use super-annotation.
Fixes IDEA-158731 "Anonymous Foo can be replaced with lambda" is not suggested when method is annotated

GitOrigin-RevId: de8c6f15e742b4ee4df02ecef5b230752d292114
This commit is contained in:
Tagir Valeev
2025-01-03 10:45:22 +01:00
committed by intellij-monorepo-bot
parent 0de99ed319
commit 8a84b67cb8
3 changed files with 53 additions and 16 deletions

View File

@@ -99,22 +99,27 @@ public final class AnonymousCanBeLambdaInspection extends AbstractBaseJavaLocalI
PsiAnnotation[] annotations = modifierList.getAnnotations();
for (PsiAnnotation annotation : annotations) {
PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
String fqn;
if (ref != null &&
ref.resolve() instanceof PsiClass annotationClass &&
((fqn = annotationClass.getQualifiedName()) == null ||
!runtimeAnnotationsToIgnore.contains(fqn))) {
final PsiAnnotation retentionAnno = AnnotationUtil.findAnnotation(annotationClass, Retention.class.getName());
// Default retention is CLASS: keep it
if (retentionAnno == null) return true;
if (retentionAnno.findAttributeValue("value") instanceof PsiReferenceExpression retentionValue &&
retentionValue.resolve() instanceof PsiField retentionField &&
(RetentionPolicy.RUNTIME.name().equals(retentionField.getName()) ||
RetentionPolicy.CLASS.name().equals(retentionField.getName()))) {
final PsiClass containingClass = retentionField.getContainingClass();
if (containingClass != null && RetentionPolicy.class.getName().equals(containingClass.getQualifiedName())) {
return true;
}
if (ref == null || !(ref.resolve() instanceof PsiClass annotationClass)) continue;
String fqn = annotationClass.getQualifiedName();
if (fqn != null && runtimeAnnotationsToIgnore.contains(fqn)) continue;
if (fqn != null && listOwner instanceof PsiMethod method &&
ContainerUtil.all(method.findSuperMethods(), sm -> {
PsiAnnotation superAnno = sm.getModifierList().findAnnotation(fqn);
return superAnno != null && AnnotationUtil.equal(superAnno, annotation);
})) {
// The same annotation exists in super method: assume that it's safe to omit it in sub-method
continue;
}
PsiAnnotation retentionAnno = AnnotationUtil.findAnnotation(annotationClass, Retention.class.getName());
// Default retention is CLASS: keep it
if (retentionAnno == null) return true;
if (retentionAnno.findAttributeValue("value") instanceof PsiReferenceExpression retentionValue &&
retentionValue.resolve() instanceof PsiField retentionField &&
(RetentionPolicy.RUNTIME.name().equals(retentionField.getName()) ||
RetentionPolicy.CLASS.name().equals(retentionField.getName()))) {
PsiClass containingClass = retentionField.getContainingClass();
if (containingClass != null && RetentionPolicy.class.getName().equals(containingClass.getQualifiedName())) {
return true;
}
}
}

View File

@@ -0,0 +1,14 @@
// "Replace with lambda" "true"
import org.jetbrains.annotations.Nullable;
interface Bar {}
interface Foo {
@Nullable
String foo(Bar bar);
}
class X {
Foo test() {
return bar -> null;
}
}

View File

@@ -0,0 +1,18 @@
// "Replace with lambda" "true"
import org.jetbrains.annotations.Nullable;
interface Bar {}
interface Foo {
@Nullable
String foo(Bar bar);
}
class X {
Foo test() {
return new <caret>Foo() {
@Nullable
@Override
public String foo(Bar bar) {return null;}
};
}
}