diff options
author | Lukasz Jarocki <lukasz.jarocki@sonarsource.com> | 2022-08-22 14:38:18 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-08-22 20:03:13 +0000 |
commit | 8f2b35bda8e929b188c6f23cfe6f64130475485f (patch) | |
tree | fae8a9960c1a74d1cc9ca8590df2ecd0c67d391b /plugins | |
parent | 3b0714c5e64aa18408b7b167c905d4c38bd062c9 (diff) | |
download | sonarqube-8f2b35bda8e929b188c6f23cfe6f64130475485f.tar.gz sonarqube-8f2b35bda8e929b188c6f23cfe6f64130475485f.zip |
SONAR-17206 added new code snippets (to display diff) to education plugin
Diffstat (limited to 'plugins')
8 files changed, 327 insertions, 6 deletions
diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java index 6d3c1613f85..5c8a6512ceb 100644 --- a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java +++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/EducationRulesDefinition.java @@ -19,24 +19,30 @@ */ package org.sonar.education; +import java.nio.charset.StandardCharsets; +import org.sonar.api.internal.apachecommons.io.IOUtils; import org.sonar.api.server.rule.RuleDescriptionSection; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.education.sensors.EducationPrinciplesSensor; import org.sonar.education.sensors.EducationWithContextsSensor; import org.sonar.education.sensors.EducationWithDetectedContextSensor; -import static org.sonar.education.sensors.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY; import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ASSESS_THE_PROBLEM_SECTION_KEY; import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY; import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.INTRODUCTION_SECTION_KEY; import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.RESOURCES_SECTION_KEY; import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY; +import static org.sonar.education.sensors.EducationWith2LinkedCodeSnippetsSensor.EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY; +import static org.sonar.education.sensors.EducationWith4LinkedCodeSnippetsSensor.EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY; +import static org.sonar.education.sensors.EducationWithSingleContextSensor.EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY; public class EducationRulesDefinition implements RulesDefinition { public static final String EDUCATION_RULE_REPOSITORY_KEY = "edu"; public static final String EDUCATION_KEY = "education"; + private static final String[] ALL_SECTIONS = {INTRODUCTION_SECTION_KEY, ROOT_CAUSE_SECTION_KEY, ASSESS_THE_PROBLEM_SECTION_KEY, + RESOURCES_SECTION_KEY, HOW_TO_FIX_SECTION_KEY}; private static final String IGNORED_FAKE_SECTION = "fake_section_to_be_ignored"; public static final String[] CONTEXTS = {"spring", "hibernate", "apache-commons", "vaadin", "mybatis"}; @@ -55,6 +61,10 @@ public class EducationRulesDefinition implements RulesDefinition { createRuleWithDescriptionSectionsAndMultipleContexts(repo); createRuleWithDescriptionSectionsAndMultipleContextsIncludingOneDetected(repo); + createRuleWith2LinkedCodeSnippetsInTwoSections(repo); + + createRuleWith4LinkedCodeSnippetsInOneSection(repo); + repo.done(); } @@ -66,7 +76,7 @@ public class EducationRulesDefinition implements RulesDefinition { ruleWithSingleContext .setHtmlDescription(String.format("This rule contains description sections. To trigger an issue using this rule you need to " + - "scan any text file with a line containing %s key word. This rule also contains 2 education principles - %s and %s", + "scan any text file with a line containing %s key word. This rule also contains 2 education principles - %s and %s", ruleKey, educationPrinciples[0], educationPrinciples[1])) .addEducationPrincipleKeys(educationPrinciples); @@ -77,7 +87,7 @@ public class EducationRulesDefinition implements RulesDefinition { private void createRuleWithDescriptionSectionsAndSingleContext(NewRepository repo) { String ruleKey = EDUCATION_WITH_SINGLE_CONTEXT_RULE_KEY; NewRule ruleWithSingleContext = repo.createRule(ruleKey).setName("Rule with description sections and single " + - "contexts for 'how to fix'") + "contexts for 'how to fix'") .addTags(EDUCATION_KEY); ruleWithSingleContext @@ -96,7 +106,7 @@ public class EducationRulesDefinition implements RulesDefinition { ruleWithMultipleContexts .setHtmlDescription(String.format("This rule contains description sections and multiple contexts for 'how to fix' section. To trigger " + - "an issue using this rule you need to scan any text file with a line containing %s keyword.", + "an issue using this rule you need to scan any text file with a line containing %s keyword.", EducationWithContextsSensor.EDUCATION_WITH_CONTEXTS_RULE_KEY)); addNonContextualizedDescriptionSections(ruleWithMultipleContexts); @@ -113,7 +123,7 @@ public class EducationRulesDefinition implements RulesDefinition { ruleWithMultipleContexts .setHtmlDescription(String.format("This rule contains description sections and multiple contexts (including one detected) for " + - "'how to fix' section. To trigger an issue using this rule you need to scan any text file with a line containing %s keyword.", + "'how to fix' section. To trigger an issue using this rule you need to scan any text file with a line containing %s keyword.", EducationWithDetectedContextSensor.EDUCATION_WITH_DETECTED_CONTEXT_RULE_KEY)); addNonContextualizedDescriptionSections(ruleWithMultipleContexts); @@ -123,6 +133,46 @@ public class EducationRulesDefinition implements RulesDefinition { } } + + private void createRuleWith2LinkedCodeSnippetsInTwoSections(NewRepository repo) { + String ruleKey = EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY; + NewRule rule2CodeSnippetsIn2Sections = repo.createRule(ruleKey).setName("Rule with description sections and code snippets " + + "linked to each other in 2 different sections in order to display diff between them.") + .addTags(EDUCATION_KEY); + + rule2CodeSnippetsIn2Sections + .setHtmlDescription(String.format("This rule contains description sections and 2 linked code snippets in 2 sections. To " + + "trigger an issue using this rule you need to scan any text file with a line containing %s key word.", ruleKey)); + + String completelyDifferentSnippetsHtml = readResource("2completelyDifferentSnippets.html"); + String codeSnippetsHtml = readResource("2codeSnippets.html"); + + rule2CodeSnippetsIn2Sections.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY)) + .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY, completelyDifferentSnippetsHtml)) + .addDescriptionSection(descriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY)) + .addDescriptionSection(descriptionSection(HOW_TO_FIX_SECTION_KEY, codeSnippetsHtml)) + .addDescriptionSection(descriptionSection(RESOURCES_SECTION_KEY)); + } + + private void createRuleWith4LinkedCodeSnippetsInOneSection(NewRepository repo) { + String ruleKey = EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY; + NewRule rule2CodeSnippetsInTheSameSection = repo.createRule(ruleKey).setName("Rule with description sections and code snippets " + + "linked to each other in the same section in order to display diff between them.") + .addTags(EDUCATION_KEY); + + rule2CodeSnippetsInTheSameSection + .setHtmlDescription(String.format("This rule contains description sections and 4 linked code snippets in the same section. To " + + "trigger an issue using this rule you need to scan any text file with a line containing %s key word.", ruleKey)); + + String codeSnippetsHtml = readResource("4codeSnippets.html"); + + rule2CodeSnippetsInTheSameSection.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY)) + .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY, codeSnippetsHtml)) + .addDescriptionSection(descriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY)) + .addDescriptionSection(descriptionSection(HOW_TO_FIX_SECTION_KEY)) + .addDescriptionSection(descriptionSection(RESOURCES_SECTION_KEY)); + } + private static void addNonContextualizedDescriptionSections(NewRule newRule) { newRule.addDescriptionSection(descriptionSection(INTRODUCTION_SECTION_KEY)) .addDescriptionSection(descriptionSection(ROOT_CAUSE_SECTION_KEY)) @@ -141,9 +191,21 @@ public class EducationRulesDefinition implements RulesDefinition { } private static RuleDescriptionSection descriptionSection(String sectionKey) { + return descriptionSection(sectionKey, HTML_LOREM_IPSUM); + } + + private static RuleDescriptionSection descriptionSection(String sectionKey, String htmlContent) { return RuleDescriptionSection.builder() .sectionKey(sectionKey) - .htmlContent(String.format("%s: %s", sectionKey, HTML_LOREM_IPSUM)) + .htmlContent(htmlContent) .build(); } + + private String readResource(String file) { + try { + return IOUtils.toString(getClass().getResource(file), StandardCharsets.UTF_8); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensor.java new file mode 100644 index 00000000000..93e89c73e06 --- /dev/null +++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensor.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.education.sensors; + +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.rule.ActiveRules; + +public class EducationWith2LinkedCodeSnippetsSensor extends EducationRuleSensor { + + public static final String EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY = "2_LINKED_CODE_SNIPPETS"; + + public EducationWith2LinkedCodeSnippetsSensor(FileSystem fs, ActiveRules activeRules) { + super(fs, activeRules, EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY); + } +} diff --git a/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensor.java b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensor.java new file mode 100644 index 00000000000..62e222b3349 --- /dev/null +++ b/plugins/sonar-education-plugin/src/main/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensor.java @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.education.sensors; + +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.rule.ActiveRules; + +public class EducationWith4LinkedCodeSnippetsSensor extends EducationRuleSensor { + + public static final String EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY = "4_LINKED_CODE_SNIPPETS"; + + public EducationWith4LinkedCodeSnippetsSensor(FileSystem fs, ActiveRules activeRules) { + super(fs, activeRules, EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY); + } +} diff --git a/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2codeSnippets.html b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2codeSnippets.html new file mode 100644 index 00000000000..f54da216cb9 --- /dev/null +++ b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2codeSnippets.html @@ -0,0 +1,45 @@ +<p>An infinite loop is one that will never end while the program is running, i.e., you have to kill the program to get out of the loop. Whether it is + by meeting the loop’s end condition or via a <code>break</code>, every loop should have an end condition.</p> +<h3>Known Limitations</h3> +<ul> + <li> False positives: when <code>yield</code> is used - <a href="https://github.com/SonarSource/SonarJS/issues/674">Issue #674</a>. </li> + <li> False positives: when an exception is raised by a function invoked within the loop. </li> + <li> False negatives: when a loop condition is based on an element of an array or object. </li> +</ul> +<h2>Noncompliant Code Example</h2> +<pre class="diff-id-1 diff-noncompliant">for (;;) { // Noncompliant; end condition omitted + // ... +} + +var j = 0; +while (true) { // Noncompliant; constant end condition + j++; +} + +var k; +var b = true; +while (b) { // Noncompliant; constant end condition + k++; +} +</pre> +<h2>Compliant Solution</h2> +<pre class="diff-id-1 diff-noncompliant">while (true) { // break will potentially allow leaving the loop + if (someCondition) { + break; + } +} + +var k; +var b = true; +while (b) { + k++; + b = k < 10; +} + +outer: +while(true) { + while(true) { + break outer; + } +} +</pre>
\ No newline at end of file diff --git a/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2completelyDifferentSnippets.html b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2completelyDifferentSnippets.html new file mode 100644 index 00000000000..ef08cfed3ac --- /dev/null +++ b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/2completelyDifferentSnippets.html @@ -0,0 +1,23 @@ +Example with 2 completely different code snippets + +<h2>Noncompliant Code Example</h2> +<pre class="diff-id-1 diff-noncompliant"> +while (true) { // Noncompliant; constant end condition + j++; +} + +var k; +var b = true; +while (b) { // Noncompliant; constant end condition + k++; +} +</pre> +<h2>Compliant Solution</h2> +<pre class="diff-id-1 diff-compliant">while (true) { // break will potentially allow leaving the loop +var x = 3; +for(let i=2; i<4; i++) { + console.log("Hello there"); +} +alert('Click here'); +thisIsNiceFunction(); +</pre>
\ No newline at end of file diff --git a/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/4codeSnippets.html b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/4codeSnippets.html new file mode 100644 index 00000000000..b14c53f8602 --- /dev/null +++ b/plugins/sonar-education-plugin/src/main/resources/org/sonar/education/4codeSnippets.html @@ -0,0 +1,34 @@ +<p>A <code>catch</code> clause that only rethrows the caught exception has the same effect as omitting the <code>catch</code> altogether and letting + it bubble up automatically, but with more code and the additional detriment of leaving maintainers scratching their heads.</p> +<p>Such clauses should either be eliminated or populated with the appropriate logic.</p> +<h2>Noncompliant Code Example</h2> +<pre class="diff-id-1 diff-noncompliant">try { + doSomething(); +} catch (ex) { // Noncompliant + throw ex; +} +</pre> +<h2>Compliant Solution</h2> +<pre class="diff-id-1 diff-compliant">try { + doSomething(); +} catch (ex) { + console.err(ex); + throw ex; +} +</pre> + +<h2>Noncompliant Code Example</h2> +<pre class="diff-id-2 diff-noncompliant">try { + doSomethingElse(); +} catch (ex) { // Noncompliant + throw ex; +} +</pre> +<h2>Compliant Solution</h2> +<pre class="diff-id-2 diff-compliant">try { + doSomethingElse(); +} catch (ex) { + console.err(ex); + throw ex; +} +</pre>
\ No newline at end of file diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensorTest.java new file mode 100644 index 00000000000..466ad338179 --- /dev/null +++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith2LinkedCodeSnippetsSensorTest.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.education.sensors; + +import java.io.IOException; +import org.junit.Test; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.internal.SensorContextTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.education.sensors.EducationWith2LinkedCodeSnippetsSensor.EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY; + +public class EducationWith2LinkedCodeSnippetsSensorTest extends EducationSensorTest { + @Test + public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException { + DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_2_LINKED_CODE_SNIPPETS_RULE_KEY); + + DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder()); + fs.add(inputFile); + + SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath()); + var sensor = new EducationWith2LinkedCodeSnippetsSensor(fs, activeRules); + + sensor.execute(sensorContextTester); + + assertThat(sensorContextTester.allIssues()).hasSize(1); + } +} diff --git a/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensorTest.java b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensorTest.java new file mode 100644 index 00000000000..85e46d99a13 --- /dev/null +++ b/plugins/sonar-education-plugin/src/test/java/org/sonar/education/sensors/EducationWith4LinkedCodeSnippetsSensorTest.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.education.sensors; + +import java.io.IOException; +import org.junit.Test; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.internal.SensorContextTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.education.sensors.EducationWith4LinkedCodeSnippetsSensor.EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY; + +public class EducationWith4LinkedCodeSnippetsSensorTest extends EducationSensorTest { + + @Test + public void processFile_givenCorrectTagPassed_oneSecurityHotspotWithContextsIsRaised() throws IOException { + DefaultInputFile inputFile = newTestFile(EDUCATION_WITH_4_LINKED_CODE_SNIPPETS_RULE_KEY); + + DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder()); + fs.add(inputFile); + + SensorContextTester sensorContextTester = SensorContextTester.create(temp.newFolder().toPath()); + var sensor = new EducationWith4LinkedCodeSnippetsSensor(fs, activeRules); + + sensor.execute(sensorContextTester); + + assertThat(sensorContextTester.allIssues()).hasSize(1); + } +} |