fixed #761 Fix checks for methods that already exists in code, for equals/hashCode and toString

GitOrigin-RevId: 12cf760279af229cfd21fc81b9142d84f61bedc1
This commit is contained in:
Michail Plushnikov
2020-09-13 14:48:49 +02:00
committed by intellij-monorepo-bot
parent f73adab6db
commit c412c6fddd
14 changed files with 121 additions and 47 deletions

View File

@@ -1,6 +1,7 @@
<ul>
<li>0.32
<ol>
<li>Fixed #761: EqualsAndHashCode: Wrong warning 'A method with one of those names already exists'</li>
<li>Fixed #802: [Only for IntelliJ>=2020.2.2] val mis-infers an Optional(T) as Optional(Object) after map.</li>
<li>Fixed #826: Error if using @FieldNameConstants in switch case</li>
<li>Fixed #858: Delombok produces duplicate @NonNull annotations on setters/getters</li>

View File

@@ -114,17 +114,16 @@ public class EqualsAndHashCodeProcessor extends AbstractClassProcessor {
return result;
}
private boolean validateExistingMethods(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
private void validateExistingMethods(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
if (hasOneOfMethodsDefined(psiClass)) {
builder.addWarning("Not generating equals and hashCode: A method with one of those names already exists. (Either both or none of these methods will be generated).");
return false;
}
return true;
}
private boolean hasOneOfMethodsDefined(@NotNull PsiClass psiClass) {
final Collection<PsiMethod> classMethodsIntern = PsiClassUtil.collectClassMethodsIntern(psiClass);
return PsiMethodUtil.hasMethodByName(classMethodsIntern, EQUALS_METHOD_NAME, HASH_CODE_METHOD_NAME);
return PsiMethodUtil.hasMethodByName(classMethodsIntern, EQUALS_METHOD_NAME, 1) ||
PsiMethodUtil.hasMethodByName(classMethodsIntern, HASH_CODE_METHOD_NAME, 0);
}
protected void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target) {
@@ -145,7 +144,7 @@ public class EqualsAndHashCodeProcessor extends AbstractClassProcessor {
result.add(createEqualsMethod(psiClass, psiAnnotation, shouldGenerateCanEqual, memberInfos));
final Collection<PsiMethod> classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);
if (shouldGenerateCanEqual && !PsiMethodUtil.hasMethodByName(classMethods, CAN_EQUAL_METHOD_NAME)) {
if (shouldGenerateCanEqual && !PsiMethodUtil.hasMethodByName(classMethods, CAN_EQUAL_METHOD_NAME, 1)) {
result.add(createCanEqualMethod(psiClass, psiAnnotation));
}

View File

@@ -29,7 +29,7 @@ import java.util.List;
*/
public class ToStringProcessor extends AbstractClassProcessor {
public static final String METHOD_NAME = "toString";
public static final String TO_STRING_METHOD_NAME = "toString";
private static final String INCLUDE_ANNOTATION_METHOD = "name";
private static final String INCLUDE_ANNOTATION_RANK = "rank";
@@ -75,16 +75,15 @@ public class ToStringProcessor extends AbstractClassProcessor {
return result;
}
private boolean validateExistingMethods(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
boolean result = true;
final Collection<PsiMethod> classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);
if (PsiMethodUtil.hasMethodByName(classMethods, METHOD_NAME)) {
builder.addWarning("Not generated '%s'(): A method with same name already exists", METHOD_NAME);
result = false;
private void validateExistingMethods(@NotNull PsiClass psiClass, @NotNull ProblemBuilder builder) {
if (hasToStringMethodDefined(psiClass)) {
builder.addWarning("Not generated '%s'(): A method with same name already exists", TO_STRING_METHOD_NAME);
}
}
return result;
private boolean hasToStringMethodDefined(@NotNull PsiClass psiClass) {
final Collection<PsiMethod> classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);
return PsiMethodUtil.hasMethodByName(classMethods, TO_STRING_METHOD_NAME, 0);
}
protected void generatePsiElements(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation, @NotNull List<? super PsiElement> target) {
@@ -93,8 +92,7 @@ public class ToStringProcessor extends AbstractClassProcessor {
@NotNull
Collection<PsiMethod> createToStringMethod(@NotNull PsiClass psiClass, @NotNull PsiAnnotation psiAnnotation) {
final Collection<PsiMethod> classMethods = PsiClassUtil.collectClassMethodsIntern(psiClass);
if (PsiMethodUtil.hasMethodByName(classMethods, METHOD_NAME)) {
if (hasToStringMethodDefined(psiClass)) {
return Collections.emptyList();
}
@@ -110,7 +108,7 @@ public class ToStringProcessor extends AbstractClassProcessor {
final String paramString = createParamString(psiClass, memberInfos, psiAnnotation, forceCallSuper);
final String blockText = String.format("return \"%s(%s)\";", getSimpleClassName(psiClass), paramString);
final LombokLightMethodBuilder methodBuilder = new LombokLightMethodBuilder(psiManager, METHOD_NAME)
final LombokLightMethodBuilder methodBuilder = new LombokLightMethodBuilder(psiManager, TO_STRING_METHOD_NAME)
.withMethodReturnType(PsiType.getJavaLangString(psiManager, GlobalSearchScope.allScope(psiClass.getProject())))
.withContainingClass(psiClass)
.withNavigationElement(psiAnnotation)

View File

@@ -61,7 +61,7 @@ public class BuilderPreDefinedInnerClassMethodProcessor extends AbstractBuilderP
}
// create 'toString' method
if (!existedMethodNames.contains(ToStringProcessor.METHOD_NAME)) {
if (!existedMethodNames.contains(ToStringProcessor.TO_STRING_METHOD_NAME)) {
result.add(builderHandler.createToStringMethod(psiAnnotation, psiBuilderClass));
}

View File

@@ -211,7 +211,7 @@ public class EqualsAndHashCodeToStringHandler {
final PsiAnnotation getterLombokAnnotation = PsiAnnotationSearchUtil.findAnnotation(psiClass, Getter.class);
hasGetter = null == getterLombokAnnotation || null != LombokProcessorUtil.getMethodModifier(getterLombokAnnotation);
} else {
hasGetter = PsiMethodUtil.hasMethodByName(PsiClassUtil.collectClassMethodsIntern(psiClass), getterName);
hasGetter = PsiMethodUtil.hasMethodByName(PsiClassUtil.collectClassMethodsIntern(psiClass), getterName, 0);
}
return hasGetter ? getterName + "()" : fieldName;

View File

@@ -343,7 +343,7 @@ public class SuperBuilderHandler extends BuilderHandler {
result.add(buildMethod);
}
if (!existedMethodNames.contains(ToStringProcessor.METHOD_NAME)) {
if (!existedMethodNames.contains(ToStringProcessor.TO_STRING_METHOD_NAME)) {
// create 'toString' method
result.add(createToStringMethod(psiAnnotation, baseClassBuilder, forceCallSuper));
}

View File

@@ -3,9 +3,7 @@ package de.plushnikov.intellij.plugin.util;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @author Plushnikov Michail
@@ -17,35 +15,25 @@ public class PsiMethodUtil {
return elementFactory.createCodeBlockFromText("{" + blockText + "}", psiElement);
}
public static boolean hasMethodByName(@NotNull Collection<PsiMethod> classMethods, @NotNull String methodName) {
return classMethods.stream().map(PsiMethod::getName).anyMatch(methodName::equals);
public static boolean hasMethodByName(@NotNull Collection<PsiMethod> classMethods, @NotNull String methodName, int paramCount) {
return classMethods.stream()
.filter(m -> methodName.equals(m.getName()))
.anyMatch(m -> acceptedParameterCount(m, paramCount));
}
public static boolean hasMethodByName(@NotNull Collection<PsiMethod> classMethods, String... methodNames) {
final List<String> searchedMethodNames = Arrays.asList(methodNames);
return classMethods.stream().map(PsiMethod::getName).anyMatch(searchedMethodNames::contains);
public static boolean hasSimilarMethod(@NotNull Collection<PsiMethod> classMethods, @NotNull String methodName, int paramCount) {
return classMethods.stream()
.filter(m -> methodName.equalsIgnoreCase(m.getName()))
.anyMatch(m -> acceptedParameterCount(m, paramCount));
}
public static boolean hasSimilarMethod(@NotNull Collection<PsiMethod> classMethods, @NotNull String methodName, int methodArgCount) {
for (PsiMethod classMethod : classMethods) {
if (isSimilarMethod(classMethod, methodName, methodArgCount)) {
return true;
}
private static boolean acceptedParameterCount(@NotNull PsiMethod classMethod, int methodArgCount) {
int minArgs = classMethod.getParameterList().getParametersCount();
int maxArgs = minArgs;
if (classMethod.isVarArgs()) {
minArgs--;
maxArgs = Integer.MAX_VALUE;
}
return false;
}
private static boolean isSimilarMethod(@NotNull PsiMethod classMethod, @NotNull String methodName, int methodArgCount) {
boolean equalNames = methodName.equalsIgnoreCase(classMethod.getName());
if (equalNames) {
int minArgs = classMethod.getParameterList().getParametersCount();
int maxArgs = minArgs;
if (classMethod.isVarArgs()) {
minArgs--;
maxArgs = Integer.MAX_VALUE;
}
return !(methodArgCount < minArgs || methodArgCount > maxArgs);
}
return false;
return !(methodArgCount < minArgs || methodArgCount > maxArgs);
}
}

View File

@@ -20,6 +20,7 @@ daemon.donate.title=Lombok support plugin updated to v{0}
daemon.donate.content=<br/>\
Helpful? <b><a href="https://www.paypal.me/mplushnikov">Donate with PayPal</a></b><br/><br/>\
Fixes:<br/>\
- Fixed (<a href="https://github.com/mplushnikov/lombok-intellij-plugin/issues/761">#761</a>): EqualsAndHashCode: Wrong warning 'A method with one of those names already exists'<br/>\
- Fixed (<a href="https://github.com/mplushnikov/lombok-intellij-plugin/issues/802">#802</a>): val mis-infers an Optional(T) as Optional(Object) after map. Only for IntelliJ>=2020.2.2<br/>\
- Fixed (<a href="https://github.com/mplushnikov/lombok-intellij-plugin/issues/826">#826</a>): Error if using @FieldNameConstants in switch case<br/>\
- Fixed (<a href="https://github.com/mplushnikov/lombok-intellij-plugin/issues/858">#858</a>): Delombok produces duplicate @NonNull annotations on setters/getters<br/>\

View File

@@ -59,4 +59,7 @@ public class EqualsAndHashCodeTest extends AbstractLombokParsingTestCase {
doTest(true);
}
public void testEqualsandhashcode$EqualsAndHashCodeWithNamedExistingMethods() {
doTest(true);
}
}

View File

@@ -38,4 +38,8 @@ public class ToStringTest extends AbstractLombokParsingTestCase {
public void testTostring$ToStringSimpleClassName() {
doTest(true);
}
public void testTostring$ToStringWithNamedExistingMethods() {
doTest(true);
}
}

View File

@@ -0,0 +1,40 @@
import java.util.Objects;
public class EqualsAndHashCodeWithNamedExistingMethods {
private int someInt;
private Integer someInteger;
public boolean equals(Object o, Object o2) {
return o.equals(o2);
}
public int hashCode(Float someFloat) {
return Objects.hash(someFloat);
}
public boolean equals(final Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCodeWithNamedExistingMethods)) return false;
final EqualsAndHashCodeWithNamedExistingMethods other = (EqualsAndHashCodeWithNamedExistingMethods) o;
if (!other.canEqual((Object) this)) return false;
if (this.someInt != other.someInt) return false;
final Object this$someInteger = this.someInteger;
final Object other$someInteger = other.someInteger;
if (this$someInteger == null ? other$someInteger != null : !this$someInteger.equals(other$someInteger))
return false;
return true;
}
protected boolean canEqual(final Object other) {
return other instanceof EqualsAndHashCodeWithNamedExistingMethods;
}
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.someInt;
final Object $someInteger = this.someInteger;
result = result * PRIME + ($someInteger == null ? 43 : $someInteger.hashCode());
return result;
}
}

View File

@@ -0,0 +1,11 @@
public class ToStringWithNamedExistingMethods {
private int someInt;
public String toString(String string) {
return string;
}
public String toString() {
return "ToStringWithNamedExistingMethods(someInt=" + this.someInt + ")";
}
}

View File

@@ -0,0 +1,18 @@
import lombok.EqualsAndHashCode;
import java.util.Objects;
@EqualsAndHashCode
public class EqualsAndHashCodeWithNamedExistingMethods {
<caret>
private int someInt;
private Integer someInteger;
public boolean equals(Object o, Object o2) {
return o.equals(o2);
}
public int hashCode(Float someFloat) {
return Objects.hash(someFloat);
}
}

View File

@@ -0,0 +1,11 @@
import lombok.ToString;
@ToString
public class ToStringWithNamedExistingMethods {
<caret>
private int someInt;
public String toString(String string) {
return string;
}
}