Skip to content

Commit 653c8b8

Browse files
authored
Merge pull request #1358 from AlexTereshenkov/master
Approved by taus-semmle
2 parents 74688bb + 9377638 commit 653c8b8

File tree

8 files changed

+152
-1
lines changed

8 files changed

+152
-1
lines changed

change-notes/1.21/analysis-python.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
| **Query** | **Tags** | **Purpose** |
1111
|-----------|----------|-------------|
1212
| Accepting unknown SSH host keys when using Paramiko (`py/paramiko-missing-host-key-validation`) | security, external/cwe/cwe-295 | Finds instances where Paramiko is configured to accept unknown host keys. Results are shown on LGTM by default. |
13-
13+
| Use of 'return' or 'yield' outside a function (`py/return-or-yield-outside-function`) | reliability, correctness | Finds instances where `return`, `yield`, and `yield from` are used outside a function. Results are not shown on LGTM by default. |
1414

1515
## Changes to existing queries
1616

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# invalid class with return outside a function
2+
class InvalidClass1(object):
3+
if [1, 2, 3]:
4+
return "Exists"
5+
6+
# invalid statement with yield outside a function
7+
for i in [1, 2, 3]:
8+
yield i
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>In Python, <code>return</code> and <code>yield</code> statements and the <code>yield from</code>
8+
expression can only be used within a function. Using them outside a function or a class
9+
method results in a <code>SyntaxError</code> at runtime.</p>
10+
</overview>
11+
12+
<recommendation>
13+
<p>Using these elements outside a function or a class method usually indicates an error in the logic.
14+
Consequently, it is not possible to suggest a general fix.</p>
15+
</recommendation>
16+
17+
<example>
18+
<p>In this example, a <code>return</code> statement is used outside a class method in a class and
19+
a <code>yield</code> statement is used outside a function in a scope of a module which would result
20+
in a <code>SyntaxError</code> when running this code.
21+
22+
In this example, the invalid class could be corrected by placing the <code>return</code> statement
23+
in a class method, or by refactoring the class into a function. The invalid <code>yield</code> statement
24+
could become part of a new generator function containing the <code>for</code> loop.</p>
25+
<sample src="ReturnOrYieldOutsideFunction.py" />
26+
</example>
27+
28+
<references>
29+
<li>Python reference: <a href="https://docs.python.org/3.7/reference/simple_stmts.html#the-return-statement">
30+
The return statement</a>.</li>
31+
<li>Python reference: <a href="https://docs.python.org/3.7/reference/simple_stmts.html#yield">
32+
The yield statement</a>.</li>
33+
<li>Python PEP-380: <a href="https://docs.python.org/3/whatsnew/3.3.html#pep-380">
34+
The yield from expression</a>.</li>
35+
</references>
36+
</qhelp>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @name Use of 'return' or 'yield' outside a function
3+
* @description Using 'return' or 'yield' outside a function causes a 'SyntaxError' at runtime.
4+
* @kind problem
5+
* @tags reliability
6+
* correctness
7+
* @problem.severity error
8+
* @sub-severity low
9+
* @precision medium
10+
* @id py/return-or-yield-outside-function
11+
*/
12+
13+
import python
14+
15+
from AstNode node, string kind
16+
where
17+
not node.getScope() instanceof Function and
18+
(
19+
node instanceof Return and kind = "return"
20+
or
21+
node instanceof Yield and kind = "yield"
22+
or
23+
node instanceof YieldFrom and kind = "yield from"
24+
)
25+
select node, "'" + kind + "' is used outside a function."
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
| ReturnOrYieldOutsideFunction_test.py:31:9:31:23 | Return | 'return' is used outside a function. |
2+
| ReturnOrYieldOutsideFunction_test.py:36:9:36:15 | Yield | 'yield' is used outside a function. |
3+
| ReturnOrYieldOutsideFunction_test.py:41:9:41:25 | YieldFrom | 'yield from' is used outside a function. |
4+
| ReturnOrYieldOutsideFunction_test.py:45:5:45:12 | Return | 'return' is used outside a function. |
5+
| ReturnOrYieldOutsideFunction_test.py:49:5:49:11 | Yield | 'yield' is used outside a function. |
6+
| ReturnOrYieldOutsideFunction_test.py:53:5:53:16 | YieldFrom | 'yield from' is used outside a function. |
7+
| ReturnOrYieldOutsideFunction_test.py:57:1:57:14 | YieldFrom | 'yield from' is used outside a function. |
8+
| ReturnOrYieldOutsideFunction_test.py:60:1:60:12 | Return | 'return' is used outside a function. |
9+
| ReturnOrYieldOutsideFunction_test.py:63:1:63:11 | Yield | 'yield' is used outside a function. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Statements/ReturnOrYieldOutsideFunction.ql
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# valid class with return inside a function
2+
class ValidClass1(object):
3+
def class_method(self):
4+
return False
5+
6+
# valid class with yield inside a function
7+
class ValidClass2(object):
8+
def class_method(self):
9+
yield 1
10+
11+
# valid class with yield from inside a function
12+
class ValidClass3(object):
13+
def class_method(self):
14+
yield from [1, 2]
15+
16+
# valid function with the return statement
17+
def valid_func1():
18+
return True
19+
20+
# valid function with the yield statement
21+
def valid_func2():
22+
yield 1
23+
24+
# valid function with the yield from statement
25+
def valid_func3():
26+
yield from [1, 2]
27+
28+
# invalid class with return outside a function
29+
class InvalidClass1(object):
30+
if [1, 2, 3]:
31+
return "Exists"
32+
33+
# invalid class with yield outside a function
34+
class InvalidClass2(object):
35+
while True:
36+
yield 1
37+
38+
# invalid class with yield from outside a function
39+
class InvalidClass3(object):
40+
while True:
41+
yield from [1, 2]
42+
43+
# invalid statement with return outside a function
44+
for i in [1, 2, 3]:
45+
return i
46+
47+
# invalid statement with yield outside a function
48+
for i in [1, 2, 3]:
49+
yield i
50+
51+
# invalid statement with yield from outside a function
52+
for i in [1, 2, 3]:
53+
yield from i
54+
55+
# invalid statement with yield from outside a function
56+
var = [1,2,3]
57+
yield from var
58+
59+
# invalid statement with return outside a function
60+
return False
61+
62+
# invalid statement with yield outside a function
63+
yield False
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
| ReturnOrYieldOutsideOfFunction_test.py:31:9:31:23 | Return | 'return' is used outside of a function. |
2+
| ReturnOrYieldOutsideOfFunction_test.py:36:9:36:15 | Yield | 'yield' is used outside of a function. |
3+
| ReturnOrYieldOutsideOfFunction_test.py:41:9:41:25 | YieldFrom | 'yield from' is used outside of a function. |
4+
| ReturnOrYieldOutsideOfFunction_test.py:45:5:45:12 | Return | 'return' is used outside of a function. |
5+
| ReturnOrYieldOutsideOfFunction_test.py:49:5:49:11 | Yield | 'yield' is used outside of a function. |
6+
| ReturnOrYieldOutsideOfFunction_test.py:53:5:53:16 | YieldFrom | 'yield from' is used outside of a function. |
7+
| ReturnOrYieldOutsideOfFunction_test.py:57:1:57:14 | YieldFrom | 'yield from' is used outside of a function. |
8+
| ReturnOrYieldOutsideOfFunction_test.py:60:1:60:12 | Return | 'return' is used outside of a function. |
9+
| ReturnOrYieldOutsideOfFunction_test.py:63:1:63:11 | Yield | 'yield' is used outside of a function. |

0 commit comments

Comments
 (0)