From 2504ec6d321f6163e9ef23b8b9a4e3f471c89c64 Mon Sep 17 00:00:00 2001 From: Henri Viik Date: Sun, 27 Nov 2022 20:49:28 +0100 Subject: [PATCH] IDEA-305079 show usages for record fields using lombok @Builder and @With #2252 GitOrigin-RevId: fae916295f1a3cc40d759bf10a995446aa43a7ca --- .../LombokFieldFindUsagesHandlerFactory.java | 22 ++++++++++--------- .../clazz/builder/BuilderProcessor.java | 14 +++++++----- .../AbstractConstructorClassProcessor.java | 5 ++++- .../plugin/usage/LombokUsageTest.java | 18 +++++++++++++++ .../usage/FindUsageBuilderRecord.java | 19 ++++++++++++++++ .../testData/usage/FindUsageWitherRecord.java | 20 +++++++++++++++++ 6 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 plugins/lombok/testData/usage/FindUsageBuilderRecord.java create mode 100644 plugins/lombok/testData/usage/FindUsageWitherRecord.java diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokFieldFindUsagesHandlerFactory.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokFieldFindUsagesHandlerFactory.java index f23d03dfa04b..2c152cec3667 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokFieldFindUsagesHandlerFactory.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokFieldFindUsagesHandlerFactory.java @@ -6,6 +6,8 @@ import com.intellij.openapi.project.DumbService; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiField; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiRecordComponent; import com.intellij.psi.util.PsiUtilCore; import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder; import de.plushnikov.intellij.plugin.psi.LombokLightFieldBuilder; @@ -26,9 +28,9 @@ public class LombokFieldFindUsagesHandlerFactory extends FindUsagesHandlerFactor @Override public boolean canFindUsages(@NotNull PsiElement element) { - if (element instanceof PsiField && !DumbService.isDumb(element.getProject())) { - final PsiField psiField = (PsiField) element; - final PsiClass containingClass = psiField.getContainingClass(); + if ((element instanceof PsiField || element instanceof PsiRecordComponent) && !DumbService.isDumb(element.getProject())) { + final PsiMember psiMember = (PsiMember) element; + final PsiClass containingClass = psiMember.getContainingClass(); if (containingClass != null) { return Arrays.stream(containingClass.getMethods()).anyMatch(LombokLightMethodBuilder.class::isInstance) || Arrays.stream(containingClass.getInnerClasses()).anyMatch(LombokLightClassBuilder.class::isInstance); @@ -42,34 +44,34 @@ public class LombokFieldFindUsagesHandlerFactory extends FindUsagesHandlerFactor return new FindUsagesHandler(element) { @Override public PsiElement @NotNull [] getSecondaryElements() { - final PsiField psiField = (PsiField) getPsiElement(); - final PsiClass containingClass = psiField.getContainingClass(); + final PsiMember psiMember = (PsiMember) getPsiElement(); + final PsiClass containingClass = psiMember.getContainingClass(); if (containingClass != null) { final Collection elements = new ArrayList<>(); - processClass(containingClass, psiField, elements); + processClass(containingClass, psiMember, elements); Arrays.stream(containingClass.getInnerClasses()) - .forEach(psiClass -> processClass(psiClass, psiField, elements)); + .forEach(psiClass -> processClass(psiClass, psiMember, elements)); return PsiUtilCore.toPsiElementArray(elements); } return PsiElement.EMPTY_ARRAY; } - private static void processClass(PsiClass containingClass, PsiField refPsiField, Collection collector) { + private static void processClass(PsiClass containingClass, PsiMember refPsiField, Collection collector) { processClassMethods(containingClass, refPsiField, collector); processClassFields(containingClass, refPsiField, collector); } - private static void processClassFields(PsiClass containingClass, PsiField refPsiField, Collection collector) { + private static void processClassFields(PsiClass containingClass, PsiMember refPsiField, Collection collector) { Arrays.stream(containingClass.getFields()) .filter(LombokLightFieldBuilder.class::isInstance) .filter(psiField -> psiField.getNavigationElement() == refPsiField) .forEach(collector::add); } - private static void processClassMethods(PsiClass containingClass, PsiField refPsiField, Collection collector) { + private static void processClassMethods(PsiClass containingClass, PsiMember refPsiField, Collection collector) { Arrays.stream(containingClass.getMethods()) .filter(LombokLightMethodBuilder.class::isInstance) .filter(psiMethod -> psiMethod.getNavigationElement() == refPsiField) diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/builder/BuilderProcessor.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/builder/BuilderProcessor.java index 05bee745316e..c119e13e95e4 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/builder/BuilderProcessor.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/builder/BuilderProcessor.java @@ -83,12 +83,14 @@ public class BuilderProcessor extends AbstractClassProcessor { @Override protected void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List target) { - if (PsiAnnotationSearchUtil.isNotAnnotatedWith(psiClass, LombokClassNames.ALL_ARGS_CONSTRUCTOR, - LombokClassNames.REQUIRED_ARGS_CONSTRUCTOR, LombokClassNames.NO_ARGS_CONSTRUCTOR)) { - // Create all args constructor only if there is no declared constructors and no lombok constructor annotations - final Collection definedConstructors = PsiClassUtil.collectClassConstructorIntern(psiClass); - if (definedConstructors.isEmpty()) { - target.addAll(getAllArgsConstructorProcessor().createAllArgsConstructor(psiClass, PsiModifier.PACKAGE_LOCAL, psiAnnotation)); + if (!psiClass.isRecord()) { + if (PsiAnnotationSearchUtil.isNotAnnotatedWith(psiClass, LombokClassNames.ALL_ARGS_CONSTRUCTOR, + LombokClassNames.REQUIRED_ARGS_CONSTRUCTOR, LombokClassNames.NO_ARGS_CONSTRUCTOR)) { + // Create all args constructor only if there is no declared constructors and no lombok constructor annotations + final Collection definedConstructors = PsiClassUtil.collectClassConstructorIntern(psiClass); + if (definedConstructors.isEmpty()) { + target.addAll(getAllArgsConstructorProcessor().createAllArgsConstructor(psiClass, PsiModifier.PACKAGE_LOCAL, psiAnnotation)); + } } } diff --git a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/constructor/AbstractConstructorClassProcessor.java b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/constructor/AbstractConstructorClassProcessor.java index 921d50cf5bc7..ec4008ea2a81 100644 --- a/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/constructor/AbstractConstructorClassProcessor.java +++ b/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/processor/clazz/constructor/AbstractConstructorClassProcessor.java @@ -3,6 +3,7 @@ package de.plushnikov.intellij.plugin.processor.clazz.constructor; import com.intellij.codeInsight.daemon.impl.quickfix.SafeDeleteFix; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.RecordAugmentProvider; import com.intellij.psi.impl.light.LightReferenceListBuilder; import com.intellij.psi.impl.light.LightTypeParameterBuilder; import com.intellij.psi.util.PsiTypesUtil; @@ -182,7 +183,9 @@ public abstract class AbstractConstructorClassProcessor extends AbstractClassPro protected static Collection getAllNotInitializedAndNotStaticFields(@NotNull PsiClass psiClass) { Collection allNotInitializedNotStaticFields = new ArrayList<>(); final boolean classAnnotatedWithValue = PsiAnnotationSearchUtil.isAnnotatedWith(psiClass, LombokClassNames.VALUE); - for (PsiField psiField : PsiClassUtil.collectClassFieldsIntern(psiClass)) { + Collection fields = psiClass.isRecord() ? RecordAugmentProvider.getFieldAugments(psiClass) + : PsiClassUtil.collectClassFieldsIntern(psiClass); + for (PsiField psiField : fields) { // skip fields named $ boolean addField = !psiField.getName().startsWith(LombokUtils.LOMBOK_INTERN_FIELD_MARKER); diff --git a/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/usage/LombokUsageTest.java b/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/usage/LombokUsageTest.java index 0905b461d9f6..ea6a4fb062e5 100644 --- a/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/usage/LombokUsageTest.java +++ b/plugins/lombok/src/test/java/de/plushnikov/intellij/plugin/usage/LombokUsageTest.java @@ -7,9 +7,11 @@ import com.intellij.find.findUsages.JavaClassFindUsagesOptions; import com.intellij.find.impl.FindManagerImpl; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiClass; +import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.usageView.UsageInfo; import com.intellij.util.CommonProcessors; import de.plushnikov.intellij.plugin.AbstractLombokLightCodeInsightTestCase; +import de.plushnikov.intellij.plugin.LombokTestUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -21,6 +23,12 @@ import java.util.List; */ public class LombokUsageTest extends AbstractLombokLightCodeInsightTestCase { + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return LombokTestUtil.LOMBOK_NEW_DESCRIPTOR; + } + public void testFindUsageGetterSetter() { final Collection usages = loadTestClass(); assertUsages(usages, "findUsageGetterSetter.setBar", "findUsageGetterSetter.getBar"); @@ -36,11 +44,21 @@ public class LombokUsageTest extends AbstractLombokLightCodeInsightTestCase { assertUsages(usages, "findUsageWither.withBar", "findUsageWither.getBar"); } + public void testFindUsageWitherRecord() { + final Collection usages = loadTestClass(); + assertUsages(usages, "findUsageWitherRecord.withBar", "findUsageWitherRecord.bar"); + } + public void testFindUsageBuilder() { final Collection usages = loadTestClass(); assertUsages(usages, "FindUsageBuilder.builder().bar", "findUsageBuilder.getBar"); } + public void testFindUsageBuilderRecord() { + final Collection usages = loadTestClass(); + assertUsages(usages, "FindUsageBuilderRecord.builder().bar", "findUsageBuilderRecord.bar"); + } + public void testFindUsageSingularBuilder() { final Collection usages = loadTestClass(); assertUsages(usages, "FindUsageSingularBuilder.builder().bar", "FindUsageSingularBuilder.builder().bars", diff --git a/plugins/lombok/testData/usage/FindUsageBuilderRecord.java b/plugins/lombok/testData/usage/FindUsageBuilderRecord.java new file mode 100644 index 000000000000..d98e467deeb3 --- /dev/null +++ b/plugins/lombok/testData/usage/FindUsageBuilderRecord.java @@ -0,0 +1,19 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +import lombok.Builder; + +@Builder +public record FindUsageBuilderRecord( + int foo, + String bar +) { + + public static void main(String[] args) { + FindUsageBuilderRecord findUsageBuilderRecord = FindUsageBuilderRecord.builder() + .bar("bar") + .foo(1981) + .build(); + + System.out.println("Bar is: " + findUsageBuilderRecord.bar()); + System.out.println("Foo is: " + findUsageBuilderRecord.foo()); + } +} diff --git a/plugins/lombok/testData/usage/FindUsageWitherRecord.java b/plugins/lombok/testData/usage/FindUsageWitherRecord.java new file mode 100644 index 000000000000..8bb0bacb29a1 --- /dev/null +++ b/plugins/lombok/testData/usage/FindUsageWitherRecord.java @@ -0,0 +1,20 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +import lombok.Value; +import lombok.experimental.Wither; + +@Wither +@Value +public record FindUsageWitherRecord( + int foo, + String bar +) { + + public static void main(String[] args) { + FindUsageWitherRecord findUsageWitherRecord = new FindUsageWitherRecord(1, "bar"); + findUsageWitherRecord + .withBar("myBar") + .withFoo(1981); + System.out.println("Bar is: " + findUsageWitherRecord.bar()); + System.out.println("Foo is: " + findUsageWitherRecord.foo()); + } +}