Skip to content

Commit 67792ef

Browse files
SONARPY-1285 Fix FP on S1451 when shebang head lines are used (SonarSource#1446)
1 parent a004b13 commit 67792ef

File tree

5 files changed

+57
-8
lines changed

5 files changed

+57
-8
lines changed

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
*/
2020
package org.sonar.python.checks;
2121

22-
import java.util.regex.Matcher;
2322
import java.util.regex.Pattern;
23+
import java.util.stream.Stream;
2424
import org.sonar.check.Rule;
2525
import org.sonar.check.RuleProperty;
2626
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
@@ -48,6 +48,7 @@ public class FileHeaderCopyrightCheck extends PythonSubscriptionCheck {
4848
defaultValue = "false")
4949
public boolean isRegularExpression = false;
5050
private Pattern searchPattern = null;
51+
private Pattern shebangPattern = Pattern.compile("^#![^\\n]+\\n", Pattern.MULTILINE);
5152

5253
@Override
5354
public void initialize(Context context) {
@@ -60,12 +61,16 @@ public void initialize(Context context) {
6061
}
6162
}
6263

63-
String retrievedText = getHeaderText(ctx);
64+
var header = getHeaderText(ctx);
65+
var headerWithoutShebang = shebangPattern.matcher(header).replaceFirst("");
6466

6567
if (isRegularExpression) {
66-
checkRegularExpression(ctx, retrievedText);
67-
} else {
68-
if (!headerFormat.isEmpty() && !retrievedText.startsWith(headerFormat)) {
68+
checkRegularExpression(ctx, header, headerWithoutShebang);
69+
} else if (!headerFormat.isEmpty()) {
70+
var matches = Stream.of(header, headerWithoutShebang)
71+
.anyMatch(h -> h.startsWith(headerFormat));
72+
73+
if (!matches) {
6974
ctx.addFileIssue(MESSAGE);
7075
}
7176
}
@@ -80,9 +85,12 @@ private static String getHeaderText(SubscriptionContext ctx) {
8085
return ctx.pythonFile().content();
8186
}
8287

83-
private void checkRegularExpression(SubscriptionContext ctx, String fileContent) {
84-
Matcher matcher = searchPattern.matcher(fileContent);
85-
if (!matcher.find() || matcher.start() != 0) {
88+
private void checkRegularExpression(SubscriptionContext ctx, String... fileContent) {
89+
var matches = Stream.of(fileContent)
90+
.map(searchPattern::matcher)
91+
.anyMatch(matcher -> matcher.find() && matcher.start() == 0);
92+
93+
if (!matches) {
8694
ctx.addFileIssue(MESSAGE);
8795
}
8896
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,22 @@ public void test_searchPattern_exception() {
141141
IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, () -> SubscriptionVisitor.analyze(check, context));
142142
Assertions.assertThat(e.getMessage()).isEqualTo("[FileHeaderCopyrightCheck] Unable to compile the regular expression: "+fileHeaderCopyrightCheck.headerFormat);
143143
}
144+
145+
@Test
146+
public void shebangTest() {
147+
var fileHeaderCopyrightCheck = new FileHeaderCopyrightCheck();
148+
fileHeaderCopyrightCheck.headerFormat = "# Copyright FOO\n";
149+
PythonCheckVerifier.verifyNoIssue("src/test/resources/checks/fileHeaderCopyright/shebangCopyright.py", fileHeaderCopyrightCheck);
150+
}
151+
152+
@Test
153+
public void shebangPatternTest() {
154+
var fileHeaderCopyrightCheck = new FileHeaderCopyrightCheck();
155+
fileHeaderCopyrightCheck.isRegularExpression = true;
156+
fileHeaderCopyrightCheck.headerFormat = "# Copyright[ ]20[0-9]{2}";
157+
PythonCheckVerifier.verifyNoIssue("src/test/resources/checks/fileHeaderCopyright/searchPatternShebangCopyright.py",
158+
fileHeaderCopyrightCheck);
159+
PythonCheckVerifier.verify("src/test/resources/checks/fileHeaderCopyright/searchPatternShebangCopyrightNonCompliant.py",
160+
fileHeaderCopyrightCheck);
161+
}
144162
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2020
3+
# All rights reserved.
4+
5+
def foo():
6+
pass
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env python3
2+
# Copyright_2020
3+
# All rights reserved.
4+
# Noncompliant@-4
5+
def foo():
6+
pass
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env python3
2+
# Copyright FOO
3+
4+
def fun():
5+
if expression:
6+
pass
7+
if expression:
8+
pass
9+
if expression:
10+
pass
11+
return

0 commit comments

Comments
 (0)