Skip to content

Commit 5a7b985

Browse files
joke1196sonartech
authored andcommitted
SONARPY-2443: Fix NPE in SingleCharCharacterClassCheck (#340)
GitOrigin-RevId: 21eb656705d804c9336a54cc51084d9fdf9de43b
1 parent 37bd9c1 commit 5a7b985

6 files changed

Lines changed: 55 additions & 54 deletions

File tree

python-checks/src/main/java/org/sonar/python/checks/regex/AbstractRegexCheck.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,13 @@ public static Optional<Integer> mapPythonFlag(QualifiedExpression ch) {
208208
return Optional.ofNullable(result);
209209
}
210210

211-
public PreciseIssue addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
211+
public Optional<PreciseIssue> addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
212212
if (reportedRegexTrees.add(regexTree)) {
213213
PreciseIssue issue = regexContext.addIssue(regexTree, message);
214214
secondaries.stream().map(PythonRegexIssueLocation::preciseLocation).forEach(issue::secondary);
215215
// TODO: Add cost to the issue
216-
return issue;
216+
return Optional.of(issue);
217217
}
218-
return null;
218+
return Optional.empty();
219219
}
220220
}

python-checks/src/main/java/org/sonar/python/checks/regex/MultipleWhitespaceCheck.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,18 @@ public void checkRegex(RegexParseResult regexParseResult, CallExpression regexFu
4545
}
4646

4747
@Override
48-
public PreciseIssue addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
49-
var issue = super.addIssue(regexTree, message, cost, secondaries);
50-
51-
var whitespacesCount = regexTree.getRange().getEndingOffset() - regexTree.getRange().getBeginningOffset() + 1;
52-
var quickFixReplacement = String.format("{%d}", whitespacesCount);
53-
var issueLocation = PythonRegexIssueLocation.preciseLocation(regexTree, null);
54-
var textEdit = new PythonTextEdit(quickFixReplacement,
55-
issueLocation.startLine(),
56-
issueLocation.startLineOffset(),
57-
issueLocation.endLine(),
58-
issueLocation.endLineOffset());
59-
issue.addQuickFix(PythonQuickFix.newQuickFix(String.format(QUICK_FIX_FORMAT, quickFixReplacement), textEdit));
60-
return issue;
48+
public Optional<PreciseIssue> addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
49+
return super.addIssue(regexTree, message, cost, secondaries).map(issue -> {
50+
var whitespacesCount = regexTree.getRange().getEndingOffset() - regexTree.getRange().getBeginningOffset() + 1;
51+
var quickFixReplacement = String.format("{%d}", whitespacesCount);
52+
var issueLocation = PythonRegexIssueLocation.preciseLocation(regexTree, null);
53+
var textEdit = new PythonTextEdit(quickFixReplacement,
54+
issueLocation.startLine(),
55+
issueLocation.startLineOffset(),
56+
issueLocation.endLine(),
57+
issueLocation.endLineOffset());
58+
issue.addQuickFix(PythonQuickFix.newQuickFix(String.format(QUICK_FIX_FORMAT, quickFixReplacement), textEdit));
59+
return issue;
60+
});
6161
}
6262
}
63-

python-checks/src/main/java/org/sonar/python/checks/regex/SingleCharCharacterClassCheck.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,15 @@ public void checkRegex(RegexParseResult regexParseResult, CallExpression regexFu
4444
}
4545

4646
@Override
47-
public PreciseIssue addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
48-
var issue = super.addIssue(regexTree, message, cost, secondaries);
49-
var quickFixReplacement = regexTree.getText();
50-
var issueLocation = PythonRegexIssueLocation.preciseLocation(regexTree, null);
51-
var textEdit = new PythonTextEdit(quickFixReplacement,
52-
issueLocation.startLine(), issueLocation.startLineOffset() - 1,
53-
issueLocation.endLine(), issueLocation.endLineOffset() + 1);
54-
issue.addQuickFix(PythonQuickFix.newQuickFix(QUICK_FIX_MESSAGE, textEdit));
55-
return issue;
47+
public Optional<PreciseIssue> addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
48+
return super.addIssue(regexTree, message, cost, secondaries).map(issue -> {
49+
var quickFixReplacement = regexTree.getText();
50+
var issueLocation = PythonRegexIssueLocation.preciseLocation(regexTree, null);
51+
var textEdit = new PythonTextEdit(quickFixReplacement,
52+
issueLocation.startLine(), issueLocation.startLineOffset() - 1,
53+
issueLocation.endLine(), issueLocation.endLineOffset() + 1);
54+
issue.addQuickFix(PythonQuickFix.newQuickFix(QUICK_FIX_MESSAGE, textEdit));
55+
return issue;
56+
});
5657
}
5758
}

python-checks/src/main/java/org/sonar/python/checks/regex/UnquantifiedNonCapturingGroupCheck.java

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,23 @@ public void checkRegex(RegexParseResult regexParseResult, CallExpression regexFu
4141
}
4242

4343
@Override
44-
public PreciseIssue addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
45-
var issue = super.addIssue(regexTree, message, cost, secondaries);
46-
47-
Optional.of(regexTree)
48-
.filter(NonCapturingGroupTree.class::isInstance)
49-
.map(NonCapturingGroupTree.class::cast)
50-
.filter(group -> Objects.nonNull(group.getElement()))
51-
.map(group -> {
52-
var quickFixReplacement = group.getElement().getText();
53-
var issueLocation = PythonRegexIssueLocation.preciseLocation(group, null);
54-
var textEdit = new PythonTextEdit(quickFixReplacement,
55-
issueLocation.startLine(),
56-
issueLocation.startLineOffset(),
57-
issueLocation.endLine(),
58-
issueLocation.endLineOffset());
59-
return PythonQuickFix.newQuickFix(QUICK_FIX_MESSAGE, textEdit);
60-
}).ifPresent(issue::addQuickFix);
61-
62-
return issue;
44+
public Optional<PreciseIssue> addIssue(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
45+
return super.addIssue(regexTree, message, cost, secondaries).map(issue -> {
46+
Optional.of(regexTree)
47+
.filter(NonCapturingGroupTree.class::isInstance)
48+
.map(NonCapturingGroupTree.class::cast)
49+
.filter(group -> Objects.nonNull(group.getElement()))
50+
.map(group -> {
51+
var quickFixReplacement = group.getElement().getText();
52+
var issueLocation = PythonRegexIssueLocation.preciseLocation(group, null);
53+
var textEdit = new PythonTextEdit(quickFixReplacement,
54+
issueLocation.startLine(),
55+
issueLocation.startLineOffset(),
56+
issueLocation.endLine(),
57+
issueLocation.endLineOffset());
58+
return PythonQuickFix.newQuickFix(QUICK_FIX_MESSAGE, textEdit);
59+
}).ifPresent(issue::addQuickFix);
60+
return issue;
61+
});
6362
}
6463
}

python-checks/src/main/java/org/sonar/python/checks/regex/VerboseRegexCheck.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ public void checkRegex(RegexParseResult regexParseResult, CallExpression regexFu
5656
new PythonVerboseRegexRepetitionCheckVisitor().visit(regexParseResult);
5757
}
5858

59-
public PreciseIssue addIssueWithQuickFix(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
60-
return Optional.ofNullable(addIssue(regexTree, message, cost, secondaries))
59+
public Optional<PreciseIssue> addIssueWithQuickFix(RegexSyntaxElement regexTree, String message, @Nullable Integer cost, List<RegexIssueLocation> secondaries) {
60+
return addIssue(regexTree, message, cost, secondaries)
6161
.map(issue -> {
6262
Matcher matcher = issueMessagePattern.matcher(message);
6363
String quickFixReplacement = matcher.replaceFirst("$1");
@@ -71,8 +71,7 @@ public PreciseIssue addIssueWithQuickFix(RegexSyntaxElement regexTree, String me
7171
issueLocation.endLineOffset());
7272
issue.addQuickFix(PythonQuickFix.newQuickFix(String.format(QUICK_FIX_FORMAT, quickFixReplacement), textEdit));
7373
return issue;
74-
})
75-
.orElse(null);
74+
});
7675
}
7776

7877
private class PythonVerboseRegexRangeCheckVisitor extends RegexBaseVisitor {
@@ -89,8 +88,9 @@ public void visitCharacterRange(CharacterRangeTree tree) {
8988
issueLocation.endLine(),
9089
issueLocation.endLineOffset());
9190

92-
var issue = addIssue(tree, String.format(REDUNDANT_RANGE_MESSAGE, quickFixReplacement, tree.getText()), null, Collections.emptyList());
93-
issue.addQuickFix(PythonQuickFix.newQuickFix(String.format(QUICK_FIX_FORMAT, quickFixReplacement), textEdit));
91+
addIssue(tree, String.format(REDUNDANT_RANGE_MESSAGE, quickFixReplacement, tree.getText()), null, Collections.emptyList())
92+
.ifPresent(issue -> issue.addQuickFix(PythonQuickFix.newQuickFix(String.format(QUICK_FIX_FORMAT, quickFixReplacement), textEdit)));
93+
9494
}
9595
super.visitCharacterRange(tree);
9696
}
@@ -120,9 +120,8 @@ public void visit(RegexTree tree) {
120120
repetitionLocation.endLineOffset());
121121

122122
var issueMessage = String.format(REDUNDANT_REPETITION_MESSAGE, treeText + quickFixReplacement, treeText + repetition.getText());
123-
var issue = addIssue(repetition, issueMessage, null,
124-
List.of(new RegexIssueLocation(tree, REDUNDANT_REPETITION_SECONDARY_LOCATION_MESSAGE)));
125-
issue.addQuickFix(PythonQuickFix.newQuickFix(String.format(QUICK_FIX_FORMAT, quickFixReplacement), textEdit));
123+
addIssue(repetition, issueMessage, null, List.of(new RegexIssueLocation(tree, REDUNDANT_REPETITION_SECONDARY_LOCATION_MESSAGE)))
124+
.ifPresent(issue -> issue.addQuickFix(PythonQuickFix.newQuickFix(String.format(QUICK_FIX_FORMAT, quickFixReplacement), textEdit)));
126125
}
127126
});
128127
super.visit(tree);

python-checks/src/test/resources/checks/regex/singleCharCharacterClassCheck.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ def non_compliant():
2222
# ^^
2323
changed = re.match(r'[b][c]', input, re.M) # Noncompliant 2
2424

25+
regex = r"agency_id=[a-zA-Z0-9_-]{3,18}[=]?" # Noncompliant
26+
matches = re.findall(regex, "")
27+
matches = re.findall(regex, "") # This checks if adding twice the issue to the regex tree is safe
2528

2629
def compliant():
2730
input = "abcdefghijklmnopqa"

0 commit comments

Comments
 (0)