[lombok] IDEA-321989 prevent StringIndexOutOfBoundsException for short method names

GitOrigin-RevId: 67abc464309949d02319255c07cb1fd67403d8f8
This commit is contained in:
Michail Plushnikov
2023-06-12 22:07:14 +02:00
committed by intellij-monorepo-bot
parent 942bc7b32a
commit de042e2e44
3 changed files with 76 additions and 26 deletions

View File

@@ -4,6 +4,7 @@ package de.plushnikov.intellij.plugin.inspection;
import com.intellij.codeInspection.*; import com.intellij.codeInspection.*;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*; import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocComment;
@@ -17,7 +18,10 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static com.intellij.util.ObjectUtils.tryCast; import static com.intellij.util.ObjectUtils.tryCast;
@@ -56,7 +60,8 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
if (annotation != null) { if (annotation != null) {
if (!annotation.getAttributes().isEmpty()) { if (!annotation.getAttributes().isEmpty()) {
isGetterAtClassLevel = false; isGetterAtClassLevel = false;
} else { }
else {
annotatedFields.add(field); annotatedFields.add(field);
} }
break; break;
@@ -73,9 +78,10 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
} }
} }
List<Pair<PsiField, PsiMethod>> allCandidates = new ArrayList<>(staticCandidates); List<Pair<PsiField, PsiMethod>> allCandidates = new ArrayList<>(staticCandidates);
if (isGetterAtClassLevel && (!instanceCandidates.isEmpty() || !annotatedFields.isEmpty()) ) { if (isGetterAtClassLevel && (!instanceCandidates.isEmpty() || !annotatedFields.isEmpty())) {
warnOrFix(psiClass, instanceCandidates, annotatedFields); warnOrFix(psiClass, instanceCandidates, annotatedFields);
} else { }
else {
allCandidates.addAll(instanceCandidates); allCandidates.addAll(instanceCandidates);
} }
for (Pair<PsiField, PsiMethod> candidate : allCandidates) { for (Pair<PsiField, PsiMethod> candidate : allCandidates) {
@@ -114,17 +120,18 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
|| !method.isWritable()) { || !method.isWritable()) {
return false; return false;
} }
final boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC);
final String methodName = method.getName(); final String methodName = method.getName();
final boolean isBooleanType = PsiTypes.booleanType().equals(returnType); final boolean isBooleanType = PsiTypes.booleanType().equals(returnType);
if ((isBooleanType ? !methodName.startsWith("is") : !methodName.startsWith("get")) if (isBooleanType ? !methodName.startsWith("is") : !methodName.startsWith("get")) {
|| methodName.length() == 3
|| Character.isDigit(methodName.charAt(3))) {
return false; return false;
} }
final String fieldName = isBooleanType
? methodName.substring(2, 3).toLowerCase(Locale.ROOT) + methodName.substring(3) final String fieldName = StringUtil.getPropertyName(methodName);
: methodName.substring(3, 4).toLowerCase(Locale.ROOT) + methodName.substring(4); if (StringUtil.isEmpty(fieldName)) {
return false;
}
if (method.getBody() == null) { if (method.getBody() == null) {
return false; return false;
} }
@@ -149,20 +156,19 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
if (qualifier != null) { if (qualifier != null) {
if (thisExpression == null) { if (thisExpression == null) {
return false; return false;
} else if (thisExpression.getQualifier() != null) { }
else if (thisExpression.getQualifier() != null) {
if (!thisExpression.getQualifier().isReferenceTo(psiClass)) { if (!thisExpression.getQualifier().isReferenceTo(psiClass)) {
return false; return false;
} }
} }
} }
final @Nullable String identifier = fieldRef.getReferenceName(); final @Nullable String identifier = fieldRef.getReferenceName();
if (identifier == null) { if (!fieldName.equals(identifier) && !StringUtil.capitalize(fieldName).equals(identifier)) {
return false;
}
if (!identifier.equals(fieldName)
&& !identifier.equals(fieldName.substring(0, 1).toUpperCase(Locale.ROOT) + fieldName.substring(1))) {
return false; return false;
} }
final boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC);
final PsiField field = psiClass.findFieldByName(identifier, false); final PsiField field = psiClass.findFieldByName(identifier, false);
if (field == null if (field == null
|| !field.isWritable() || !field.isWritable()
@@ -172,7 +178,8 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
} }
if (isMethodStatic) { if (isMethodStatic) {
staticCandidates.add(Pair.pair(field, method)); staticCandidates.add(Pair.pair(field, method));
} else { }
else {
instanceCandidates.add(Pair.pair(field, method)); instanceCandidates.add(Pair.pair(field, method));
} }
return true; return true;
@@ -192,7 +199,8 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
psiClassNameIdentifier != null ? psiClassNameIdentifier.getTextRangeInParent() : psiClass.getTextRange(), psiClassNameIdentifier != null ? psiClassNameIdentifier.getTextRangeInParent() : psiClass.getTextRange(),
fix); fix);
} else if (myApplyFix) { }
else if (myApplyFix) {
LombokGetterMayBeUsedFix.effectivelyDoFix(psiClass, fieldsAndMethods, annotatedFields); LombokGetterMayBeUsedFix.effectivelyDoFix(psiClass, fieldsAndMethods, annotatedFields);
} }
} }
@@ -202,8 +210,9 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
final LocalQuickFix fix = new LombokGetterMayBeUsedFix(field.getName()); final LocalQuickFix fix = new LombokGetterMayBeUsedFix(field.getName());
myHolder.registerProblem(method, myHolder.registerProblem(method,
LombokBundle.message("inspection.lombok.getter.may.be.used.display.field.message", LombokBundle.message("inspection.lombok.getter.may.be.used.display.field.message",
field.getName()), fix); field.getName()), fix);
} else if (myApplyFix) { }
else if (myApplyFix) {
LombokGetterMayBeUsedFix.effectivelyDoFix(field, method); LombokGetterMayBeUsedFix.effectivelyDoFix(field, method);
} }
} }
@@ -235,7 +244,8 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
final PsiElement element = descriptor.getPsiElement(); final PsiElement element = descriptor.getPsiElement();
if (element instanceof PsiMethod) { if (element instanceof PsiMethod) {
new LombokGetterMayBeUsedVisitor(null, true).visitMethodForFix((PsiMethod)element); new LombokGetterMayBeUsedVisitor(null, true).visitMethodForFix((PsiMethod)element);
} else if (element instanceof PsiClass) { }
else if (element instanceof PsiClass) {
new LombokGetterMayBeUsedVisitor(null, true).visitClass((PsiClass)element); new LombokGetterMayBeUsedVisitor(null, true).visitClass((PsiClass)element);
} }
} }
@@ -247,7 +257,7 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
} }
Project project = field.getProject(); Project project = field.getProject();
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiAnnotation annotation = factory.createAnnotationFromText("@"+LombokClassNames.GETTER, field); final PsiAnnotation annotation = factory.createAnnotationFromText("@" + LombokClassNames.GETTER, field);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(annotation); JavaCodeStyleManager.getInstance(project).shortenClassReferences(annotation);
modifierList.addAfter(annotation, null); modifierList.addAfter(annotation, null);
removeMethodAndMoveJavaDoc(field, method); removeMethodAndMoveJavaDoc(field, method);
@@ -262,7 +272,7 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
removeMethodAndMoveJavaDoc(field, method); removeMethodAndMoveJavaDoc(field, method);
} }
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiAnnotation newAnnotation = factory.createAnnotationFromText("@"+LombokClassNames.GETTER, aClass); final PsiAnnotation newAnnotation = factory.createAnnotationFromText("@" + LombokClassNames.GETTER, aClass);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(newAnnotation); JavaCodeStyleManager.getInstance(project).shortenClassReferences(newAnnotation);
final PsiModifierList modifierList = aClass.getModifierList(); final PsiModifierList modifierList = aClass.getModifierList();
if (modifierList == null) { if (modifierList == null) {
@@ -295,14 +305,16 @@ public class LombokGetterMayBeUsedInspection extends LombokJavaInspectionBase im
if (fieldJavaDoc == null) { if (fieldJavaDoc == null) {
if (javaDocGetterText.isEmpty()) { if (javaDocGetterText.isEmpty()) {
fieldJavaDoc = factory.createDocCommentFromText("/**\n*/"); fieldJavaDoc = factory.createDocCommentFromText("/**\n*/");
} else { }
else {
fieldJavaDoc = factory.createDocCommentFromText("/**\n* -- GETTER --\n* " + javaDocGetterText + "\n*/"); fieldJavaDoc = factory.createDocCommentFromText("/**\n* -- GETTER --\n* " + javaDocGetterText + "\n*/");
} }
for (PsiDocTag returnTag : returnTags) { for (PsiDocTag returnTag : returnTags) {
fieldJavaDoc.add(returnTag); fieldJavaDoc.add(returnTag);
} }
field.getParent().addBefore(fieldJavaDoc, field); field.getParent().addBefore(fieldJavaDoc, field);
} else { }
else {
@NotNull PsiElement @NotNull [] fieldJavaDocChildren = Arrays.stream(fieldJavaDoc.getChildren()) @NotNull PsiElement @NotNull [] fieldJavaDocChildren = Arrays.stream(fieldJavaDoc.getChildren())
.filter(e -> e instanceof PsiDocToken) .filter(e -> e instanceof PsiDocToken)
.toArray(PsiElement[]::new); .toArray(PsiElement[]::new);

View File

@@ -40,4 +40,8 @@ public class LombokGetterMayBeUsedInspectionTest extends LightDaemonAnalyzerTest
public void testInstanceAndStaticFields() { public void testInstanceAndStaticFields() {
doTest(); doTest();
} }
public void testShortMethods() {
doTest();
}
} }

View File

@@ -0,0 +1,34 @@
public class ShortMethods {
private int someInt;
private boolean someBoolean;
private boolean b;
private boolean x;
<warning descr="Field 'someInt' may have Lombok @Getter">public int getSomeInt() {
return someInt;
}</warning>
<warning descr="Field 'someBoolean' may have Lombok @Getter">public boolean isSomeBoolean() {
return someBoolean;
}</warning>
<warning descr="Field 'b' may have Lombok @Getter">public boolean isB() {
return b;
}</warning>
public int getX() {
return someInt;
}
public int get() {
return someInt;
}
public boolean is() {
return b;
}
public boolean b() {
return b;
}
}