Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ Desktop.ini

.java-version
.mvn/.develocity/

CLAUDE.md
.claude
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,25 @@ class FieldAssignments {
private final int bar = foo[2]; // Compliant (array access)
}

// Test case from SONARJAVA-5796: @Builder.Default with @SuperBuilder should not trigger
@lombok.experimental.SuperBuilder
class LombokSuperBuilderWithDefault {
@lombok.Builder.Default
private final int foo = 1; // Compliant - Builder.Default requires this pattern

@lombok.Builder.Default
private final String bar = "test"; // Compliant - Builder.Default requires this pattern
}

@lombok.Builder
class LombokBuilderWithDefault {
@lombok.Builder.Default
private final int baz = 42; // Compliant - Builder.Default requires this pattern
}

// Without Builder annotations, should still raise issues
class NotABuilder {
@lombok.Builder.Default
private final int shouldRaise = 1; // Noncompliant {{Make this final field static too.}}
}

Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ public void setContext(JavaFileScannerContext context) {
@Override
public void visitNode(Tree tree) {
nestedClassesLevel++;
for (Tree member : ((ClassTree) tree).members()) {
ClassTree classTree = (ClassTree) tree;
for (Tree member : classTree.members()) {
if (member.is(Tree.Kind.VARIABLE)) {
VariableTree variableTree = (VariableTree) member;
if (staticNonFinal(variableTree) && hasConstantInitializer(variableTree) && !isObjectInInnerClass(variableTree)) {
if (staticNonFinal(variableTree) && hasConstantInitializer(variableTree) && !isObjectInInnerClass(variableTree) && !isLombokBuilderDefault(variableTree, classTree)) {
reportIssue(variableTree.simpleName(), "Make this final field static too.");
}
}
Expand Down Expand Up @@ -136,4 +137,28 @@ private static boolean isFinal(VariableTree variableTree) {
private static boolean isStatic(VariableTree variableTree) {
return ModifiersUtils.hasModifier(variableTree.modifiers(), Modifier.STATIC);
}

/**
* Check if a field is annotated with @Builder.Default in a class annotated with @Builder or @SuperBuilder.
* Lombok's builder pattern requires final fields with initializers when using @Builder.Default.
*/
private static boolean isLombokBuilderDefault(VariableTree variableTree, ClassTree classTree) {
// Check if field has @Builder.Default annotation
boolean hasBuilderDefault = variableTree.modifiers().annotations().stream()
.anyMatch(annotation -> {
var type = annotation.symbolType();
return type.is("lombok.Builder.Default") || type.is("lombok.Builder$Default");
});

if (!hasBuilderDefault) {
return false;
}

// Check if class has @Builder or @SuperBuilder annotation
return classTree.modifiers().annotations().stream()
.anyMatch(annotation -> {
var type = annotation.symbolType();
return type.is("lombok.Builder") || type.is("lombok.experimental.SuperBuilder");
});
}
}
Loading