java:S1186 False positive on record compact canonical constructors

Product: SonarQube Community Build 26.5.0.122743
sonar-java version: sonar-java 8.29 (build 43460)
sonar-java SE version: sonar-java-symbolic-execution 8.16.4 (build 1912)
Java source level: 21 (javac 21, source/target 21)

Rule

java:S1186 — Methods should not be empty

Description

Rule java:S1186 raises a false positive on compact canonical constructors in Java records. While the constructor body appears syntactically empty ({}) at the AST level, it is not semantically empty. The Java compiler implicitly synthesizes the field assignments for every record component. Flagging the compact form while ignoring the semantically identical explicit form (this.a = a; this.b = b;) is inconsistent. This false positive is particularly disruptive when the compact constructor is intentionally declared solely to carry annotations (e.g., @QueryProjection in Querydsl or Jackson’s @JsonCreator).

Reproducer

public class SomeDtoHolder {
​
    @java.lang.annotation.Target(java.lang.annotation.ElementType.CONSTRUCTOR)
    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
    public @interface QueryProjection {}
​
    // BEFORE — flagged by java:S1186 (false positive)
    public record SomeDtoA(int a, int b) {
        @QueryProjection
        public SomeDtoA {
        }
    }
​
    // AFTER — NOT flagged, although behavior is identical
    public record SomeDtoB(int a, int b) {
        @QueryProjection
        public SomeDtoB(int a, int b) {
            this.a = a;
            this.b = b;
        }
    }
}

Expected behavior

java:S1186 should not flag record compact canonical constructors, treating them as implicitly populated rather than truly empty methods.

Actual behavior

The rule triggers on the compact form public SomeDtoA {}, but accepts the equivalent explicit canonical constructor without raising an issue.

Hi,

Thank you for the report and the easy-to-use reproducer! Yes, this does look like a false positive when a compact constructor carries an annotation. I do not see a reason to allow an unannotated compact constructors, but in this case (or any other case), the rule can easily be suppressed with a comment, for example, // intentionally empty, placed inside method body.

I have filed a ticket, SONARJAVA-6434, to track this issue.