Skip to content

Commit 2f49914

Browse files
pynicolasivandalbosco
authored andcommitted
SONARPY-205 SelfAssignmentCheck: fix FP on builtin function
1 parent a985381 commit 2f49914

File tree

5 files changed

+256
-2
lines changed

5 files changed

+256
-2
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2017 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;
21+
22+
import com.google.common.annotations.VisibleForTesting;
23+
import com.google.common.io.Resources;
24+
import java.io.IOException;
25+
import java.net.URL;
26+
import java.nio.charset.StandardCharsets;
27+
import java.util.List;
28+
import java.util.Set;
29+
import java.util.stream.Collectors;
30+
31+
public enum PythonBuiltinFunctions {
32+
33+
INSTANCE;
34+
35+
private static final Set<String> BUILTINS = loadBuiltinNames(PythonBuiltinFunctions.class.getResource("builtins.txt"));
36+
37+
public static boolean contains(String name) {
38+
return BUILTINS.contains(name);
39+
}
40+
41+
@VisibleForTesting
42+
static Set<String> loadBuiltinNames(URL resourceUrl) {
43+
try {
44+
List<String> lines = Resources.readLines(resourceUrl, StandardCharsets.UTF_8);
45+
return lines.stream()
46+
.map(String::trim)
47+
.filter(s -> !s.startsWith("#"))
48+
.filter(s -> !s.isEmpty())
49+
.collect(Collectors.toSet());
50+
} catch (IOException e) {
51+
throw new IllegalStateException("Cannot load " + resourceUrl, e);
52+
}
53+
}
54+
55+
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.HashSet;
2626
import java.util.Set;
2727
import org.sonar.check.Rule;
28+
import org.sonar.python.PythonBuiltinFunctions;
2829
import org.sonar.python.PythonCheck;
2930
import org.sonar.python.api.PythonGrammar;
3031
import org.sonar.python.api.PythonKeyword;
@@ -91,9 +92,14 @@ private boolean isException(AstNode expressionStatement, AstNode assigned) {
9192
if (!potentialFunctionCalls.isEmpty()) {
9293
return true;
9394
}
94-
if (assigned.getTokens().size() == 1 && importedNames.contains(assigned.getTokenValue())) {
95-
return true;
95+
96+
if (assigned.getTokens().size() == 1) {
97+
String tokenValue = assigned.getTokenValue();
98+
if (importedNames.contains(tokenValue) || PythonBuiltinFunctions.contains(tokenValue)) {
99+
return true;
100+
}
96101
}
102+
97103
AstNode suite = expressionStatement.getFirstAncestor(PythonGrammar.SUITE);
98104
return suite != null && suite.getParent().is(PythonGrammar.CLASSDEF, PythonGrammar.TRY_STMT);
99105
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Python 2: https://docs.python.org/2/library/functions.html
2+
abs
3+
all
4+
any
5+
basestring
6+
bin
7+
bool
8+
bytearray
9+
callable
10+
chr
11+
classmethod
12+
cmp
13+
compile
14+
complex
15+
delattr
16+
dict
17+
dir
18+
divmod
19+
enumerate
20+
eval
21+
execfile
22+
file
23+
filter
24+
float
25+
format
26+
frozenset
27+
getattr
28+
globals
29+
hasattr
30+
hash
31+
help
32+
hex
33+
id
34+
input
35+
int
36+
isinstance
37+
issubclass
38+
iter
39+
len
40+
list
41+
locals
42+
long
43+
map
44+
max
45+
memoryview
46+
min
47+
next
48+
object
49+
oct
50+
open
51+
ord
52+
pow
53+
print
54+
property
55+
range
56+
raw_input
57+
reduce
58+
reload
59+
repr
60+
reversed
61+
round
62+
set
63+
setattr
64+
slice
65+
sorted
66+
staticmethod
67+
str
68+
sum
69+
super
70+
tuple
71+
type
72+
unichr
73+
unicode
74+
vars
75+
xrange
76+
zip
77+
__import__
78+
79+
# Python 3: https://docs.python.org/3/library/functions.html
80+
abs
81+
all
82+
any
83+
ascii
84+
bin
85+
bool
86+
bytearray
87+
bytes
88+
callable
89+
chr
90+
classmethod
91+
compile
92+
complex
93+
delattr
94+
dict
95+
dir
96+
divmod
97+
enumerate
98+
eval
99+
exec
100+
filter
101+
float
102+
format
103+
frozenset
104+
getattr
105+
globals
106+
hasattr
107+
hash
108+
help
109+
hex
110+
id
111+
input
112+
int
113+
isinstance
114+
issubclass
115+
iter
116+
len
117+
list
118+
locals
119+
map
120+
max
121+
memoryview
122+
min
123+
next
124+
object
125+
oct
126+
open
127+
ord
128+
pow
129+
print
130+
property
131+
range
132+
repr
133+
reversed
134+
round
135+
set
136+
setattr
137+
slice
138+
sorted
139+
staticmethod
140+
str
141+
sum
142+
super
143+
tuple
144+
type
145+
vars
146+
zip
147+
__import__
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2017 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;
21+
22+
import java.net.URL;
23+
import org.junit.Test;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
public class PythonBuiltinFunctionsTest {
28+
29+
@Test
30+
public void test() throws Exception {
31+
assertThat(PythonBuiltinFunctions.contains("abs")).isTrue();
32+
assertThat(PythonBuiltinFunctions.contains("ascii")).isTrue(); // python3
33+
assertThat(PythonBuiltinFunctions.contains("basestring")).isTrue(); // python2
34+
assertThat(PythonBuiltinFunctions.contains("xxx")).isFalse();
35+
}
36+
37+
@Test(expected = IllegalStateException.class)
38+
public void unknown_resource() throws Exception {
39+
PythonBuiltinFunctions.loadBuiltinNames(new URL("file:/xxxx"));
40+
}
41+
42+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,7 @@ def f():
2929
import2 = import2
3030
import3 = import3
3131
import4 = import4
32+
33+
34+
if (python2):
35+
unichr = unichr # ok, builtin

0 commit comments

Comments
 (0)