[lombok] IDEA-366441 Fix Intellij warnings for Lombok @With annotation functionality on class

GitOrigin-RevId: 3eeeeeeff7b4c510bcfd835a1b6eec7037a6fcc5
This commit is contained in:
Michail Plushnikov
2025-02-11 22:14:03 +01:00
committed by intellij-monorepo-bot
parent 4a760237af
commit 5640847303
4 changed files with 111 additions and 31 deletions

View File

@@ -11,6 +11,7 @@ import de.plushnikov.intellij.plugin.thirdparty.LombokUtils;
import de.plushnikov.intellij.plugin.util.LombokProcessorUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiClassUtil;
import de.plushnikov.intellij.plugin.util.PsiMethodUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -101,9 +102,9 @@ public final class WitherProcessor extends AbstractClassProcessor {
}
}
private static @NotNull Collection<PsiMethod> createFieldWithers(@NotNull PsiClass psiClass,
@NotNull String methodModifier,
@NotNull AccessorsInfo accessors) {
private @NotNull Collection<PsiMethod> createFieldWithers(@NotNull PsiClass psiClass,
@NotNull String methodModifier,
@NotNull AccessorsInfo accessors) {
Collection<PsiMethod> result = new ArrayList<>();
final Collection<PsiField> witherFields = getWitherFields(psiClass);
@@ -118,18 +119,21 @@ public final class WitherProcessor extends AbstractClassProcessor {
return result;
}
private static @NotNull Collection<PsiField> getWitherFields(@NotNull PsiClass psiClass) {
private @NotNull Collection<PsiField> getWitherFields(@NotNull PsiClass psiClass) {
Collection<PsiField> witherFields = new ArrayList<>();
final AccessorsInfo.AccessorsValues classAccessorsValues = AccessorsInfo.getAccessorsValues(psiClass);
final Collection<PsiMethod> existingMethods = filterToleratedElements(PsiClassUtil.collectClassMethodsIntern(psiClass));
for (PsiField psiField : psiClass.getFields()) {
if (shouldGenerateWither(psiField)) {
if (shouldGenerateWither(psiField, classAccessorsValues, existingMethods)) {
witherFields.add(psiField);
}
}
return witherFields;
}
private static boolean shouldGenerateWither(@NotNull PsiField psiField) {
private static boolean shouldGenerateWither(@NotNull PsiField psiField, @NotNull AccessorsInfo.AccessorsValues classAccessorsValues,
@NotNull Collection<PsiMethod> existingMethods) {
boolean createWither = true;
PsiModifierList modifierList = psiField.getModifierList();
if (null != modifierList) {
@@ -142,16 +146,39 @@ public final class WitherProcessor extends AbstractClassProcessor {
createWither &= !psiField.getName().startsWith(LombokUtils.LOMBOK_INTERN_FIELD_MARKER);
// Skip fields having Wither annotation already
createWither &= !PsiAnnotationSearchUtil.isAnnotatedWith(psiField, LombokClassNames.WITHER, LombokClassNames.WITH);
final AccessorsInfo accessorsInfo = AccessorsInfo.buildFor(psiField).withFluent(false);
final AccessorsInfo accessorsInfo = AccessorsInfo.buildFor(psiField, classAccessorsValues).withFluent(false);
createWither &= accessorsInfo.acceptsFieldName(psiField.getName());
if (createWither) {
createWither = isWitherMethodUnique(psiField, accessorsInfo, existingMethods);
}
}
return createWither;
}
private static boolean isWitherMethodUnique(@NotNull PsiField psiField, @NotNull AccessorsInfo accessorsInfo,
@NotNull Collection<PsiMethod> existingMethods) {
final Collection<String> possibleWitherNames =
LombokUtils.toAllWitherNames(accessorsInfo, psiField.getName(), PsiTypes.booleanType().equals(psiField.getType()));
for (String witherName : possibleWitherNames) {
if (PsiMethodUtil.hasSimilarMethod(existingMethods, witherName, 1)) {
return false;
}
}
return true;
}
@Override
public LombokPsiElementUsage checkFieldUsage(@NotNull PsiField psiField, @NotNull PsiAnnotation psiAnnotation) {
if (shouldGenerateWither(psiField)) {
return LombokPsiElementUsage.READ_WRITE;
final PsiClass containingClass = psiField.getContainingClass();
if (containingClass != null) {
final AccessorsInfo.AccessorsValues classAccessorsValues = AccessorsInfo.getAccessorsValues(containingClass);
final Collection<PsiMethod> existingMethods = filterToleratedElements(PsiClassUtil.collectClassMethodsIntern(containingClass));
if (shouldGenerateWither(psiField, classAccessorsValues, existingMethods)) {
return LombokPsiElementUsage.READ_WRITE;
}
}
return LombokPsiElementUsage.NONE;
}

View File

@@ -47,15 +47,20 @@ public final class WitherFieldProcessor extends AbstractFieldProcessor {
protected boolean validate(@NotNull PsiAnnotation psiAnnotation, @NotNull PsiField psiField, @NotNull ProblemSink builder) {
validateOnXAnnotations(psiAnnotation, psiField, builder, "onParam");
boolean valid = validateVisibility(psiAnnotation);
final PsiClass containingClass = psiField.getContainingClass();
boolean valid = null != containingClass;
valid &= validateVisibility(psiAnnotation);
valid &= validName(psiField, builder);
valid &= validNonStatic(psiField, builder);
valid &= validNonFinalInitialized(psiField, builder);
valid &= validIsWitherUnique(psiField, builder);
final PsiClass containingClass = psiField.getContainingClass();
valid &=
null != containingClass && (containingClass.hasModifierProperty(PsiModifier.ABSTRACT) || validConstructor(containingClass, builder));
if (valid) {
valid = validIsWitherUnique(psiField, builder,
filterToleratedElements(PsiClassUtil.collectClassMethodsIntern(containingClass)));
valid &= containingClass.hasModifierProperty(PsiModifier.ABSTRACT) || validConstructor(containingClass, builder);
}
return valid;
}
@@ -90,7 +95,7 @@ public final class WitherFieldProcessor extends AbstractFieldProcessor {
private static boolean validNonStatic(@NotNull PsiField psiField, final @NotNull ProblemSink builder) {
if (psiField.hasModifierProperty(PsiModifier.STATIC)) {
builder.addWarningMessage("inspection.message.not.generating.wither")
.withLocalQuickFixes(()->PsiQuickFixFactory.createModifierListFix(psiField, PsiModifier.STATIC, false, false));
.withLocalQuickFixes(() -> PsiQuickFixFactory.createModifierListFix(psiField, PsiModifier.STATIC, false, false));
return false;
}
return true;
@@ -102,26 +107,21 @@ public final class WitherFieldProcessor extends AbstractFieldProcessor {
psiField.hasModifierProperty(PsiModifier.FINAL) && !PsiAnnotationSearchUtil.isAnnotatedWith(psiClass, LombokClassNames.VALUE) &&
psiField.hasInitializer() && !PsiAnnotationSearchUtil.isAnnotatedWith(psiField, LombokClassNames.BUILDER_DEFAULT)) {
builder.addWarningMessage("inspection.message.not.generating.wither.for.this.field.withers.cannot.be.generated")
.withLocalQuickFixes(()->PsiQuickFixFactory.createModifierListFix(psiField, PsiModifier.FINAL, false, false));
.withLocalQuickFixes(() -> PsiQuickFixFactory.createModifierListFix(psiField, PsiModifier.FINAL, false, false));
return false;
}
return true;
}
private boolean validIsWitherUnique(@NotNull PsiField psiField, final @NotNull ProblemSink builder) {
final PsiClass fieldContainingClass = psiField.getContainingClass();
if (fieldContainingClass != null) {
final Collection<PsiMethod> classMethods = filterToleratedElements(PsiClassUtil.collectClassMethodsIntern(fieldContainingClass));
final AccessorsInfo accessorsInfo = buildAccessorsInfo(psiField);
final String psiFieldName = psiField.getName();
final Collection<String> possibleWitherNames =
LombokUtils.toAllWitherNames(accessorsInfo, psiFieldName, PsiTypes.booleanType().equals(psiField.getType()));
for (String witherName : possibleWitherNames) {
if (PsiMethodUtil.hasSimilarMethod(classMethods, witherName, 1)) {
builder.addWarningMessage("inspection.message.not.generating.s.method.with.that.name.already.exists", witherName);
return false;
}
private static boolean validIsWitherUnique(@NotNull PsiField psiField, @NotNull ProblemSink builder,
@NotNull Collection<PsiMethod> existingMethods) {
final AccessorsInfo accessorsInfo = buildAccessorsInfo(psiField);
final Collection<String> possibleWitherNames =
LombokUtils.toAllWitherNames(accessorsInfo, psiField.getName(), PsiTypes.booleanType().equals(psiField.getType()));
for (String witherName : possibleWitherNames) {
if (PsiMethodUtil.hasSimilarMethod(existingMethods, witherName, 1)) {
builder.addWarningMessage("inspection.message.not.generating.s.method.with.that.name.already.exists", witherName);
return false;
}
}
return true;
@@ -182,7 +182,9 @@ public final class WitherFieldProcessor extends AbstractFieldProcessor {
return result;
}
public @Nullable PsiMethod createWitherMethod(@NotNull PsiField psiField, @NotNull String methodModifier, @NotNull AccessorsInfo accessorsInfo) {
public @Nullable PsiMethod createWitherMethod(@NotNull PsiField psiField,
@NotNull String methodModifier,
@NotNull AccessorsInfo accessorsInfo) {
LombokLightMethodBuilder methodBuilder = null;
final PsiClass psiFieldContainingClass = psiField.getContainingClass();
if (psiFieldContainingClass != null) {

View File

@@ -11,4 +11,8 @@ public class WithHighlightTest extends AbstractLombokHighlightsTest {
doTest();
}
public void testWitherExample() {
doTest();
}
}

View File

@@ -0,0 +1,47 @@
import lombok.Getter;
import lombok.With;
@With
@Getter
public class WitherExample {
private String matchingTypeParameter;
private String differentTypeParameter;
public WitherExample() {
this.matchingTypeParameter = null;
this.differentTypeParameter = null;
}
public WitherExample(String matchingTypeParameter, String differentTypeParameter) {
this.matchingTypeParameter = matchingTypeParameter;
this.differentTypeParameter = differentTypeParameter;
}
/*
* Intellij GUI displays a compilation warning for this method when it should not.
* The warning states that the same method signature is already defined in the class (Lombok @With),
* however this is not the case when actually compiled.
* The method `withMatchingTypeParameter(String matchingTypeParameter)` is never generated by Lombok as the below method is defined with the same name.
*/
public WitherExample withMatchingTypeParameter(String matchingTypeParameter) {
this.matchingTypeParameter = matchingTypeParameter;
return this;
}
/*
* When calling this method Intellij GUI displays two options when it should only display one
* An option is given for `withDifferentTypeParameter(String differentTypeParameter)`,
* however that method is never generated by Lombok as the below method is defined with the same name.
*/
public WitherExample withDifferentTypeParameter(int differentTypeParameter) {
this.differentTypeParameter = String.valueOf(differentTypeParameter);
return this;
}
public static void main(String[] args) {
WitherExample underTest = new WitherExample("value", "123");
underTest.withDifferentTypeParameter(1);
underTest.withDifferentTypeParameter<error descr="'withDifferentTypeParameter(int)' in 'WitherExample' cannot be applied to '(java.lang.String)'">("not valid")</error>;
}
}