SonarQube does not see Lombok Builder in certain cases for java:S1068

SonarQube Server v10.8 (100206)
SonarQube for IDE 10.14.1.80220

If I were to do this:

  @Builder
  public static class Example {
    private Long id = 1L;
  }

Then SonarQube would behave as expected and mark the id field as used. But a common pattern in my codebase is to provide a builder with defaults for a different class. This works like this:

@UtilityClass
public class ExampleFactory {
  public static class ExampleBuilder {
    private Long id = 1L;
  }

  @Builder
  private Example build(Long id) {
    var example = new Example();
    example.setId(id);
    return example;
  }
}

Where @Builder Delomoks to

@UtilityClass
public final class ExampleFactory {
  @org.springframework.lang.NonNull
  public static ExampleBuilder builder() {
    return new ExampleBuilder();
  }

  public static class ExampleBuilder {
    private Long id = 1L;

    ExampleBuilder() {
    }

    @org.springframework.lang.NonNull
    public ExampleBuilder id(Long id) {
      this.id = id;
      return this;
    }

    @org.springframework.lang.NonNull
    public Example build() {
      return ExampleFactory.build(this.id);
    }

    @org.springframework.lang.NonNull
    public String toString() {
      return "ExampleFactory.ExampleBuilder(id=" + this.id + ")";
    }
  }

  private static Example build(Long id) {
    var example = new Example();
    example.setId(id);
    return Example;

  }
}

But Sonar does not seem to realize this and incorrectly flags the id field as not used (java:S1068). To be fair, IntelliJ itself does not seem to understand this either, so my full suppression that I’ve been using on the Builder classes looks like this:
@SuppressWarnings({"java:S1068", "unused", "FieldMayBeFinal", "MismatchedQueryAndUpdateOfCollection"})

Hi Lars,

I was able to reproduce the issue and noticed that you can remove ExampleBuilder while still achieving the same result with Lombok. If you are intentionally keeping ExampleBuilder, using the @SuppressWarnings annotation is indeed the best approach. The analyzer does not analyze generated bytecode, so it cannot determine if the variable is used.

Too reduce the noise, for Lombok, we don’t report S1068 on code blocks annotated with specific annotations. However, in this case, ExampleBuilder is not annotated and it seems Lombok is overwriting ExampleBuilder during code generation. If ExampleBuilder were named differently, like ExampleBuilder_, it would not work. We would prefer not to encode this specific knowledge into the analyzer.

you can remove ExampleBuilder while still achieving the same result with Lombok

No, you cannot. The ExampleBuilder is there to provide the default values for the fields.

it seems Lombok is overwriting ExampleBuilder during code generation

No, it’s not overwriting it, its just modifying it by adding the missing methods/constructors

The analyzer does not analyze generated bytecode, so it cannot determine if the variable is used.

Oh, I thought for some reason that sonarqube could see generated bytecode. It seems to respect nullability annotations on generated code, for example. But maybe that’s working through some other means.

No, you cannot. The ExampleBuilder is there to provide the default values for the fields.

I hadn’t considered using ExampleBuilder to initialize the fields—thank you for sharing this approach. I’ve learned something new!

1 Like