Skip to content

Commit 0cdc634

Browse files
vilchik-elenapynicolas
authored andcommitted
SONARPY-145 Raise issues with precise location for SameBranchCheck
1 parent e0326f3 commit 0cdc634

4 files changed

Lines changed: 57 additions & 27 deletions

File tree

its/ruling/src/test/resources/expected/python-S1871.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
664,
1010
],
1111
'project:buildbot-0.8.6p1/contrib/bzr_buildbot.py':[
12-
221,
12+
229,
1313
],
1414
'project:django-1.4/django/contrib/auth/tests/auth_backends.py':[
1515
120,
@@ -49,7 +49,7 @@
4949
115,
5050
],
5151
'project:django-1.4/django/utils/regex_helper.py':[
52-
182,
52+
183,
5353
],
5454
'project:django-1.4/django/views/generic/date_based.py':[
5555
160,
@@ -78,7 +78,7 @@
7878
295,
7979
],
8080
'project:twisted-12.1.0/twisted/news/nntp.py':[
81-
904,
81+
905,
8282
],
8383
'project:twisted-12.1.0/twisted/persisted/aot.py':[
8484
491,
@@ -94,7 +94,7 @@
9494
111,
9595
],
9696
'project:twisted-12.1.0/twisted/test/test_doc.py':[
97-
67,
97+
69,
9898
],
9999
'project:twisted-12.1.0/twisted/trial/runner.py':[
100100
626,
@@ -106,7 +106,7 @@
106106
419,
107107
],
108108
'project:twisted-12.1.0/twisted/web/sux.py':[
109-
331,
109+
333,
110110
556,
111111
],
112112
'project:twisted-12.1.0/twisted/words/protocols/oscar.py':[

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

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,21 @@
2020
package org.sonar.python.checks;
2121

2222
import com.sonar.sslr.api.AstNode;
23-
import com.sonar.sslr.api.Grammar;
2423
import java.util.LinkedList;
2524
import java.util.List;
2625
import javax.annotation.Nullable;
2726
import org.sonar.check.Priority;
2827
import org.sonar.check.Rule;
28+
import org.sonar.python.PythonCheck;
2929
import org.sonar.python.api.PythonGrammar;
3030
import org.sonar.python.api.PythonKeyword;
31+
import org.sonar.python.api.PythonTokenType;
3132
import org.sonar.squidbridge.annotations.ActivatedByDefault;
3233
import org.sonar.squidbridge.annotations.SqaleConstantRemediation;
33-
import org.sonar.squidbridge.checks.SquidCheck;
3434
import org.sonar.sslr.ast.AstSelect;
3535

36+
import static org.sonar.python.api.PythonGrammar.STMT_LIST;
37+
3638
@Rule(
3739
key = SameBranchCheck.CHECK_KEY,
3840
priority = Priority.MAJOR,
@@ -41,7 +43,7 @@
4143
)
4244
@SqaleConstantRemediation("10min")
4345
@ActivatedByDefault
44-
public class SameBranchCheck extends SquidCheck<Grammar> {
46+
public class SameBranchCheck extends PythonCheck {
4547
public static final String CHECK_KEY = "S1871";
4648
public static final String MESSAGE = "Either merge this branch with the identical one on line \"%s\" or change one of the implementations.";
4749

@@ -91,15 +93,43 @@ private void findSameBranches(List<AstNode> branches) {
9193
}
9294

9395
private void checkBranch(List<AstNode> branches, int index) {
96+
AstNode duplicateBlock = branches.get(index);
9497
for (int j = 0; j < index; j++) {
95-
if (CheckUtils.equalNodes(branches.get(j), branches.get(index))) {
96-
String message = String.format(MESSAGE, branches.get(j).getToken().getLine() + 1);
97-
getContext().createLineViolation(this, message, branches.get(index).getToken().getLine() + 1);
98+
AstNode originalBlock = branches.get(j);
99+
if (CheckUtils.equalNodes(originalBlock, duplicateBlock)) {
100+
String message = String.format(MESSAGE, originalBlock.getToken().getLine() + 1);
101+
addIssue(location(duplicateBlock, message))
102+
.secondary(location(originalBlock, "Original"));
98103
return;
99104
}
100105
}
101106
}
102107

108+
private static IssueLocation location(AstNode suiteNode, String message) {
109+
AstNode firstStatement = suiteNode.getFirstChild(PythonGrammar.STATEMENT);
110+
if (firstStatement != null) {
111+
return new IssueLocation(firstStatement, getLastNode(suiteNode), message);
112+
} else {
113+
return new IssueLocation(suiteNode.getFirstChild(STMT_LIST), message);
114+
}
115+
}
116+
117+
/**
118+
* We need this method to avoid passing of new line or dedent as pointer to end of issue location
119+
*/
120+
private static AstNode getLastNode(AstNode node) {
121+
if (node.getNumberOfChildren() == 0) {
122+
return node;
123+
}
124+
125+
AstNode lastChild = node.getLastChild();
126+
while (lastChild.is(PythonTokenType.NEWLINE, PythonTokenType.DEDENT, PythonTokenType.INDENT)) {
127+
lastChild = lastChild.getPreviousSibling();
128+
}
129+
130+
return getLastNode(lastChild);
131+
}
132+
103133
private static AstNode singleIfChild(AstNode suite) {
104134
List<AstNode> statements = suite.getChildren(PythonGrammar.STATEMENT);
105135
if (statements.size() == 1) {

python-checks/src/test/java/org/sonar/python/checks/SameBranchCheckTest.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,14 @@
2121

2222
import java.io.File;
2323
import org.junit.Test;
24-
import org.sonar.python.PythonAstScanner;
25-
import org.sonar.squidbridge.api.SourceFile;
26-
import org.sonar.squidbridge.checks.CheckMessagesVerifier;
24+
import org.sonar.python.checks.utils.PythonCheckVerifier;
2725

2826
public class SameBranchCheckTest {
2927

3028
@Test
3129
public void test() {
3230
SameBranchCheck check = new SameBranchCheck();
33-
34-
SourceFile file = PythonAstScanner.scanSingleFile(new File("src/test/resources/checks/sameBranch.py"), check);
35-
CheckMessagesVerifier.verify(file.getCheckMessages())
36-
.next().atLine(9).withMessage(String.format(SameBranchCheck.MESSAGE, 5))
37-
.next().atLine(17).withMessage(String.format(SameBranchCheck.MESSAGE, 14))
38-
.next().atLine(26).withMessage(String.format(SameBranchCheck.MESSAGE, 21))
39-
.next().atLine(32).withMessage(String.format(SameBranchCheck.MESSAGE, 30))
40-
.noMore();
31+
PythonCheckVerifier.verify(new File("src/test/resources/checks/sameBranch.py"), check);
4132
}
4233

4334
}

python-checks/src/test/resources/checks/sameBranch.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,39 @@
33

44
if 0 <= a < 10:
55
print(1)
6+
foo()
67
elif 10 <= a < 20:
78
print(2)
89
elif 20 <= a < 50:
9-
print(1)
10+
print(1) # Noncompliant [[sc=5;ec=10;el=+1;secondary=-5]]
11+
foo()
1012
else:
1113
print(3)
1214

1315
if param == 1:
1416
print(1)
1517
else:
1618
if param == 2:
17-
print(1)
19+
print(1) # Noncompliant [[secondary=-3]]
1820

1921

2022
if param == 1:
21-
print(1)
23+
if True:
24+
pass
2225
else:
2326
if param == 2:
2427
print(2)
2528
else:
26-
print(1)
29+
if True: # Noncompliant [[secondary=-6]]
30+
pass
2731

2832
if True:
2933
if True:
3034
print(1)
3135
else:
32-
print(1)
36+
print(1) # Noncompliant {{Either merge this branch with the identical one on line "34" or change one of the implementations.}}
37+
38+
if 1: print("1"); foo()
39+
# Noncompliant@+1 [[secondary=-2;sc=9;ec=26;el=+0]]
40+
elif 2: print("1"); foo()
41+
else: print("2")

0 commit comments

Comments
 (0)