Skip to content

Commit 816716c

Browse files
marco-bearzi-sonarsourcepynicolasnils-werner-sonarsource
authored
SONARPY-234: Rule 1291: Track uses of 'NOSONAR' comments (#1089)
* Rule 1291: Track uses of 'NOSONAR' comments * Rule 1291: Track uses of 'NOSONAR' comments * Rule 1291: Fix coverage * Add integration test for Rule 1291 and reduce redundancy in ITS * Update python-checks/src/main/java/org/sonar/python/checks/NoSonarCommentCheck.java Co-authored-by: pynicolas <[email protected]> * Remove S1291 rule * Update warning message Co-authored-by: Nils Werner <[email protected]> * Add message to test file Co-authored-by: pynicolas <[email protected]> Co-authored-by: Nils Werner <[email protected]>
1 parent bbdaf5e commit 816716c

File tree

13 files changed

+161
-39
lines changed

13 files changed

+161
-39
lines changed

its/plugin/it-python-plugin-test/profiles/nosonar.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@
77
<key>PrintStatementUsage</key>
88
<priority>INFO</priority>
99
</rule>
10+
<rule>
11+
<repositoryKey>python</repositoryKey>
12+
<key>NoSonar</key>
13+
<priority>INFO</priority>
14+
</rule>
1015
</rules>
1116
</profile>

its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/BanditReportTest.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@
2727
import org.junit.Test;
2828
import org.sonarqube.ws.Common;
2929
import org.sonarqube.ws.Issues;
30-
import org.sonarqube.ws.client.issues.SearchRequest;
3130

32-
import static com.sonar.python.it.plugin.Tests.newWsClient;
33-
import static java.util.Collections.singletonList;
31+
import static com.sonar.python.it.plugin.Tests.issues;
3432
import static org.assertj.core.api.Assertions.assertThat;
3533

3634
public class BanditReportTest {
@@ -48,7 +46,7 @@ public void import_report() {
4846
SonarScanner.create()
4947
.setProjectDir(new File("projects/bandit_project")));
5048

51-
List<Issues.Issue> issues = issues();
49+
List<Issues.Issue> issues = issues(PROJECT);
5250
assertThat(issues).hasSize(1);
5351
Issues.Issue issue = issues.get(0);
5452
assertThat(issue.getComponent()).isEqualTo("bandit_project:src/file1.py");
@@ -59,8 +57,4 @@ public void import_report() {
5957
assertThat(issue.getEffort()).isEqualTo("5min");
6058
}
6159

62-
private static List<Issues.Issue> issues() {
63-
return newWsClient().issues().search(new SearchRequest().setProjects(singletonList(PROJECT))).getIssuesList();
64-
}
65-
6660
}

its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Flake8ReportTest.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@
2727
import org.junit.Test;
2828
import org.sonarqube.ws.Common;
2929
import org.sonarqube.ws.Issues;
30-
import org.sonarqube.ws.client.issues.SearchRequest;
3130

32-
import static com.sonar.python.it.plugin.Tests.newWsClient;
33-
import static java.util.Collections.singletonList;
31+
import static com.sonar.python.it.plugin.Tests.issues;
3432
import static org.assertj.core.api.Assertions.assertThat;
3533

3634
public class Flake8ReportTest {
@@ -48,7 +46,7 @@ public void import_report() {
4846
SonarScanner.create()
4947
.setProjectDir(new File("projects/flake8_project")));
5048

51-
List<Issues.Issue> issues = issues();
49+
List<Issues.Issue> issues = issues(PROJECT);
5250
assertThat(issues).hasSize(5);
5351
Issues.Issue issue = issues.get(0);
5452
assertThat(issue.getComponent()).isEqualTo("flake8_project:src/file1.py");
@@ -68,7 +66,4 @@ public void import_report() {
6866
assertThat(issue.getEffort()).isEqualTo("5min");
6967
}
7068

71-
private static List<Issues.Issue> issues() {
72-
return newWsClient().issues().search(new SearchRequest().setProjects(singletonList(PROJECT))).getIssuesList();
73-
}
7469
}

its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/NoSonarTest.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
import com.sonar.orchestrator.Orchestrator;
2323
import com.sonar.orchestrator.build.SonarScanner;
2424
import java.io.File;
25+
import java.util.List;
2526
import org.junit.BeforeClass;
2627
import org.junit.ClassRule;
2728
import org.junit.Test;
29+
import org.sonarqube.ws.Issues;
2830

29-
import static com.sonar.python.it.plugin.Tests.getMeasureAsInt;
31+
import static com.sonar.python.it.plugin.Tests.issues;
3032
import static org.assertj.core.api.Assertions.assertThat;
3133

3234
public class NoSonarTest {
@@ -52,12 +54,11 @@ public static void startServer() {
5254

5355
@Test
5456
public void test_nosonar() {
55-
assertThat(getProjectMeasureAsInt("violations")).isEqualTo(1);
56-
}
57-
58-
/* Helper methods */
59-
60-
private Integer getProjectMeasureAsInt(String metricKey) {
61-
return getMeasureAsInt(PROJECT_KEY, metricKey);
57+
List<Issues.Issue> issues = issues(PROJECT_KEY);
58+
assertThat(issues).hasSize(2);
59+
assertThat(issues.get(0).getRule()).isEqualTo("python:PrintStatementUsage");
60+
assertThat(issues.get(0).getLine()).isEqualTo(1);
61+
assertThat(issues.get(1).getRule()).isEqualTo("python:NoSonar");
62+
assertThat(issues.get(1).getLine()).isEqualTo(2);
6263
}
6364
}

its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/PylintReportTest.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,10 @@
2323
import com.sonar.orchestrator.build.BuildResult;
2424
import com.sonar.orchestrator.build.SonarScanner;
2525
import java.io.File;
26-
import java.util.List;
2726
import org.junit.ClassRule;
2827
import org.junit.Test;
29-
import org.sonarqube.ws.Issues.Issue;
30-
import org.sonarqube.ws.client.issues.SearchRequest;
3128

32-
import static com.sonar.python.it.plugin.Tests.newWsClient;
33-
import static java.util.Collections.singletonList;
29+
import static com.sonar.python.it.plugin.Tests.issues;
3430
import static org.assertj.core.api.Assertions.assertThat;
3531

3632
public class PylintReportTest {
@@ -84,10 +80,6 @@ public void multiple_reports() {
8480
assertThat(issues(projectKey)).hasSize(8);
8581
}
8682

87-
private static List<Issue> issues(String projectKey) {
88-
return newWsClient().issues().search(new SearchRequest().setProjects(singletonList(projectKey))).getIssuesList();
89-
}
90-
9183
private static BuildResult analyseProjectWithReport(String projectKey, String property, String reportPaths) {
9284
ORCHESTRATOR.getServer().provisionProject(projectKey, projectKey);
9385
ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "py", "no_rule");

its/plugin/it-python-plugin-test/src/test/java/com/sonar/python/it/plugin/Tests.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@
3131
import org.junit.ClassRule;
3232
import org.junit.runner.RunWith;
3333
import org.junit.runners.Suite;
34+
import org.sonarqube.ws.Issues;
3435
import org.sonarqube.ws.Measures.ComponentWsResponse;
3536
import org.sonarqube.ws.Measures.Measure;
3637
import org.sonarqube.ws.client.HttpConnector;
3738
import org.sonarqube.ws.client.WsClient;
3839
import org.sonarqube.ws.client.WsClientFactories;
40+
import org.sonarqube.ws.client.issues.SearchRequest;
3941
import org.sonarqube.ws.client.measures.ComponentRequest;
4042

41-
import static com.sonar.orchestrator.container.Server.ADMIN_LOGIN;
42-
import static com.sonar.orchestrator.container.Server.ADMIN_PASSWORD;
4343
import static java.lang.Double.parseDouble;
4444
import static java.util.Collections.singletonList;
4545
import static org.assertj.core.api.Assertions.assertThat;
@@ -123,15 +123,14 @@ static WsClient newWsClient() {
123123
return newWsClient(null, null);
124124
}
125125

126-
static WsClient newAdminWsClient() {
127-
return newWsClient(ADMIN_LOGIN, ADMIN_PASSWORD);
128-
}
129-
130126
static WsClient newWsClient(String login, String password) {
131127
return WsClientFactories.getDefault().newClient(HttpConnector.newBuilder()
132128
.url(ORCHESTRATOR.getServer().getUrl())
133129
.credentials(login, password)
134130
.build());
135131
}
136132

133+
static List<Issues.Issue> issues(String projectKey) {
134+
return newWsClient().issues().search(new SearchRequest().setProjects(singletonList(projectKey))).getIssuesList();
135+
}
137136
}

python-checks/src/main/java/org/sonar/python/checks/CheckList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ public static Iterable<Class> getChecks() {
186186
NonStringInAllPropertyCheck.class,
187187
NoPersonReferenceInTodoCheck.class,
188188
NoReRaiseInExitCheck.class,
189+
NoSonarCommentCheck.class,
189190
NotImplementedErrorInOperatorMethodsCheck.class,
190191
OneStatementPerLineCheck.class,
191192
OsExecCheck.class,
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2022 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.checks;
21+
22+
import org.sonar.check.Rule;
23+
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
24+
import org.sonar.plugins.python.api.tree.Token;
25+
import org.sonar.plugins.python.api.tree.Tree;
26+
import org.sonar.plugins.python.api.tree.Trivia;
27+
28+
import static org.sonar.python.metrics.FileLinesVisitor.containsNoSonarComment;
29+
30+
31+
@Rule(key = "NoSonar")
32+
33+
public class NoSonarCommentCheck extends PythonSubscriptionCheck {
34+
35+
private static final String MESSAGE = "Is #NOSONAR used to exclude false-positive or to hide real quality flaw?";
36+
37+
@Override
38+
public void initialize(Context context) {
39+
context.registerSyntaxNodeConsumer(Tree.Kind.TOKEN, ctx -> {
40+
Token token = (Token) ctx.syntaxNode();
41+
for (Trivia trivia : token.trivia()) {
42+
if (containsNoSonarComment(trivia)) {
43+
ctx.addIssue(trivia.token(), MESSAGE);
44+
}
45+
}
46+
});
47+
}
48+
}
49+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<p>Any issue to quality rule can be deactivated with the <code>NOSONAR</code> marker. This marker is pretty useful to exclude false-positive results
2+
but it can also be used abusively to hide real quality flaws.</p>
3+
<p>This rule raises an issue when <code>NOSONAR</code> is used.</p>
4+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"title": "Track uses of \"NOSONAR\" comments",
3+
"type": "CODE_SMELL",
4+
"status": "ready",
5+
"remediation": {
6+
"func": "Constant\/Issue",
7+
"constantCost": "1min"
8+
},
9+
"tags": [
10+
"bad-practice"
11+
],
12+
"defaultSeverity": "Major",
13+
"ruleSpecification": "RSPEC-1291",
14+
"sqKey": "NoSonar",
15+
"scope": "All",
16+
"quickfix": "unknown"
17+
}

0 commit comments

Comments
 (0)