Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public class JavaRulingTest {
private static final Logger LOG = LoggerFactory.getLogger(JavaRulingTest.class);

private static final ImmutableSet<String> SUBSET_OF_ENABLED_RULES = ImmutableSet.of(
"S2095", "S2189", "S2222", "S2259", "S2583", "S2589", "S2637", "S2689", "S2755", "S3065",
"S2095", "S2189", "S2222", "S2583", "S2589", "S2637", "S2689", "S2755", "S3065",
"S3516", "S3518", "S3546", "S3655", "S3824", "S3958", "S3959", "S4165", "S4449", "S6373", "S6374", "S6376", "S6377");

@ClassRule
Expand Down Expand Up @@ -96,7 +96,7 @@ public static void prepare() throws Exception {
List<String> extraNonDefaultRules = List.of("S3546", "S6374");
ProfileGenerator.generate(ORCHESTRATOR, "Sonar Way", ImmutableMap.of(), new HashSet<>(), SUBSET_OF_ENABLED_RULES, result,
extraNonDefaultRules);
assertThat(result).hasSize(23); // ALL symbolic-execution rules
assertThat(result).hasSize(22); // ALL symbolic-execution rules minus S2259 that has been deprecated

Path allRulesFolder = Paths.get("src/test/resources");
effectiveDumpOldFolder = tmpDumpOldFolder.getRoot().toPath().toAbsolutePath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.sonar.java.se.plugin;

import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import org.sonar.java.se.checks.AllowXMLInclusionCheck;
import org.sonar.java.se.checks.BooleanGratuitousExpressionsCheck;
import org.sonar.java.se.checks.ConditionalUnreachableCodeCheck;
Expand All @@ -43,6 +45,12 @@
import org.sonar.java.se.checks.XxeProcessingCheck;

public class JavaSECheckList {
/**
* A list of checks that are overridden by other analyzers.
*/
public static final Set<Class<? extends SECheck>> OVERRIDDEN_CHECKS = Set.of(
NullDereferenceCheck.class // S2259
);

private JavaSECheckList(){
// no need to instantiate
Expand Down Expand Up @@ -79,4 +87,14 @@ public static List<Class<? extends SECheck>> getChecks() {
MinMaxRangeCheck.class);
}

/**
* Compute a list of checks that are not overridden by other analyzers.
* @return the list of checks returned by {@link #getChecks()} minus the checks in {@link #OVERRIDDEN_CHECKS}.
*/
public static List<Class<? extends SECheck>> getNonOverriddenChecks() {
return getChecks().stream()
.filter(Predicate.not(OVERRIDDEN_CHECKS::contains))
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@
package org.sonar.java.se.plugin;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.sonar.api.SonarEdition;
import org.sonar.api.SonarProduct;
import org.sonar.api.SonarRuntime;
import org.sonar.api.batch.rule.CheckFactory;
import org.sonar.api.batch.rule.Checks;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.scanner.ScannerSide;
import org.sonar.api.server.ServerSide;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.java.annotations.VisibleForTesting;
import org.sonar.java.se.SymbolicExecutionVisitor;
import org.sonar.java.se.checks.SECheck;
import org.sonar.plugins.java.api.CheckRegistrar;
Expand Down Expand Up @@ -54,7 +58,7 @@ public void register(RegistrarContext registrarContext) {

@Override
public void register(RegistrarContext registrarContext, CheckFactory checkFactory) {
Checks<JavaCheck> checks = checkFactory.<JavaCheck>create(REPOSITORY_KEY).addAnnotatedChecks(JavaSECheckList.getChecks());
Checks<JavaCheck> checks = checkFactory.<JavaCheck>create(REPOSITORY_KEY).addAnnotatedChecks(getChecks(runtime));

var seChecks = checks.all().stream()
.filter(SECheck.class::isInstance)
Expand All @@ -71,11 +75,35 @@ public void register(RegistrarContext registrarContext, CheckFactory checkFactor
public void customRulesDefinition(RulesDefinition.Context context, RulesDefinition.NewRepository javaRepository) {
RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_BASE_PATH, SONAR_WAY_PATH, runtime);

ruleMetadataLoader.addRulesByAnnotatedClass(javaRepository, new ArrayList<>(JavaSECheckList.getChecks()));
ruleMetadataLoader.addRulesByAnnotatedClass(javaRepository, new ArrayList<>(getChecks(runtime)));

setTemplates(javaRepository);
}

@VisibleForTesting
/**
* Returns a list of checks based on whether the runtime might include other analyzers that provide alternative implementation of checks.
* @param runtime The analysis runtime
* @returns The full list of checks in SQCB, a reduced list of checks in other runtime.
*/
static List<Class<? extends SECheck>> getChecks(SonarRuntime runtime) {
if (isStandaloneSymbolicExecutionAnalyzer(runtime)) {
return JavaSECheckList.getChecks();
}
return JavaSECheckList.getNonOverriddenChecks();
}

@VisibleForTesting
/**
* Test if the analyzer is running in a context where another analyzer has overridden some of its checks.
* @param The analysis runtime
* @return true if not
*/
static boolean isStandaloneSymbolicExecutionAnalyzer(SonarRuntime runtime) {
return runtime.getProduct() != SonarProduct.SONARLINT &&
runtime.getEdition() == SonarEdition.COMMUNITY;
}

private static void setTemplates(RulesDefinition.NewRepository repository) {
RULE_TEMPLATES_KEY.forEach(ruleKey -> repository.rule(ruleKey).setTemplate(true));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public static List<RuleKey> getSonarWayRuleKeys() {
"S2095",
"S2189",
"S2222",
"S2259",
"S2583",
"S2589",
"S2637",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"S2095",
"S2189",
"S2222",
"S2259",
"S2583",
"S2589",
"S2637",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@
class JavaSECheckListTest {

@Test
void getChecks() {
assertThat(JavaSECheckList.getChecks()).isNotNull().hasSize(23);
void getNonOverriddenChecks_returns_a_list_of_checks_that_are_not_overridden_by_other_plugins() {
assertThat(JavaSECheckList.getNonOverriddenChecks())
.hasSize(22)
.doesNotContainAnyElementsOf(JavaSECheckList.OVERRIDDEN_CHECKS);
}

@Test
void getChecks_returns_the_full_list_of_checks() {
assertThat(JavaSECheckList.getChecks())
.hasSize(23);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,31 @@ class JavaSECheckRegistrarTest {
"S6374"
);

private static final SonarRuntime SQC = SonarRuntimeImpl.forSonarQube(
Version.parse("8.1"),
SonarQubeSide.SERVER,
SonarEdition.SONARCLOUD
);
private static final SonarRuntime SQCB = SonarRuntimeImpl.forSonarQube(
Version.parse("25.1"),
SonarQubeSide.SERVER,
SonarEdition.COMMUNITY
);
private static final SonarRuntime SQS_DEVELOPER = SonarRuntimeImpl.forSonarQube(
Version.parse("2025.1"),
SonarQubeSide.SERVER,
SonarEdition.DEVELOPER
);
private static final SonarRuntime SQ_FOR_IDE = SonarRuntimeImpl.forSonarLint(Version.parse("10.22.0.81232"));

@Test
void register_rules() {
CheckRegistrar registrar = new JavaSECheckRegistrar(null);
SonarRuntime sonarqubeServerDeveloper = SonarRuntimeImpl.forSonarQube(
Version.parse("2025.1"),
SonarQubeSide.SERVER,
SonarEdition.DEVELOPER
);
CheckRegistrar registrar = new JavaSECheckRegistrar(sonarqubeServerDeveloper);
TestCheckRegistrarContext context = new TestCheckRegistrarContext();

CheckFactory checkFactory = new CheckFactory(activeRules);
Expand All @@ -66,6 +88,25 @@ void register_rules() {
assertThat(context.testRuleKeys).isEmpty();
}

@Test
void getChecks_returns_the_expected_amount_of_checks_depending_on_the_runtime() {
assertThat(JavaSECheckRegistrar.getChecks(SQC)).hasSize(22);
assertThat(JavaSECheckRegistrar.getChecks(SQS_DEVELOPER)).hasSize(22);
assertThat(JavaSECheckRegistrar.getChecks(SQ_FOR_IDE)).hasSize(22);
assertThat(JavaSECheckRegistrar.getChecks(SQCB)).hasSize(23);
}

@Test
void is_only_in_standalone_mode_in_sqcb() {
assertThat(JavaSECheckRegistrar.isStandaloneSymbolicExecutionAnalyzer(SQCB)).isTrue();

assertThat(JavaSECheckRegistrar.isStandaloneSymbolicExecutionAnalyzer(SQS_DEVELOPER)).isFalse();
assertThat(JavaSECheckRegistrar.isStandaloneSymbolicExecutionAnalyzer(SQC)).isFalse();

assertThat(JavaSECheckRegistrar.isStandaloneSymbolicExecutionAnalyzer(SQ_FOR_IDE)).isFalse();
}


@Test
void rules_definition() {
SonarRuntime sonarRuntime = SonarRuntimeImpl.forSonarQube(Version.create(10, 2), SonarQubeSide.SERVER, SonarEdition.ENTERPRISE);
Expand All @@ -85,7 +126,7 @@ void rules_definition() {
assertThat(repository.name()).isEqualTo("Sonar");
assertThat(repository.language()).isEqualTo("java");
List<RulesDefinition.Rule> rules = repository.rules();
assertThat(rules).hasSize(23);
assertThat(rules).hasSize(22);

var activeByDefault = rules.stream()
.filter(k -> !rulesNotActiveByDefault.contains(k.key()))
Expand Down Expand Up @@ -117,7 +158,7 @@ private static String[] getRuleKeysWithRepo() {

private static String[] getRuleKeys() {
var ruleKeys = new ArrayList<String>();
for (Class<? extends SECheck> check : JavaSECheckList.getChecks()) {
for (Class<? extends SECheck> check : JavaSECheckList.getNonOverriddenChecks()) {
ruleKeys.add(check.getAnnotation(Rule.class).key());
}
return ruleKeys.toArray(new String[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ void constructor() {
JavaSEProfileRegistrar registrar = new JavaSEProfileRegistrar();
TestProfileRegistrarContext context = new TestProfileRegistrarContext();
registrar.register(context);
assertThat(context.defaultQualityProfileRules).hasSize(21); // 2 are not in the default profile
assertThat(context.defaultQualityProfileRules).hasSize(20); // 3 are not in the default profile
}

}