mirror of
https://github.com/github/codeql.git
synced 2025-12-19 10:23:15 +01:00
Should fix #1833, #2137, and #2187. Internally, comprehensions are (at present) elaborated into local functions and iterators as described in [PEP-289](https://www.python.org/dev/peps/pep-0289/). That is, something like: ``` g = (x**2 for x in range(10)) ``` becomes something akin to ``` def __gen(exp): for x in exp: yield x**2 g = __gen(iter(range(10))) ``` In the context of the top-level of a class, this means `__gen` looks as if it is a method of the class, and in particular `exp` looks like it's the `self` argument of this method, which leads the points-to analysis to think that `exp` is an instance of the surrounding class itself. The fix in this case is pretty simple: we look for occurrences of `exp` (in fact called `.0` internally -- carefully chosen to _not_ be a valid Python identifier) and explicitly exclude this parameter from being classified as a `self` parameter.
172 lines
2.4 KiB
Python
172 lines
2.4 KiB
Python
|
|
|
|
|
|
|
|
|
|
def break_in_finally(seq, x):
|
|
for i in seq:
|
|
try:
|
|
x()
|
|
finally:
|
|
break
|
|
return 0
|
|
|
|
def return_in_finally(seq, x):
|
|
for i in seq:
|
|
try:
|
|
x()
|
|
finally:
|
|
return 1
|
|
return 0
|
|
|
|
#Break in loop in finally
|
|
#This is OK
|
|
def return_in_loop_in_finally(f, seq):
|
|
try:
|
|
f()
|
|
finally:
|
|
for i in seq:
|
|
break
|
|
|
|
#But this is not
|
|
def return_in_loop_in_finally(f, seq):
|
|
try:
|
|
f()
|
|
finally:
|
|
for i in seq:
|
|
return
|
|
|
|
def unnecessary_pass(arg):
|
|
print (arg)
|
|
pass
|
|
|
|
#Non-iterator in for loop
|
|
|
|
class NonIterator(object):
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
for x in NonIterator():
|
|
do_something(x)
|
|
|
|
#None in for loop
|
|
|
|
def dodgy_iter(x):
|
|
if x:
|
|
s = None
|
|
else:
|
|
s = [0,1]
|
|
#Error -- Could be None
|
|
for i in s:
|
|
print(i)
|
|
#Ok Test for None
|
|
if s:
|
|
for i in s:
|
|
print(i)
|
|
if s is not None:
|
|
for i in s:
|
|
print(i)
|
|
|
|
#OK to iterate over:
|
|
class GetItem(object):
|
|
|
|
def __getitem__(self, i):
|
|
return i
|
|
|
|
for y in GetItem():
|
|
pass
|
|
|
|
class D(dict): pass
|
|
|
|
for z in D():
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def modification_of_locals():
|
|
x = 0
|
|
locals()['x'] = 1
|
|
l = locals()
|
|
l.update({'x':1, 'y':2})
|
|
l.pop('y')
|
|
del l['x']
|
|
l.clear()
|
|
return x
|
|
|
|
|
|
#C-style things
|
|
|
|
if (cond):
|
|
pass
|
|
|
|
while (cond):
|
|
pass
|
|
|
|
assert (test)
|
|
|
|
def parens(x):
|
|
return (x)
|
|
|
|
|
|
#ODASA-2038
|
|
class classproperty(object):
|
|
|
|
def __init__(self, getter):
|
|
self.getter = getter
|
|
|
|
def __get__(self, instance, instance_type):
|
|
return self.getter(instance_type)
|
|
|
|
class WithClassProperty(object):
|
|
|
|
@classproperty
|
|
def x(self):
|
|
return [0]
|
|
|
|
wcp = WithClassProperty()
|
|
|
|
for i in wcp.x:
|
|
assert x == 0
|
|
for i in WithClassProperty.x:
|
|
assert x == 0
|
|
|
|
#Should use context mamager
|
|
|
|
class CM(object):
|
|
|
|
def __enter__(self):
|
|
pass
|
|
|
|
def __exit__(self, ex, cls, tb):
|
|
pass
|
|
|
|
def write(self, data):
|
|
pass
|
|
|
|
def no_with():
|
|
f = CM()
|
|
try:
|
|
f.write("Hello ")
|
|
f.write(" World\n")
|
|
finally:
|
|
f.close()
|
|
|
|
#Assert without side-effect
|
|
def assert_ok(seq):
|
|
assert all(isinstance(element, (str, unicode)) for element in seq)
|
|
|
|
# False positive. ODASA-8042. Fixed in PR #2401.
|
|
class false_positive:
|
|
e = (x for x in [])
|
|
|