Skip to content

Commit d1d1e51

Browse files
SONARPY-215 Rule S3699: The output of functions that don't return anything should not be used (#1100)
1 parent 9967790 commit d1d1e51

File tree

12 files changed

+191
-0
lines changed

12 files changed

+191
-0
lines changed

its/ruling/src/test/java/org/sonar/python/it/RulingHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ static List<String> bugRuleKeys() {
6262
"S2757",
6363
"S2823",
6464
"S3403",
65+
"S3699",
6566
"S3827",
6667
"S3862",
6768
"S3923",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
'project:django-2.2.3/django/core/files/locks.py':[
3+
108,
4+
112,
5+
],
6+
'project:numpy-1.16.4/numpy/core/records.py':[
7+
482,
8+
],
9+
'project:numpy-1.16.4/numpy/ma/mrecords.py':[
10+
259,
11+
],
12+
'project:tensorflow/python/data/kernel_tests/interleave_test.py':[
13+
297,
14+
297,
15+
],
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
'calibre:src/calibre/ebooks/metadata/xisbn.py':[
3+
106,
4+
],
5+
'calibre:src/calibre/ebooks/oeb/polish/tests/container.py':[
6+
100,
7+
],
8+
'calibre:src/calibre/ebooks/snb/snbfile.py':[
9+
89,
10+
],
11+
'calibre:src/calibre/gui2/open_with.py':[
12+
325,
13+
],
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
'nltk:nltk/app/collocations_app.py':[
3+
119,
4+
],
5+
'nltk:nltk/app/concordance_app.py':[
6+
277,
7+
],
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
'salt:salt/cloud/clouds/linode.py':[
3+
1814,
4+
],
5+
'salt:salt/utils/http.py':[
6+
453,
7+
],
8+
}

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
@@ -252,6 +252,7 @@ public static Iterable<Class> getChecks() {
252252
UselessParenthesisAfterKeywordCheck.class,
253253
UselessParenthesisCheck.class,
254254
UselessStatementCheck.class,
255+
UseOfEmptyReturnValueCheck.class,
255256
VerifiedSslTlsCertificateCheck.class,
256257
WeakSSLProtocolCheck.class,
257258
WildcardImportCheck.class,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 java.util.Optional;
23+
import org.sonar.check.Rule;
24+
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
25+
import org.sonar.plugins.python.api.SubscriptionContext;
26+
import org.sonar.plugins.python.api.tree.AssignmentStatement;
27+
import org.sonar.plugins.python.api.tree.CallExpression;
28+
import org.sonar.plugins.python.api.tree.Expression;
29+
import org.sonar.plugins.python.api.tree.RegularArgument;
30+
import org.sonar.python.types.InferredTypes;
31+
32+
import static org.sonar.plugins.python.api.tree.Tree.Kind.ASSIGNMENT_STMT;
33+
import static org.sonar.plugins.python.api.tree.Tree.Kind.CALL_EXPR;
34+
import static org.sonar.plugins.python.api.tree.Tree.Kind.REGULAR_ARGUMENT;
35+
36+
@Rule(key = "S3699")
37+
public class UseOfEmptyReturnValueCheck extends PythonSubscriptionCheck {
38+
39+
private static final String MESSAGE = "Remove this use of the output from \"%s\"; \"%s\" doesn’t return anything.";
40+
41+
@Override
42+
public void initialize(Context context) {
43+
context.registerSyntaxNodeConsumer(ASSIGNMENT_STMT, ctx -> checkReturnValue(((AssignmentStatement) ctx.syntaxNode()).assignedValue(), ctx));
44+
context.registerSyntaxNodeConsumer(REGULAR_ARGUMENT, ctx -> checkReturnValue(((RegularArgument) ctx.syntaxNode()).expression(), ctx));
45+
}
46+
47+
private static void checkReturnValue(Expression expression, SubscriptionContext ctx) {
48+
if (expression.is(CALL_EXPR) && expression.type().equals(InferredTypes.NONE)) {
49+
CallExpression callExpression = (CallExpression) expression;
50+
Optional.ofNullable(callExpression.calleeSymbol())
51+
.ifPresent(symbol -> ctx.addIssue(expression, String.format(MESSAGE, symbol.name(), symbol.name())));
52+
}
53+
}
54+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<p>If a function does not return anything, it makes no sense to use its output. Specifically, passing it to another function, or assigning its
2+
"result" to a variable is probably a bug because such functions return nothing, which is probably not what was intended.</p>
3+
<h2>Noncompliant Code Example</h2>
4+
<pre>
5+
def foo():
6+
l = [1, 2, 3]
7+
result = l.append(42) # Noncompliant, `append` mutates list l
8+
return result
9+
</pre>
10+
<h2>Compliant Solution</h2>
11+
<pre>
12+
def foo():
13+
l = [1, 2, 3]
14+
l.append(42)
15+
return l
16+
</pre>
17+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"title": "The output of functions that don\u0027t return anything should not be used",
3+
"type": "BUG",
4+
"status": "ready",
5+
"remediation": {
6+
"func": "Constant\/Issue",
7+
"constantCost": "5min"
8+
},
9+
"tags": [],
10+
"defaultSeverity": "Major",
11+
"ruleSpecification": "RSPEC-3699",
12+
"sqKey": "S3699",
13+
"scope": "All",
14+
"quickfix": "unknown"
15+
}

python-checks/src/main/resources/org/sonar/l10n/py/rules/python/Sonar_way_profile.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"S3457",
7373
"S3516",
7474
"S3626",
75+
"S3699",
7576
"S3752",
7677
"S3776",
7778
"S3827",

0 commit comments

Comments
 (0)