Skip to content

Commit ac9027f

Browse files
SONARPY-542 Validate behavior of S4784 and S2077 with assignment expr… (SonarSource#631)
1 parent 8af5fd8 commit ac9027f

5 files changed

Lines changed: 41 additions & 7 deletions

File tree

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,6 @@
132132
'project:buildbot-slave-0.8.6p1/buildslave/scripts/runner.py':[
133133
160,
134134
],
135-
'project:django-2.2.3/django/contrib/admin/options.py':[
136-
1641,
137-
],
138135
'project:django-2.2.3/django/contrib/admindocs/urls.py':[
139136
41,
140137
],
@@ -151,6 +148,7 @@
151148
87,
152149
],
153150
'project:django-2.2.3/django/contrib/gis/geometry.py':[
151+
7,
154152
13,
155153
],
156154
'project:django-2.2.3/django/contrib/humanize/templatetags/humanize.py':[
@@ -219,6 +217,9 @@
219217
14,
220218
18,
221219
23,
220+
30,
221+
42,
222+
57,
222223
],
223224
'project:django-2.2.3/django/utils/html.py':[
224225
22,
@@ -278,6 +279,7 @@
278279
2791,
279280
],
280281
'project:numpy-1.16.4/numpy/core/_internal.py':[
282+
146,
281283
150,
282284
],
283285
'project:numpy-1.16.4/numpy/core/code_generators/genapi.py':[
@@ -299,6 +301,7 @@
299301
158,
300302
159,
301303
160,
304+
265,
302305
],
303306
'project:numpy-1.16.4/numpy/distutils/cpuinfo.py':[
304307
218,
@@ -394,7 +397,6 @@
394397
604,
395398
606,
396399
609,
397-
620,
398400
640,
399401
734,
400402
872,

python-checks/src/main/java/org/sonar/python/checks/hotspots/RegexCheck.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2828
import org.sonar.plugins.python.api.SubscriptionContext;
2929
import org.sonar.plugins.python.api.tree.Argument;
30+
import org.sonar.plugins.python.api.tree.AssignmentExpression;
3031
import org.sonar.plugins.python.api.tree.BinaryExpression;
3132
import org.sonar.plugins.python.api.tree.CallExpression;
3233
import org.sonar.plugins.python.api.tree.Expression;
@@ -61,7 +62,7 @@ public void initialize(Context context) {
6162
}
6263

6364
private static void checkRegexArgument(Argument arg, SubscriptionContext ctx) {
64-
String literal = arg.firstToken().value();
65+
String literal = null;
6566
IssueLocation secondaryLocation = null;
6667
if (!arg.is(Tree.Kind.REGULAR_ARGUMENT)) {
6768
return;
@@ -73,6 +74,11 @@ private static void checkRegexArgument(Argument arg, SubscriptionContext ctx) {
7374
secondaryLocation = IssueLocation.preciseLocation(expression, "");
7475
literal = Expressions.unescape((StringLiteral) expression);
7576
}
77+
} else if (argExpression.is(Tree.Kind.STRING_LITERAL)) {
78+
literal = Expressions.unescape((StringLiteral) argExpression);
79+
}
80+
if (literal == null) {
81+
return;
7682
}
7783
if (isSuspiciousRegex(literal)) {
7884
PreciseIssue preciseIssue = ctx.addIssue(arg, MESSAGE);
@@ -83,12 +89,16 @@ private static void checkRegexArgument(Argument arg, SubscriptionContext ctx) {
8389
}
8490

8591
private static Expression getExpression(@Nullable Expression expr) {
92+
expr = Expressions.removeParentheses(expr);
8693
if (expr == null) {
8794
return null;
8895
}
8996
if (expr.is(Tree.Kind.MODULO) || expr.is(Tree.Kind.PLUS)) {
9097
return getExpression(((BinaryExpression) expr).leftOperand());
9198
}
99+
if (expr.is(Tree.Kind.ASSIGNMENT_EXPRESSION)) {
100+
return getExpression(((AssignmentExpression) expr).expression());
101+
}
92102
return expr;
93103
}
94104

python-checks/src/main/java/org/sonar/python/checks/hotspots/SQLQueriesCheck.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
import java.util.Objects;
2525
import java.util.Optional;
2626
import java.util.Set;
27+
import javax.annotation.Nullable;
2728
import org.sonar.check.Rule;
2829
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
2930
import org.sonar.plugins.python.api.SubscriptionContext;
3031
import org.sonar.plugins.python.api.tree.AliasedName;
3132
import org.sonar.plugins.python.api.tree.Argument;
33+
import org.sonar.plugins.python.api.tree.AssignmentExpression;
3234
import org.sonar.plugins.python.api.tree.BaseTreeVisitor;
3335
import org.sonar.plugins.python.api.tree.BinaryExpression;
3436
import org.sonar.plugins.python.api.tree.CallExpression;
@@ -134,7 +136,7 @@ private static Optional<Tree> sensitiveArgumentValue(CallExpression callExpressi
134136
if (!arg.is(Tree.Kind.REGULAR_ARGUMENT)) {
135137
return Optional.empty();
136138
}
137-
Expression expression = ((RegularArgument) arg).expression();
139+
Expression expression = getExpression(((RegularArgument) arg).expression());
138140
if (expression.is(Tree.Kind.NAME)) {
139141
expression = Expressions.singleAssignedValue((Name) expression);
140142
}
@@ -166,6 +168,14 @@ private static boolean isAssignment(RegularArgument arg) {
166168
return arg.equalToken() != null;
167169
}
168170

171+
private static Expression getExpression(@Nullable Expression expr) {
172+
expr = Expressions.removeParentheses(expr);
173+
if (expr.is(Tree.Kind.ASSIGNMENT_EXPRESSION)) {
174+
return getExpression(((AssignmentExpression) expr).expression());
175+
}
176+
return expr;
177+
}
178+
169179
private static class FormattedStringVisitor extends BaseTreeVisitor {
170180
boolean hasFormattedString = false;
171181

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ def build_validator(regex):
1616
RegexValidator('(a{1})+') # Noncompliant
1717
RegexValidator('(a+)+') # Noncompliant
1818
RegexValidator('(a{1}){2}') # Noncompliant
19-
19+
RegexValidator(x:='(a+)+') # Noncompliant
20+
RegexValidator((x:='(a+)+')) # Noncompliant
21+
RegexValidator(x:='a+')
22+
RegexValidator(42)
2023

2124

2225
def define_http_endpoint(path, view):
@@ -25,6 +28,8 @@ def define_http_endpoint(path, view):
2528
RegexValidator(regexp) # Noncompliant [[secondary=-1]]
2629
# ^^^^^^
2730
RegexValidator(*path)
31+
something = 42
32+
RegexValidator(something)
2833

2934
import re
3035
from re import compile
@@ -58,3 +63,6 @@ def test_valid_numpy_version():
5863
res = re.match(version_pattern, np.__version__) # Noncompliant
5964
else:
6065
res = re.match(version_pattern + dev_suffix, np.__version__) # Noncompliant
66+
67+
def formatted_regex():
68+
pattern = re.compile(r'{}-\d+-{}$'.format(foo, bar))

python-checks/src/test/resources/checks/hotspots/sqlQuery.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ def query_my_user(request, params):
1919
MyUser.objects.raw('SELECT * FROM mytable WHERE name = "%s"'.format(value)) # Noncompliant
2020
MyUser.objects.raw(f"SELECT * FROM mytable WHERE name = '{value}'") # Noncompliant
2121
MyUser.objects.raw(F"SELECT * FROM mytable WHERE name = '{value}'") # Noncompliant
22+
MyUser.objects.raw(x := F"SELECT * FROM mytable WHERE name = '{value}'") # Noncompliant
2223
MyUser.objects.raw(request) # OK
2324
MyUser.objects.raw(hardcoded_request) # OK
25+
MyUser.objects.raw(x := hardcoded_request) # OK
2426
MyUser.objects.raw(formatted_request) # Noncompliant [[secondary=-14]]
2527
MyUser.objects.raw(formatted_request2) # Noncompliant
2628
MyUser.objects.raw(formatted_request3) # Noncompliant
29+
MyUser.objects.raw(y := formatted_request3) # Noncompliant
30+
MyUser.objects.raw((y := formatted_request3)) # Noncompliant
2731
MyUser.objects.raw(formatted_request4) # FN, multiple assignments
2832
MyUser.objects.raw(formatted_request5) # OK
2933
MyUser.objects.raw(*formatted_request5) # OK

0 commit comments

Comments
 (0)