Skip to content

Commit df6793f

Browse files
committed
Java inspections and refactoring processors refactoring (part 1).
1 parent e86cece commit df6793f

10 files changed

Lines changed: 1592 additions & 1305 deletions

File tree

java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInspection/AnonymousCanBeLambdaInspection.java

Lines changed: 548 additions & 480 deletions
Large diffs are not rendered by default.

java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInspection/AnonymousCanBeMethodReferenceInspection.java

Lines changed: 155 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.intellij.java.language.psi.*;
2020
import com.intellij.java.language.psi.codeStyle.JavaCodeStyleManager;
2121
import com.intellij.java.language.psi.util.RedundantCastUtil;
22+
import consulo.annotation.access.RequiredReadAction;
23+
import consulo.annotation.access.RequiredWriteAction;
2224
import consulo.annotation.component.ExtensionImpl;
2325
import consulo.document.util.TextRange;
2426
import consulo.language.editor.inspection.*;
@@ -32,9 +34,9 @@
3234
import consulo.logging.Logger;
3335
import consulo.project.Project;
3436
import consulo.util.collection.ContainerUtil;
35-
import org.jetbrains.annotations.Nls;
3637

3738
import jakarta.annotation.Nonnull;
39+
3840
import java.util.Collection;
3941
import java.util.Collections;
4042

@@ -43,138 +45,167 @@
4345
*/
4446
@ExtensionImpl
4547
public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalInspectionTool<AnonymousCanBeMethodReferenceInspectionState> {
46-
private static final Logger LOG = Logger.getInstance(AnonymousCanBeMethodReferenceInspection.class);
47-
48-
49-
@Nonnull
50-
@Override
51-
public HighlightDisplayLevel getDefaultLevel() {
52-
return HighlightDisplayLevel.WARNING;
53-
}
54-
55-
@Nonnull
56-
@Override
57-
public LocalizeValue getGroupDisplayName() {
58-
return InspectionLocalize.groupNamesLanguageLevelSpecificIssuesAndMigrationAids();
59-
}
60-
61-
@Nls
62-
@Nonnull
63-
@Override
64-
public LocalizeValue getDisplayName() {
65-
return LocalizeValue.localizeTODO("Anonymous type can be replaced with method reference");
66-
}
67-
68-
@Override
69-
public boolean isEnabledByDefault() {
70-
return true;
71-
}
72-
73-
@Nonnull
74-
@Override
75-
public String getShortName() {
76-
return "Anonymous2MethodRef";
77-
}
78-
79-
@Nonnull
80-
@Override
81-
public InspectionToolState<? extends AnonymousCanBeMethodReferenceInspectionState> createStateProvider() {
82-
return new AnonymousCanBeMethodReferenceInspectionState();
83-
}
84-
85-
@Nonnull
86-
@Override
87-
public PsiElementVisitor buildVisitorImpl(@Nonnull final ProblemsHolder holder,
88-
boolean isOnTheFly,
89-
LocalInspectionToolSession session,
90-
AnonymousCanBeMethodReferenceInspectionState state) {
91-
return new JavaElementVisitor() {
92-
@Override
93-
public void visitAnonymousClass(PsiAnonymousClass aClass) {
94-
super.visitAnonymousClass(aClass);
95-
if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(aClass, true, state.reportNotAnnotatedInterfaces, Collections.emptySet())) {
96-
final PsiMethod method = aClass.getMethods()[0];
97-
final PsiCodeBlock body = method.getBody();
98-
PsiExpression lambdaBodyCandidate = LambdaCanBeMethodReferenceInspection.extractMethodReferenceCandidateExpression(body, false);
99-
final PsiExpression methodRefCandidate = LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(method.getParameterList().getParameters(), aClass.getBaseClassType(),
100-
aClass.getParent(), lambdaBodyCandidate);
101-
if (methodRefCandidate instanceof PsiCallExpression) {
102-
final PsiCallExpression callExpression = (PsiCallExpression) methodRefCandidate;
103-
final PsiMethod resolveMethod = callExpression.resolveMethod();
104-
if (resolveMethod != method && !AnonymousCanBeLambdaInspection.functionalInterfaceMethodReferenced(resolveMethod, aClass, callExpression)) {
105-
final PsiElement parent = aClass.getParent();
106-
if (parent instanceof PsiNewExpression) {
107-
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression) parent).getClassOrAnonymousClassReference();
108-
if (classReference != null) {
109-
final PsiElement lBrace = aClass.getLBrace();
110-
LOG.assertTrue(lBrace != null);
111-
final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent());
112-
ProblemHighlightType highlightType = LambdaCanBeMethodReferenceInspection.checkQualifier(lambdaBodyCandidate) ? ProblemHighlightType.LIKE_UNUSED_SYMBOL :
113-
ProblemHighlightType.INFORMATION;
114-
holder.registerProblem(parent, "Anonymous #ref #loc can be replaced with method reference", highlightType, rangeInElement, new ReplaceWithMethodRefFix());
115-
}
116-
}
117-
}
118-
}
119-
}
120-
}
121-
};
122-
}
48+
private static final Logger LOG = Logger.getInstance(AnonymousCanBeMethodReferenceInspection.class);
49+
12350

124-
private static class ReplaceWithMethodRefFix implements LocalQuickFix {
12551
@Nonnull
12652
@Override
127-
public LocalizeValue getName() {
128-
return LocalizeValue.localizeTODO("Replace with method reference");
53+
public HighlightDisplayLevel getDefaultLevel() {
54+
return HighlightDisplayLevel.WARNING;
12955
}
13056

57+
@Nonnull
13158
@Override
132-
public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) {
133-
final PsiElement element = descriptor.getPsiElement();
134-
if (element instanceof PsiNewExpression) {
135-
final PsiAnonymousClass anonymousClass = ((PsiNewExpression) element).getAnonymousClass();
136-
if (anonymousClass == null) {
137-
return;
138-
}
139-
final PsiMethod[] methods = anonymousClass.getMethods();
140-
if (methods.length != 1) {
141-
return;
142-
}
59+
public LocalizeValue getGroupDisplayName() {
60+
return InspectionLocalize.groupNamesLanguageLevelSpecificIssuesAndMigrationAids();
61+
}
14362

144-
final PsiParameter[] parameters = methods[0].getParameterList().getParameters();
145-
final PsiType functionalInterfaceType = anonymousClass.getBaseClassType();
146-
PsiExpression methodRefCandidate = LambdaCanBeMethodReferenceInspection.extractMethodReferenceCandidateExpression(methods[0].getBody(), false);
147-
final PsiExpression candidate = LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(parameters, functionalInterfaceType, anonymousClass.getParent(), methodRefCandidate);
63+
@Nonnull
64+
@Override
65+
public LocalizeValue getDisplayName() {
66+
return LocalizeValue.localizeTODO("Anonymous type can be replaced with method reference");
67+
}
14868

149-
final String methodRefText = LambdaCanBeMethodReferenceInspection.createMethodReferenceText(candidate, functionalInterfaceType, parameters);
69+
@Override
70+
public boolean isEnabledByDefault() {
71+
return true;
72+
}
73+
74+
@Nonnull
75+
@Override
76+
public String getShortName() {
77+
return "Anonymous2MethodRef";
78+
}
15079

151-
replaceWithMethodReference(project, methodRefText, anonymousClass.getBaseClassType(), anonymousClass.getParent());
152-
}
80+
@Nonnull
81+
@Override
82+
public InspectionToolState<? extends AnonymousCanBeMethodReferenceInspectionState> createStateProvider() {
83+
return new AnonymousCanBeMethodReferenceInspectionState();
15384
}
154-
}
155-
156-
static void replaceWithMethodReference(@Nonnull Project project, String methodRefText, PsiType castType, PsiElement replacementTarget) {
157-
final Collection<PsiComment> comments = ContainerUtil.map(PsiTreeUtil.findChildrenOfType(replacementTarget, PsiComment.class), comment -> (PsiComment) comment.copy());
158-
159-
if (methodRefText != null) {
160-
final String canonicalText = castType.getCanonicalText();
161-
final PsiExpression psiExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText("(" + canonicalText + ")" + methodRefText, replacementTarget);
162-
163-
PsiElement castExpr = replacementTarget.replace(psiExpression);
164-
if (RedundantCastUtil.isCastRedundant((PsiTypeCastExpression) castExpr)) {
165-
final PsiExpression operand = ((PsiTypeCastExpression) castExpr).getOperand();
166-
LOG.assertTrue(operand != null);
167-
castExpr = castExpr.replace(operand);
168-
}
169-
170-
PsiElement anchor = PsiTreeUtil.getParentOfType(castExpr, PsiStatement.class);
171-
if (anchor == null) {
172-
anchor = castExpr;
173-
}
174-
for (PsiComment comment : comments) {
175-
anchor.getParent().addBefore(comment, anchor);
176-
}
177-
JavaCodeStyleManager.getInstance(project).shortenClassReferences(castExpr);
85+
86+
@Nonnull
87+
@Override
88+
public PsiElementVisitor buildVisitorImpl(
89+
@Nonnull final ProblemsHolder holder,
90+
boolean isOnTheFly,
91+
LocalInspectionToolSession session,
92+
AnonymousCanBeMethodReferenceInspectionState state
93+
) {
94+
return new JavaElementVisitor() {
95+
@Override
96+
@RequiredReadAction
97+
public void visitAnonymousClass(@Nonnull PsiAnonymousClass aClass) {
98+
super.visitAnonymousClass(aClass);
99+
if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(
100+
aClass,
101+
true,
102+
state.reportNotAnnotatedInterfaces,
103+
Collections.emptySet()
104+
)) {
105+
PsiMethod method = aClass.getMethods()[0];
106+
PsiCodeBlock body = method.getBody();
107+
PsiExpression lambdaBodyCandidate =
108+
LambdaCanBeMethodReferenceInspection.extractMethodReferenceCandidateExpression(body, false);
109+
PsiExpression methodRefCandidate = LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(
110+
method.getParameterList().getParameters(),
111+
aClass.getBaseClassType(),
112+
aClass.getParent(),
113+
lambdaBodyCandidate
114+
);
115+
if (methodRefCandidate instanceof PsiCallExpression call) {
116+
PsiMethod resolveMethod = call.resolveMethod();
117+
if (resolveMethod != method
118+
&& !AnonymousCanBeLambdaInspection.functionalInterfaceMethodReferenced(resolveMethod, aClass, call)
119+
&& aClass.getParent() instanceof PsiNewExpression newExpr) {
120+
PsiJavaCodeReferenceElement classReference = newExpr.getClassOrAnonymousClassReference();
121+
if (classReference != null) {
122+
PsiElement lBrace = aClass.getLBrace();
123+
LOG.assertTrue(lBrace != null);
124+
TextRange rangeInElement =
125+
new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent());
126+
ProblemHighlightType highlightType =
127+
LambdaCanBeMethodReferenceInspection.checkQualifier(lambdaBodyCandidate)
128+
? ProblemHighlightType.LIKE_UNUSED_SYMBOL
129+
: ProblemHighlightType.INFORMATION;
130+
holder.newProblem(LocalizeValue.localizeTODO("Anonymous #ref #loc can be replaced with method reference"))
131+
.range(newExpr, rangeInElement)
132+
.highlightType(highlightType)
133+
.withFix(new ReplaceWithMethodRefFix())
134+
.create();
135+
}
136+
}
137+
}
138+
}
139+
}
140+
};
141+
}
142+
143+
private static class ReplaceWithMethodRefFix implements LocalQuickFix {
144+
@Nonnull
145+
@Override
146+
public LocalizeValue getName() {
147+
return LocalizeValue.localizeTODO("Replace with method reference");
148+
}
149+
150+
@Override
151+
@RequiredWriteAction
152+
public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) {
153+
if (descriptor.getPsiElement() instanceof PsiNewExpression newExpression) {
154+
PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass();
155+
if (anonymousClass == null) {
156+
return;
157+
}
158+
PsiMethod[] methods = anonymousClass.getMethods();
159+
if (methods.length != 1) {
160+
return;
161+
}
162+
163+
PsiParameter[] parameters = methods[0].getParameterList().getParameters();
164+
PsiType functionalInterfaceType = anonymousClass.getBaseClassType();
165+
PsiExpression methodRefCandidate =
166+
LambdaCanBeMethodReferenceInspection.extractMethodReferenceCandidateExpression(methods[0].getBody(), false);
167+
PsiExpression candidate = LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(
168+
parameters,
169+
functionalInterfaceType,
170+
anonymousClass.getParent(),
171+
methodRefCandidate
172+
);
173+
174+
String methodRefText =
175+
LambdaCanBeMethodReferenceInspection.createMethodReferenceText(candidate, functionalInterfaceType, parameters);
176+
177+
replaceWithMethodReference(project, methodRefText, anonymousClass.getBaseClassType(), anonymousClass.getParent());
178+
}
179+
}
180+
}
181+
182+
@RequiredWriteAction
183+
static void replaceWithMethodReference(@Nonnull Project project, String methodRefText, PsiType castType, PsiElement replacementTarget) {
184+
Collection<PsiComment> comments = ContainerUtil.map(
185+
PsiTreeUtil.findChildrenOfType(replacementTarget, PsiComment.class),
186+
comment -> (PsiComment) comment.copy()
187+
);
188+
189+
if (methodRefText != null) {
190+
String canonicalText = castType.getCanonicalText();
191+
PsiExpression psiExpression = JavaPsiFacade.getElementFactory(project)
192+
.createExpressionFromText("(" + canonicalText + ")" + methodRefText, replacementTarget);
193+
194+
PsiElement castExpr = replacementTarget.replace(psiExpression);
195+
if (RedundantCastUtil.isCastRedundant((PsiTypeCastExpression) castExpr)) {
196+
PsiExpression operand = ((PsiTypeCastExpression) castExpr).getOperand();
197+
LOG.assertTrue(operand != null);
198+
castExpr = castExpr.replace(operand);
199+
}
200+
201+
PsiElement anchor = PsiTreeUtil.getParentOfType(castExpr, PsiStatement.class);
202+
if (anchor == null) {
203+
anchor = castExpr;
204+
}
205+
for (PsiComment comment : comments) {
206+
anchor.getParent().addBefore(comment, anchor);
207+
}
208+
JavaCodeStyleManager.getInstance(project).shortenClassReferences(castExpr);
209+
}
178210
}
179-
}
180211
}

0 commit comments

Comments
 (0)