diff --git a/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/AbstractChecks.java b/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/AbstractChecks.java index 1d54e97f0f1..c9c6ed10e13 100644 --- a/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/AbstractChecks.java +++ b/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/AbstractChecks.java @@ -44,6 +44,7 @@ public class AbstractChecks { private final CustomJavaScriptRulesDefinition[] customRulesDefinitions; private final CustomRuleRepository[] customRuleRepositories; private final Set> checksByRepository = new HashSet<>(); + private boolean hasCustomChecks; public AbstractChecks(CheckFactory checkFactory, @Nullable CustomJavaScriptRulesDefinition[] customRulesDefinitions, @Nullable CustomRuleRepository[] customRuleRepositories) { this.checkFactory = checkFactory; @@ -70,6 +71,7 @@ private void addCustomChecks(CustomRuleRepository.Language language) { LOG.debug("Adding rules for repository '{}' {} from {}", rulesDefinition.repositoryKey(), rulesDefinition.checkClasses(), rulesDefinition.getClass().getCanonicalName()); doAddChecks(rulesDefinition.repositoryKey(), Arrays.asList(rulesDefinition.checkClasses())); + hasCustomChecks = true; } } @@ -80,12 +82,17 @@ private void addCustomChecks(CustomRuleRepository.Language language) { repo.checkClasses(), repo.getClass().getCanonicalName()); doAddChecks(repo.repositoryKey(), repo.checkClasses()); + hasCustomChecks = true; } } } } + public boolean hasCustomChecks() { + return hasCustomChecks; + } + public List all() { List allVisitors = new ArrayList<>(); diff --git a/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensor.java b/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensor.java index c52925a8fd2..4a92657d25b 100644 --- a/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensor.java +++ b/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensor.java @@ -78,9 +78,12 @@ public JavaScriptEslintBasedSensor(JavaScriptChecks checks, NoSonarFilter noSona @Override void analyzeFiles() throws IOException, InterruptedException { runEslintAnalysis(provideDefaultTsConfig()); - PROFILER.startInfo("Java-based frontend sensor [javascript]"); - new JavaScriptSensor(checks).execute(context); - PROFILER.stopInfo(); + if (checks.hasCustomChecks()) { + PROFILER.startInfo("Java-based frontend sensor [javascript] for custom rules"); + LOG.warn("Custom JavaScript rules are deprecated and API will be removed in future version."); + new JavaScriptSensor(checks).execute(context); + PROFILER.stopInfo(); + } } private List provideDefaultTsConfig() throws IOException { diff --git a/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptSensorTest.java b/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptSensorTest.java index fc05f573251..c664394b977 100644 --- a/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptSensorTest.java +++ b/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptSensorTest.java @@ -59,6 +59,7 @@ import org.sonar.plugins.javascript.api.JavaScriptCheck; import org.sonar.plugins.javascript.api.tree.ScriptTree; import org.sonar.plugins.javascript.api.tree.Tree; +import org.sonar.plugins.javascript.api.tree.expression.LiteralTree; import org.sonar.plugins.javascript.api.visitors.DoubleDispatchVisitorCheck; import org.sonar.plugins.javascript.api.visitors.LineIssue; import org.sonar.plugins.javascript.api.visitors.TreeVisitor; @@ -115,7 +116,20 @@ public List> checkClasses() { private final SensorContextTester context = SensorContextTester.create(baseDir); private JavaScriptSensor createSensor() { - return new JavaScriptSensor(new JavaScriptChecks(checkFactory)); + CustomRuleRepository[] ruleRepository = {new CustomRuleRepository() { + + @Override + public String repositoryKey() { + return "javascript"; + } + + @Override + public List> checkClasses() { + return ImmutableList.of(OctalNumberCheck.class); + } + }}; + + return new JavaScriptSensor(new JavaScriptChecks(checkFactory, ruleRepository)); } private JavaScriptSensor createSensorWithCustomRules() { @@ -147,11 +161,10 @@ public void should_only_log_debug_if_a_parsing_error() { context.setActiveRules(activeRules); createSensor().execute(context); Collection issues = context.allIssues(); - assertThat(issues).hasSize(0); - assertThat(context.allAnalysisErrors()).hasSize(0); + assertThat(issues).isEmpty(); + assertThat(context.allAnalysisErrors()).isEmpty(); - assertThat(logTester.logs(LoggerLevel.DEBUG).get(0)).isEqualTo("Unable to parse file with java-based frontend (some rules will not be executed): " + inputFile.toString()); - assertThat(logTester.logs(LoggerLevel.DEBUG).get(1)).startsWith("Parse error at line 3 column 1:"); + assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Unable to parse file with java-based frontend (some rules will not be executed): " + inputFile.toString()); } @Test @@ -182,7 +195,7 @@ public void should_not_add_error_to_context_when_analysis_raises_issues() { inputFile("file.js"); ActiveRules activeRules = (new ActiveRulesBuilder()) - .addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of(CheckList.JS_REPOSITORY_KEY, "S1314")).build()) + .addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of(CheckList.JS_REPOSITORY_KEY, "octal")).build()) .build(); checkFactory = new CheckFactory(activeRules); @@ -201,7 +214,7 @@ public void should_save_issue() throws Exception { ActiveRules activeRules = (new ActiveRulesBuilder()) .addRule(new NewActiveRule.Builder() // octal number - .setRuleKey(RuleKey.of(CheckList.JS_REPOSITORY_KEY, "S1314")) + .setRuleKey(RuleKey.of(CheckList.JS_REPOSITORY_KEY, "octal")) .build()) .build(); @@ -304,7 +317,7 @@ public void should_cancel_progress_report_and_return_with_no_exception_when_cont private void analyseFile(String relativePath) { ActiveRules activeRules = (new ActiveRulesBuilder()) - .addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of(CheckList.JS_REPOSITORY_KEY, "S1314")).build()) + .addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of(CheckList.JS_REPOSITORY_KEY, "octal")).build()) .build(); checkFactory = new CheckFactory(activeRules); context.setActiveRules(activeRules); @@ -410,6 +423,33 @@ public void visitScript(ScriptTree tree) { } } + @Rule( + key = "octal", + name = "Octal", + description = "desc", + tags = {"bug"}) + public static class OctalNumberCheck extends DoubleDispatchVisitorCheck { + private static final String MESSAGE = "Replace the value of the octal number (%s) by its decimal equivalent (%s)."; + + @Override + public void visitLiteral(LiteralTree tree) { + if (tree.is(Tree.Kind.NUMERIC_LITERAL)) { + String value = tree.value(); + if (value.length() > 1 && value.startsWith("0")) { + int newValue; + try { + newValue = Integer.parseInt(value, 8); + } catch (NumberFormatException e) { + return; + } + if (newValue > 9) { + addIssue(tree, String.format(MESSAGE, value, newValue)); + } + } + } + } + } + @Rule(key = "key2") public static class CustomRuleWithRuleRepository extends DoubleDispatchVisitorCheck { diff --git a/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensorTest.java b/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensorTest.java index 5556e4f0fa6..ed637bab0c4 100644 --- a/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensorTest.java +++ b/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/eslint/JavaScriptEslintBasedSensorTest.java @@ -19,6 +19,7 @@ */ package org.sonar.plugins.javascript.eslint; +import com.google.common.collect.ImmutableList; import com.google.gson.Gson; import java.io.File; import java.io.IOException; @@ -62,6 +63,9 @@ import org.sonar.api.utils.log.LoggerLevel; import org.sonar.javascript.checks.CheckList; import org.sonar.plugins.javascript.JavaScriptChecks; +import org.sonar.plugins.javascript.JavaScriptSensorTest; +import org.sonar.plugins.javascript.api.CustomRuleRepository; +import org.sonar.plugins.javascript.api.JavaScriptCheck; import org.sonar.plugins.javascript.eslint.EslintBridgeServer.AnalysisRequest; import org.sonar.plugins.javascript.eslint.EslintBridgeServer.AnalysisResponse; import org.sonarsource.nodejs.NodeCommandException; @@ -147,6 +151,7 @@ public void should_create_issues_from_eslint_based_rules() throws Exception { assertThat(firstIssue.ruleKey().rule()).isEqualTo("S3923"); assertThat(secondIssue.ruleKey().rule()).isEqualTo("S3923"); assertThat(thirdIssue.ruleKey().rule()).isEqualTo("S1451"); + assertThat(logTester.logs(LoggerLevel.WARN)).doesNotContain("Custom JavaScript rules are deprecated and API will be removed in future version."); } private AnalysisResponse response(String json) { @@ -484,13 +489,34 @@ public void should_run_old_frontend() throws Exception { .build(); context.fileSystem().add(inputFile); - JavaScriptChecks checks = checks("S1314"); + JavaScriptChecks checks = createChecksWithOctalCustomRule(); NoSonarFilter noSonarFilter = new NoSonarFilter(); JavaScriptEslintBasedSensor sensor = new JavaScriptEslintBasedSensor(checks, noSonarFilter, fileLinesContextFactory, eslintBridgeServerMock, null, tempFolder); sensor.execute(context); assertThat(context.allIssues()).hasSize(1); - assertThat(context.allIssues()).extracting(i -> i.ruleKey().toString()).containsExactly("javascript:S1314"); + assertThat(context.allIssues()).extracting(i -> i.ruleKey().toString()).containsExactly("javascript:octal"); + assertThat(logTester.logs(LoggerLevel.WARN)).contains("Custom JavaScript rules are deprecated and API will be removed in future version."); + } + + + private JavaScriptChecks createChecksWithOctalCustomRule() { + CustomRuleRepository[] ruleRepository = {new CustomRuleRepository() { + + @Override + public String repositoryKey() { + return "javascript"; + } + + @Override + public List> checkClasses() { + return ImmutableList.of(JavaScriptSensorTest.OctalNumberCheck.class); + } + }}; + ActiveRulesBuilder builder = new ActiveRulesBuilder(); + builder.addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of(CheckList.JS_REPOSITORY_KEY, "octal")).build()); + CheckFactory checkFactory = new CheckFactory(builder.build()); + return new JavaScriptChecks(checkFactory, ruleRepository); } @Test