Skip to content

Commit def406e

Browse files
SONARJAVA-4973 Fix FP on S1118 with Lombok @UtilityClass in automatic analysis (#4960)
Co-authored-by: Dorian Burihabwa <75226315+dorian-burihabwa-sonarsource@users.noreply.github.com>
1 parent bf9a0b3 commit def406e

3 files changed

Lines changed: 47 additions & 4 deletions

File tree

java-checks/src/main/java/org/sonar/java/filters/LombokFilter.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Optional;
2323
import java.util.Set;
2424
import javax.annotation.Nullable;
25+
import org.sonar.java.annotations.VisibleForTesting;
2526
import org.sonar.java.checks.AtLeastOneConstructorCheck;
2627
import org.sonar.java.checks.CollectionInappropriateCallsCheck;
2728
import org.sonar.java.checks.ConstantsShouldBeStaticFinalCheck;
@@ -181,12 +182,32 @@ public void visitAssignmentExpression(AssignmentExpressionTree tree) {
181182
}
182183

183184
private static boolean usesAnnotation(ClassTree classTree, List<String> annotations) {
185+
return usesAnnotation(classTree, annotations, false);
186+
}
187+
188+
private static boolean usesAnnotation(ClassTree classTree, List<String> annotations, boolean shouldCheckAnnotationLocalName) {
184189
SymbolMetadata classMetadata = classTree.symbol().metadata();
185-
return annotations.stream().anyMatch(classMetadata::isAnnotatedWith);
190+
191+
for(String fullyQualified: annotations) {
192+
if(classMetadata.isAnnotatedWith(fullyQualified)) {
193+
return true;
194+
}
195+
// In automatic analysis use only the last part of the annotation.
196+
if(shouldCheckAnnotationLocalName && classMetadata.isAnnotatedWith(annotationTypeIdentifier(fullyQualified))) {
197+
return true;
198+
}
199+
}
200+
201+
return false;
202+
}
203+
204+
@VisibleForTesting
205+
static String annotationTypeIdentifier(String fullyQualified) {
206+
return fullyQualified.substring(fullyQualified.lastIndexOf('.') + 1);
186207
}
187208

188209
private static boolean generatesNonPublicConstructor(ClassTree classTree) {
189-
if (usesAnnotation(classTree, UTILITY_CLASS)) {
210+
if (usesAnnotation(classTree, UTILITY_CLASS, true)) {
190211
return true;
191212
}
192213
SymbolMetadata metadata = classTree.symbol().metadata();

java-checks/src/test/files/filters/LombokFilterWithoutSemantic.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
import lombok.experimental.UtilityClass;
22

3-
// FP happening without semantics.
3+
class Utility { // WithIssue
4+
public static int triple(int in) {
5+
return in * 3;
6+
}
7+
}
8+
49
@UtilityClass
5-
public class Utility { // WithIssue
10+
class UtilityAnnotated { // NoIssue
11+
public static int triple(int in) {
12+
return in * 3;
13+
}
14+
}
15+
16+
@lombok.experimental.UtilityClass
17+
class UtilityFullyQualified { // NoIssue
618
public static int triple(int in) {
719
return in * 3;
820
}

java-checks/src/test/java/org/sonar/java/filters/LombokFilterTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import org.junit.jupiter.api.Test;
2020

21+
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.sonar.java.filters.LombokFilter.annotationTypeIdentifier;
23+
2124
class LombokFilterTest {
2225

2326
@Test
@@ -32,4 +35,11 @@ void testWithoutSemantic() {
3235
.withoutSemantic()
3336
.verify("src/test/files/filters/LombokFilterWithoutSemantic.java", new LombokFilter());
3437
}
38+
39+
@Test
40+
void testAnnotationTypeIdentifier() {
41+
assertThat(annotationTypeIdentifier("noDot")).isEqualTo("noDot");
42+
assertThat(annotationTypeIdentifier("one.dot")).isEqualTo("dot");
43+
assertThat(annotationTypeIdentifier("many.many.dots")).isEqualTo("dots");
44+
}
3545
}

0 commit comments

Comments
 (0)