Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ruleKey": "S6856",
"hasTruePositives": false,
"falseNegatives": 47,
"falseNegatives": 59,
"falsePositives": 0
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@

public class ExpressionsHelper {

private static final Set<String> SIMPLE_TYPES = Set.of(
"java.lang.String",
"java.lang.Integer",
"java.lang.Long",
"java.lang.Double",
"java.lang.Float",
"java.lang.Boolean",
"java.util.Optional"
);

private ExpressionsHelper() {
}

Expand Down Expand Up @@ -277,4 +287,13 @@ public static List<ExpressionTree> getIdentifierAssignments(IdentifierTree ident
return assignments;
}

public static boolean isStandardDataType(Type type) {
if (type.isPrimitive()) {
return true;
}

String fqn = type.fullyQualifiedName();
return SIMPLE_TYPES.contains(fqn);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.semantic.Type;
import org.sonar.plugins.java.api.tree.CompilationUnitTree;
import org.sonar.plugins.java.api.tree.ExpressionStatementTree;
import org.sonar.plugins.java.api.tree.ExpressionTree;
import org.sonar.plugins.java.api.tree.IdentifierTree;
import org.sonar.plugins.java.api.tree.ImportTree;
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.tree.VariableTree;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -232,4 +235,35 @@ private static ExpressionTree getCallArgument(String code) {
var methodInvocationTree = (MethodInvocationTree) exprStmtTree.expression();
return methodInvocationTree.arguments().get(0);
}

@ParameterizedTest
@ValueSource(strings = {"byte", "short", "int", "long", "float", "double", "boolean", "char",
"String", "Integer", "Long", "Double", "Float", "Boolean", "java.util.Optional<String>"})
void isStandardDataType_basicTypes(String type) {
String code = newCode(
"void f() {",
" " + type + " x;",
"}"
);
MethodTree method = methodTree(code);
VariableTree variable = (VariableTree) method.block().body().get(0);
Type variableType = variable.type().symbolType();
assertThat(ExpressionsHelper.isStandardDataType(variableType)).isTrue();
}

@Test
void isStandardDataType_customClass() {
String code = newCode(
"static class CustomClass {}",
"void f() {",
" CustomClass x;",
"}"
);
// The method is at index 1 (after the CustomClass at index 0)
MethodTree method = (MethodTree) classTree(code).members().get(1);
VariableTree variable = (VariableTree) method.block().body().get(0);
Type type = variable.type().symbolType();
assertThat(ExpressionsHelper.isStandardDataType(type)).isFalse();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package checks.spring.s6856;

import lombok.Data;
import lombok.Setter;

class ExtractSetterPropertiesTestData {

// Class with explicit setters
static class ExplicitSetters {
private String name;
private int age;
private boolean active;

public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}

public void setActive(boolean active) {
this.active = active;
}

// Not a setter - has wrong return type
public String setInvalid(String value) {
return value;
}

// Not a setter - has no parameters
public void setEmpty() {
}

// Not a setter - has multiple parameters
public void setMultiple(String a, String b) {
}

// Not a setter - is private
private void setPrivate(String value) {
}

// Not a setter - is static
public static void setStatic(String value) {
}
}

// Class with Lombok @Data (generates setters for all non-final fields)
@Data
static class LombokData {
private String project;
private int year;
private String month;
private final String constant = "CONST"; // Should be excluded (final)
private static String staticField; // Should be excluded (static)
}

// Class with Lombok @Setter at class level
@Setter
static class LombokClassLevelSetter {
private String firstName;
private String lastName;
private final String id = "ID"; // Should be excluded (final)
private static int count; // Should be excluded (static)
}

// Class with Lombok @Setter at field level
static class LombokFieldLevelSetter {
@Setter
private String email;
@Setter
private int score;
private String noSetter; // No @Setter, should be excluded
private static String staticField; // Should be excluded (static)
}

// Mixed: explicit setters + Lombok field-level @Setter
static class MixedSetters {
@Setter
private String lombokField;
private String explicitField;

public void setExplicitField(String value) {
this.explicitField = value;
}
}

// Class with no setters
static class NoSetters {
private String field;

public String getField() {
return field;
}
}

// Empty class
static class EmptyClass {
}

// Class with only getters
static class OnlyGetters {
private String name;
private int age;

public String getName() {
return name;
}

public int getAge() {
return age;
}
}
}
Loading
Loading