Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
4aebbb9
SONARJAVA-6506 Avoid suggesting records for DTO contracts
francois-mora-sonarsource Jun 23, 2026
4f4d86a
Refactor framework annotation prefix lookup
francois-mora-sonarsource Jun 23, 2026
2ca67ad
SONARJAVA-6506 Update scope coverage tests
francois-mora-sonarsource Jun 23, 2026
6710233
SONARJAVA-6506 Cover Spring Data document types
francois-mora-sonarsource Jun 23, 2026
476334d
SONARJAVA-6506 Remove arbitrary Spring Data prefixes
francois-mora-sonarsource Jun 23, 2026
b8eaea8
Restore self-contained framework prefix test
francois-mora-sonarsource Jun 23, 2026
270595d
SONARJAVA-6506 Handle constructor parameter annotations
francois-mora-sonarsource Jun 23, 2026
f33b9db
SONARJAVA-6506 Remove Spring Data document test stubs
francois-mora-sonarsource Jun 23, 2026
2797558
SONARJAVA-6506 Remove MongoDB test references
francois-mora-sonarsource Jun 23, 2026
2c91b51
SONARJAVA-6506 Migrate framework prefix test sample to test-sources
francois-mora-sonarsource Jun 24, 2026
25c3066
SONARJAVA-6506 Fix DTO contract follow-ups
francois-mora-sonarsource Jun 24, 2026
bc4e83c
SONARJAVA-6506 Update autoscan expectation
francois-mora-sonarsource Jun 24, 2026
8fd59fd
SONARJAVA-6506 Update autoscan FP count
francois-mora-sonarsource Jun 24, 2026
5c8f3ee
SONARJAVA-6506 Remove redundant Externalizable check
francois-mora-sonarsource Jun 24, 2026
0fbccb9
SONARJAVA-6506 Adjust serialization contract formatting
francois-mora-sonarsource Jun 24, 2026
06c1d00
SONARJAVA-6506 Update S6206 rule documentation
francois-mora-sonarsource Jun 24, 2026
6f8a94c
Remove redundant test classpath override
francois-mora-sonarsource Jun 24, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public void javaCheckTestSources() throws Exception {
SoftAssertions softly = new SoftAssertions();
softly.assertThat(newDiffs).containsExactlyInAnyOrderElementsOf(knownDiffs.values());
softly.assertThat(newTotal).isEqualTo(knownTotal);
softly.assertThat(rulesCausingFPs).hasSize(10);
softly.assertThat(rulesCausingFPs).hasSize(11);
softly.assertThat(rulesNotReporting).hasSize(19);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ruleKey": "S6206",
"hasTruePositives": true,
"falseNegatives": 0,
"falsePositives": 0
}
"falseNegatives": 2,
"falsePositives": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,17 @@ public int getI() {
return i;
}
}

@UnknownFrameworkAnnotation
public final class UnknownAnnotatedClass { // Compliant, unknown annotations may represent framework contracts
private final int i;

public UnknownAnnotatedClass(final int i) {
this.i = i;
}

public int getI() {
return i;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package checks;

import io.micronaut.http.annotation.Get;
import io.micronaut.core.annotation.Introspected;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;

class RecordInsteadOfClassCheckPackagePrefixSample {

final class ClassWithSpringValueAnnotation { // Compliant, beans factory annotations are in scope
@Value("${record.sum}")
private final int sum;

ClassWithSpringValueAnnotation(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

@ConfigurationProperties("record")
final class ClassWithConfigurationPropertiesAnnotation { // Compliant, boot context properties annotations are in scope
private final int sum;

ClassWithConfigurationPropertiesAnnotation(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

final class ClassWithSpringDataAnnotationField { // Compliant, spring data annotations are in scope
@Id
private final int sum;

ClassWithSpringDataAnnotationField(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

@Introspected
final class ClassWithMicronautCoreAnnotation { // Compliant, Micronaut core annotations are in scope
private final int sum;

ClassWithMicronautCoreAnnotation(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

@Configuration
final class ClassWithSpringConfigurationAnnotation { // Noncompliant {{Refactor this class declaration to use 'record ClassWithSpringConfigurationAnnotation(int sum)'.}}
private final int sum;

ClassWithSpringConfigurationAnnotation(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

final class ClassWithMicronautHttpGetter { // Noncompliant {{Refactor this class declaration to use 'record ClassWithMicronautHttpGetter(int sum)'.}}
private final int sum;

ClassWithMicronautHttpGetter(int sum) { this.sum = sum; }

@Get
int getSum() { return sum; }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
package checks;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.util.Optional;

public class RecordInsteadOfClassCheckSample {
Expand Down Expand Up @@ -146,6 +158,10 @@ final class ClassWithPrivateNonFinalField { private int base; }
final class ClassWithPublicFinalField { public final int base = 0; }
final class ClassWithoutFields { }
final class ClassWithoutFinalFields { private int sum; }
Object anonymousClass = new Object() {
private final int sum = 0;
int getSum() { return sum; }
};
abstract class AbstractClass { abstract void foo(); }
interface NotAClass { void foo(); }

Expand All @@ -167,6 +183,110 @@ public Optional<String> bar() { // Not the same type as the field bar.
}
}

final class SerializableClass implements Serializable { // Compliant, records have different serialization behavior
private final int sum;

SerializableClass(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

final class ExternalizableClass implements Externalizable { // Compliant, records have different serialization behavior
private final int sum;

ExternalizableClass(int sum) { this.sum = sum; }
int getSum() { return sum; }
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { }
public void writeExternal(ObjectOutput out) throws IOException { }
}

final class ClassWithWriteObject {
private final int sum;

ClassWithWriteObject(int sum) { this.sum = sum; }
int getSum() { return sum; }
private void writeObject(ObjectOutputStream out) throws IOException { }
}

final class ClassWithReadObject {
private final int sum;

ClassWithReadObject(int sum) { this.sum = sum; }
int getSum() { return sum; }
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { }
}

final class ClassWithReadObjectNoData {
private final int sum;

ClassWithReadObjectNoData(int sum) { this.sum = sum; }
int getSum() { return sum; }
private void readObjectNoData() throws ObjectStreamException { }
}

final class ClassWithWriteReplace {
private final int sum;

ClassWithWriteReplace(int sum) { this.sum = sum; }
int getSum() { return sum; }
private Object writeReplace() throws ObjectStreamException { return this; }
}

final class ClassWithReadResolve {
private final int sum;

ClassWithReadResolve(int sum) { this.sum = sum; }
int getSum() { return sum; }
private Object readResolve() throws ObjectStreamException { return this; }
}

final class ClassWithSerialPersistentFields {
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
private final int sum;

ClassWithSerialPersistentFields(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

@JsonIgnoreProperties(ignoreUnknown = true)
final class ClassWithJsonAnnotation { // Compliant, framework metadata owns the class shape
private final int sum;

ClassWithJsonAnnotation(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

final class ClassWithJsonCreatorConstructor {
private final int sum;

@JsonCreator
ClassWithJsonCreatorConstructor(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

final class ClassWithJsonAnnotatedConstructorParameter {
private final int sum;

ClassWithJsonAnnotatedConstructorParameter(@JsonProperty("total") int sum) { this.sum = sum; }
int getSum() { return sum; }
}

final class ClassWithJsonAnnotatedField {
@JsonProperty("total")
private final int sum;

ClassWithJsonAnnotatedField(int sum) { this.sum = sum; }
int getSum() { return sum; }
}

final class ClassWithJsonAnnotatedGetter {
private final int sum;

ClassWithJsonAnnotatedGetter(int sum) { this.sum = sum; }

@JsonProperty("total")
int getSum() { return sum; }
}

// When the constructor has smaller visibility, it is not possible to create a record with the same behavior.
// Order: Public > protected > package private > private

Expand Down
Loading
Loading