mirror of
https://github.com/github/codeql.git
synced 2026-05-05 05:35:13 +02:00
QL tests for Python queries and libraries.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
| wrong_arguments.py:65:1:65:7 | F0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ |
|
||||
| wrong_arguments.py:66:1:66:7 | F1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ |
|
||||
| wrong_arguments.py:67:1:67:12 | F2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:12:5:12:30 | Function __init__ | F2.__init__ |
|
||||
| wrong_arguments.py:92:1:92:27 | F6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/WrongNameForArgumentInClassInstantiation.ql
|
||||
@@ -0,0 +1,15 @@
|
||||
| wrong_arguments.py:37:1:37:4 | F0() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ |
|
||||
| wrong_arguments.py:38:1:38:4 | F1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ |
|
||||
| wrong_arguments.py:39:1:39:4 | F2() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:12:5:12:30 | Function __init__ | F2.__init__ |
|
||||
| wrong_arguments.py:40:1:40:4 | F3() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:16:5:16:40 | Function __init__ | F3.__init__ |
|
||||
| wrong_arguments.py:41:1:41:4 | F4() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:20:5:20:31 | Function __init__ | F4.__init__ |
|
||||
| wrong_arguments.py:42:1:42:4 | F5() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:24:5:24:42 | Function __init__ | F5.__init__ |
|
||||
| wrong_arguments.py:43:1:43:5 | F6() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
|
||||
| wrong_arguments.py:44:1:44:7 | F7() | Call to $@ with too few arguments; should be no fewer than 3. | wrong_arguments.py:32:5:32:33 | Function __init__ | F7.__init__ |
|
||||
| wrong_arguments.py:48:1:48:7 | F0() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ |
|
||||
| wrong_arguments.py:49:1:49:9 | F1() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ |
|
||||
| wrong_arguments.py:50:1:50:9 | F5() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:24:5:24:42 | Function __init__ | F5.__init__ |
|
||||
| wrong_arguments.py:51:1:51:9 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
|
||||
| wrong_arguments.py:52:1:52:11 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
|
||||
| wrong_arguments.py:85:1:85:12 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
|
||||
| wrong_arguments.py:86:1:86:7 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/WrongNumberArgumentsInClassInstantiation.ql
|
||||
@@ -0,0 +1,93 @@
|
||||
# Test cases corresponding to /Expressions/Arguments/wrong_arguments.py
|
||||
|
||||
class F0(object):
|
||||
def __init__(self, x):
|
||||
pass
|
||||
|
||||
class F1(object):
|
||||
def __init__(self, x, y = None):
|
||||
pass
|
||||
|
||||
class F2(object):
|
||||
def __init__(self, x, *y):
|
||||
pass
|
||||
|
||||
class F3(object):
|
||||
def __init__(self, x, y = None, *z):
|
||||
pass
|
||||
|
||||
class F4(object):
|
||||
def __init__(self, x, **y):
|
||||
pass
|
||||
|
||||
class F5(object):
|
||||
def __init__(self, x, y = None, **z):
|
||||
pass
|
||||
|
||||
class F6(object):
|
||||
def __init__(self, x, y):
|
||||
pass
|
||||
|
||||
class F7(object):
|
||||
def __init__(self, x, y, z):
|
||||
pass
|
||||
|
||||
# Too few arguments
|
||||
|
||||
F0()
|
||||
F1()
|
||||
F2()
|
||||
F3()
|
||||
F4()
|
||||
F5()
|
||||
F6(1)
|
||||
F7(1,2)
|
||||
|
||||
#Too many arguments
|
||||
|
||||
F0(1,2)
|
||||
F1(1,2,3)
|
||||
F5(1,2,3)
|
||||
F6(1,2,3)
|
||||
F6(1,2,3,4)
|
||||
|
||||
#OK
|
||||
|
||||
#Not too few
|
||||
F7(*t)
|
||||
|
||||
#Not too many
|
||||
|
||||
F2(1,2,3,4,5,6)
|
||||
|
||||
|
||||
#Illegal name
|
||||
F0(y=1)
|
||||
F1(z=1)
|
||||
F2(x=0, y=1)
|
||||
|
||||
|
||||
#Ok name
|
||||
F0(x=0)
|
||||
F1(x=0, y=1)
|
||||
F4(q=4)
|
||||
|
||||
#This is correct, but a bit weird.
|
||||
F6(**{'x':1, 'y':2})
|
||||
|
||||
t2 = (1,2)
|
||||
t3 = (1,2,3)
|
||||
|
||||
#Ok
|
||||
f(*t2)
|
||||
|
||||
#Too many
|
||||
F6(*(1,2,3))
|
||||
F6(*t3)
|
||||
|
||||
#Ok
|
||||
F6(**{'x':1, 'y':2})
|
||||
|
||||
#Illegal name
|
||||
F6(**{'x':1, 'y':2, 'z':3})
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:26:1:26:25 | class Conflict | Base classes have conflicting values for attribute 'attr': $@ and $@. | file://:Compiled Code:0:0:0:0 | int 1 | int 1 | test.py:20:13:20:16 | Tuple | Tuple |
|
||||
| test.py:26:1:26:25 | class Conflict | Base classes have conflicting values for attribute 'meth': $@ and $@. | test.py:14:5:14:19 | Function meth | Function meth | test.py:22:5:22:19 | Function meth | Function meth |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/ConflictingAttributesInBaseClasses.ql
|
||||
17
python/ql/test/query-tests/Classes/conflicting/odasa6643.py
Normal file
17
python/ql/test/query-tests/Classes/conflicting/odasa6643.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#This code has conflicting attributes,
|
||||
#but the documentation in the standard library tells you do it this way :(
|
||||
|
||||
#See https://discuss.lgtm.com/t/warning-on-normal-use-of-python-socketserver-mixins/677
|
||||
|
||||
class ThreadingMixIn(object):
|
||||
|
||||
def process_request(selfself, req):
|
||||
pass
|
||||
|
||||
class HTTPServer(object):
|
||||
|
||||
def process_request(selfself, req):
|
||||
pass
|
||||
|
||||
class _ThreadingSimpleServer(ThreadingMixIn, HTTPServer):
|
||||
pass
|
||||
52
python/ql/test/query-tests/Classes/conflicting/test.py
Normal file
52
python/ql/test/query-tests/Classes/conflicting/test.py
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
|
||||
#Conflicting attributes in base classes
|
||||
|
||||
class Common(object):
|
||||
ok1 = None
|
||||
|
||||
def ok2(self):
|
||||
return None
|
||||
|
||||
class CB1(Common):
|
||||
attr = 1
|
||||
|
||||
def meth(self):
|
||||
pass
|
||||
|
||||
|
||||
class CB2(Common):
|
||||
|
||||
attr = (x, y)
|
||||
|
||||
def meth(self):
|
||||
return 0
|
||||
|
||||
|
||||
class Conflict(CB1, CB2):
|
||||
pass
|
||||
|
||||
class Override1(Common):
|
||||
|
||||
def ok2(self):
|
||||
return 1
|
||||
|
||||
class Override2(Common):
|
||||
|
||||
def ok2(self):
|
||||
return 2
|
||||
|
||||
class OK1(Override1, Override2):
|
||||
|
||||
def ok2(self):
|
||||
return 3
|
||||
|
||||
|
||||
class Override3(Override2):
|
||||
pass
|
||||
|
||||
class OK2(Override1, Override3):
|
||||
|
||||
def ok2(self):
|
||||
return 4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| test.py:10:9:10:19 | Attribute | Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties. | test.py:3:1:3:33 | class MutatingDescriptor | MutatingDescriptor |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/MutatingDescriptor.ql
|
||||
14
python/ql/test/query-tests/Classes/descriptors/test.py
Normal file
14
python/ql/test/query-tests/Classes/descriptors/test.py
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
#This is prone to strange side effects and race conditions.
|
||||
class MutatingDescriptor(object):
|
||||
|
||||
def __init__(self, func):
|
||||
self.my_func = func
|
||||
|
||||
def __get__(self, obj, obj_type):
|
||||
#Modified state is visible to all instances of C that might call "show".
|
||||
self.my_obj = obj
|
||||
return self
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.my_func(self.my_obj, *args)
|
||||
@@ -0,0 +1 @@
|
||||
Classes/DefineEqualsWhenAddingAttributes.ql
|
||||
16
python/ql/test/query-tests/Classes/equals-attr/test.py
Normal file
16
python/ql/test/query-tests/Classes/equals-attr/test.py
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
class GenericEquality(object):
|
||||
|
||||
def __eq__(self, other):
|
||||
if type(other) is not type(self):
|
||||
return False
|
||||
for attr in self.__dict__:
|
||||
if getattr(other, attr) != getattr(self, attr):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class AddAttributes(GenericEquality):
|
||||
|
||||
def __init__(self, args):
|
||||
self.a, self.b = args
|
||||
@@ -0,0 +1 @@
|
||||
| attr_eq_test.py:21:1:21:27 | class BadColorPoint | The class 'BadColorPoint' does not override $@, but adds the new attribute $@. | attr_eq_test.py:10:5:10:28 | Function __eq__ | '__eq__' | attr_eq_test.py:25:9:25:19 | Attribute | _color |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/DefineEqualsWhenAddingAttributes.ql
|
||||
107
python/ql/test/query-tests/Classes/equals-hash/attr_eq_test.py
Normal file
107
python/ql/test/query-tests/Classes/equals-hash/attr_eq_test.py
Normal file
@@ -0,0 +1,107 @@
|
||||
class Point(object):
|
||||
|
||||
def __init__(self, x, y):
|
||||
self._x = x
|
||||
self._y = y
|
||||
|
||||
def __repr__(self):
|
||||
return 'Point(%r, %r)' % (self._x, self._y)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Point):
|
||||
return False
|
||||
return self._x == other._x and self._y == other._y
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self._x, self._y))
|
||||
|
||||
class BadColorPoint(Point):
|
||||
|
||||
def __init__(self, x, y, color):
|
||||
Point.__init__(self, x, y)
|
||||
self._color = color
|
||||
|
||||
def __repr__(self):
|
||||
return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color)
|
||||
|
||||
class GoodColorPoint(Point):
|
||||
|
||||
def __init__(self, x, y, color):
|
||||
Point.__init__(self, x, y)
|
||||
self._color = color
|
||||
|
||||
def __repr__(self):
|
||||
return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, GoodColorPoint):
|
||||
return False
|
||||
return Point.__eq__(self, other) and self._color == other._color
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self._x, self._y, self._color))
|
||||
|
||||
class GenericPoint(object):
|
||||
|
||||
def __init__(self, x, y):
|
||||
self._x = x
|
||||
self._y = y
|
||||
|
||||
def __repr__(self):
|
||||
return 'Point(%r, %r)' % (self._x, self._y)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__class__ == other.__class__ and self.__dict__ == other.__dict__
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self._x, self._y))
|
||||
|
||||
class GoodGenericColorPoint(GenericPoint):
|
||||
|
||||
def __init__(self, x, y, color):
|
||||
GenericPoint.__init__(self, x, y)
|
||||
self._color = color
|
||||
|
||||
class RedefineEq(object):
|
||||
|
||||
def __init__(self, x, y):
|
||||
self._x = x
|
||||
self._y = y
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
class OK1(RedefineEq):
|
||||
|
||||
def __init__(self, x, y, z):
|
||||
RedefineEq.__init__(self, x, y)
|
||||
self.z = z
|
||||
|
||||
class OK2(GenericPoint):
|
||||
|
||||
def __init__(self, x, y, color):
|
||||
GenericPoint.__init__(self, x, y)
|
||||
self._color = color
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
class ExpectingAttribute(object):
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.x == other.x
|
||||
|
||||
class OK3(ExpectingAttribute):
|
||||
|
||||
def __init__(self):
|
||||
self.x = 4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| incomplete_ordering.py:3:1:3:26 | class PartOrdered | Class PartOrdered implements $@, but does not implement __le__ or __gt__ or __ge__. | incomplete_ordering.py:13:5:13:28 | Function __lt__ | __lt__ |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/IncompleteOrdering.ql
|
||||
@@ -0,0 +1,18 @@
|
||||
#Incomplete ordering
|
||||
|
||||
class PartOrdered(object):
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
def __ne__(self, other):
|
||||
return self is not other
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
#Don't blame a sub-class for super-class's sins.
|
||||
class DerivedPartOrdered(PartOrdered):
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
| init_calls_subclass.py:7:9:7:24 | Attribute() | Call to self.$@ in __init__ method, which is overridden by $@. | init_calls_subclass.py:10:5:10:26 | Function set_up | set_up | init_calls_subclass.py:19:5:19:26 | Function set_up | method Sub.set_up |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/InitCallsSubclassMethod.ql
|
||||
@@ -0,0 +1,22 @@
|
||||
#Superclass __init__ calls subclass method
|
||||
|
||||
class Super(object):
|
||||
|
||||
def __init__(self, arg):
|
||||
self._state = "Not OK"
|
||||
self.set_up(arg)
|
||||
self._state = "OK"
|
||||
|
||||
def set_up(self, arg):
|
||||
"Do some set up"
|
||||
|
||||
class Sub(Super):
|
||||
|
||||
def __init__(self, arg):
|
||||
Super.__init__(self, arg)
|
||||
self.important_state = "OK"
|
||||
|
||||
def set_up(self, arg):
|
||||
Super.set_up(self, arg)
|
||||
"Do some more set up" # Dangerous as self._state is "Not OK" and
|
||||
# self.important_state is uninitialized
|
||||
@@ -0,0 +1 @@
|
||||
| missing_del.py:12:1:12:13 | class X3 | Class X3 may not be cleaned up properly as $@ is not called during deletion. | missing_del.py:9:5:9:22 | Function __del__ | method X2.__del__ |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/MissingCallToDel.ql
|
||||
@@ -0,0 +1,15 @@
|
||||
#Not calling an __del__ method:
|
||||
class X1(object):
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
class X2(X1):
|
||||
|
||||
def __del__(self):
|
||||
X1.__del__(self)
|
||||
|
||||
class X3(X2):
|
||||
|
||||
def __del__(self):
|
||||
X1.__del__(self)
|
||||
@@ -0,0 +1,3 @@
|
||||
| missing_init.py:12:1:12:13 | class B3 | Class B3 may not be initialized properly as $@ is not called from its $@. | missing_init.py:9:5:9:23 | Function __init__ | method B2.__init__ | missing_init.py:14:5:14:23 | Function __init__ | __init__ method |
|
||||
| missing_init.py:39:1:39:21 | class IUVT | Class IUVT may not be initialized properly as $@ is not called from its $@. | missing_init.py:30:5:30:23 | Function __init__ | method UT.__init__ | missing_init.py:26:5:26:23 | Function __init__ | __init__ method |
|
||||
| missing_init.py:72:1:72:13 | class AB | Class AB may not be initialized properly as $@ is not called from its $@. | missing_init.py:69:5:69:23 | Function __init__ | method AA.__init__ | missing_init.py:75:5:75:23 | Function __init__ | __init__ method |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/MissingCallToInit.ql
|
||||
185
python/ql/test/query-tests/Classes/missing-init/missing_init.py
Normal file
185
python/ql/test/query-tests/Classes/missing-init/missing_init.py
Normal file
@@ -0,0 +1,185 @@
|
||||
#Not calling an __init__ method:
|
||||
class B1(object):
|
||||
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
class B2(B1):
|
||||
|
||||
def __init__(self):
|
||||
B1.__init__(self)
|
||||
|
||||
class B3(B2):
|
||||
|
||||
def __init__(self):
|
||||
B1.__init__(self)
|
||||
|
||||
#OK if superclass __init__ is builtin as
|
||||
#builtin classes tend to rely on __new__
|
||||
class MyException(Exception):
|
||||
|
||||
def __init__(self):
|
||||
self.message = "Uninformative"
|
||||
|
||||
#ODASA-4107
|
||||
class IUT(object):
|
||||
def __init__(self):
|
||||
print("IUT init")
|
||||
|
||||
class UT(object):
|
||||
def __init__(self):
|
||||
print("UT init")
|
||||
|
||||
class PU(object):
|
||||
pass
|
||||
|
||||
class UVT(UT, PU):
|
||||
pass
|
||||
|
||||
class IUVT(IUT, UVT):
|
||||
pass
|
||||
|
||||
#False positive observed on LGTM
|
||||
class M1(object):
|
||||
def __init__(self):
|
||||
print("A")
|
||||
|
||||
class M2(object):
|
||||
pass
|
||||
|
||||
class Mult(M2, M1):
|
||||
def __init__(self):
|
||||
super(Mult, self).__init__() # Calls M1.__init__
|
||||
|
||||
class X:
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
class Y(X):
|
||||
@decorated
|
||||
def __init__(self):
|
||||
X.__init__(self)
|
||||
|
||||
class Z(Y):
|
||||
def __init__(self):
|
||||
Y.__init__(self)
|
||||
|
||||
class AA(object):
|
||||
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
class AB(AA):
|
||||
|
||||
#Don't call super class init
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
class AC(AB):
|
||||
|
||||
def __init__(self):
|
||||
#Missing call to AA.__init__ but not AC's fault.
|
||||
super(AC, self).__init__()
|
||||
|
||||
import six
|
||||
import abc
|
||||
|
||||
class BA(object):
|
||||
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BB(BA):
|
||||
|
||||
def __init__(self):
|
||||
super(BB,self).__init__()
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class CA(object):
|
||||
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
class CB(BA):
|
||||
|
||||
def __init__(self):
|
||||
super(CB,self).__init__()
|
||||
|
||||
#ODASA-5799
|
||||
class DA(object):
|
||||
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
class DB(DA):
|
||||
|
||||
class DC(DA):
|
||||
|
||||
def __init__(self):
|
||||
sup = super(DB.DC, self)
|
||||
sup.__init__()
|
||||
|
||||
#Simpler variants
|
||||
class DD(DA):
|
||||
|
||||
def __init__(self):
|
||||
sup = super(DD, self)
|
||||
sup.__init__()
|
||||
|
||||
class DE(DA):
|
||||
|
||||
class DF(DA):
|
||||
|
||||
def __init__(self):
|
||||
sup = super(DE.DF, self).__init__()
|
||||
|
||||
class FA(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
class FB(object):
|
||||
|
||||
def __init__(self):
|
||||
do_something()
|
||||
|
||||
class FC(FA, FB):
|
||||
|
||||
def __init__(self):
|
||||
#OK to skip call to FA.__init__ as that does nothing.
|
||||
FB.__init__(self)
|
||||
|
||||
#Potential false positives.
|
||||
|
||||
class ConfusingInit(B1):
|
||||
|
||||
def __init__(self):
|
||||
super_call = super(ConfusingInit, self).__init__
|
||||
super_call()
|
||||
|
||||
|
||||
# Library class
|
||||
import collections
|
||||
|
||||
class G1(collections.Counter):
|
||||
|
||||
def __init__(self):
|
||||
collections.Counter.__init__(self)
|
||||
|
||||
class G2(G1):
|
||||
|
||||
def __init__(self):
|
||||
super(G2, self).__init__()
|
||||
|
||||
class G3(collections.Counter):
|
||||
|
||||
def __init__(self):
|
||||
super(G3, self).__init__()
|
||||
|
||||
class G4(G3):
|
||||
|
||||
def __init__(self):
|
||||
G3.__init__(self)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| multiple_del.py:17:1:17:17 | class Y3 | Class Y3 may not be cleaned up properly as $@ may be called multiple times during destruction. | multiple_del.py:9:5:9:22 | Function __del__ | method Y1.__del__ |
|
||||
| multiple_del.py:34:1:34:17 | class Z3 | Class Z3 may not be cleaned up properly as $@ may be called multiple times during destruction. | multiple_del.py:26:5:26:22 | Function __del__ | method Z1.__del__ |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/SuperclassDelCalledMultipleTimes.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
| multiple_init.py:17:1:17:17 | class C3 | Class C3 may not be initialized properly as $@ may be called multiple times during initialization. | multiple_init.py:9:5:9:23 | Function __init__ | method C1.__init__ |
|
||||
| multiple_init.py:34:1:34:17 | class D3 | Class D3 may not be initialized properly as $@ may be called multiple times during initialization. | multiple_init.py:26:5:26:23 | Function __init__ | method D1.__init__ |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/SuperclassInitCalledMultipleTimes.ql
|
||||
38
python/ql/test/query-tests/Classes/multiple/multiple_del.py
Normal file
38
python/ql/test/query-tests/Classes/multiple/multiple_del.py
Normal file
@@ -0,0 +1,38 @@
|
||||
#Calling a method multiple times by using explicit calls when a base uses super()
|
||||
class Base(object):
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
class Y1(Base):
|
||||
|
||||
def __del__(self):
|
||||
super(Y1, self).__del__()
|
||||
|
||||
class Y2(Base):
|
||||
|
||||
def __del__(self):
|
||||
super(Y2, self).__del__() #When `type(self) == Y3` this calls `Y1.__del__`
|
||||
|
||||
class Y3(Y2, Y1):
|
||||
|
||||
def __del__(self):
|
||||
Y1.__del__(self)
|
||||
Y2.__del__(self)
|
||||
|
||||
#Calling a method multiple times by using explicit calls when a base inherits from other base
|
||||
class Z1(object):
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
class Z2(Z1):
|
||||
|
||||
def __del__(self):
|
||||
Z1.__del__(self)
|
||||
|
||||
class Z3(Z2, Z1):
|
||||
|
||||
def __del__(self):
|
||||
Z1.__del__(self)
|
||||
Z2.__del__(self)
|
||||
76
python/ql/test/query-tests/Classes/multiple/multiple_init.py
Normal file
76
python/ql/test/query-tests/Classes/multiple/multiple_init.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#Calling a method multiple times by using explicit calls when a base uses super()
|
||||
class Base(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
class C1(Base):
|
||||
|
||||
def __init__(self):
|
||||
super(C1, self).__init__()
|
||||
|
||||
class C2(Base):
|
||||
|
||||
def __init__(self):
|
||||
super(C2, self).__init__() #When `type(self) == C3` this calls `C1.__init__`
|
||||
|
||||
class C3(C2, C1):
|
||||
|
||||
def __init__(self):
|
||||
C1.__init__(self)
|
||||
C2.__init__(self)
|
||||
|
||||
#Calling a method multiple times by using explicit calls when a base inherits from other base
|
||||
class D1(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
class D2(D1):
|
||||
|
||||
def __init__(self):
|
||||
D1.__init__(self)
|
||||
|
||||
class D3(D2, D1):
|
||||
|
||||
def __init__(self):
|
||||
D1.__init__(self)
|
||||
D2.__init__(self)
|
||||
|
||||
#OK to call object.__init__ multiple times
|
||||
class E1(object):
|
||||
|
||||
def __init__(self):
|
||||
super(E1, self).__init__()
|
||||
|
||||
class E2(object):
|
||||
|
||||
def __init__(self):
|
||||
object.__init__(self)
|
||||
|
||||
class E3(E2, E1):
|
||||
|
||||
def __init__(self):
|
||||
E1.__init__(self)
|
||||
E2.__init__(self)
|
||||
|
||||
#Two invocations, but can only be called once
|
||||
class F1(Base):
|
||||
|
||||
def __init__(self, cond):
|
||||
if cond:
|
||||
Base.__init__(self)
|
||||
else:
|
||||
Base.__init__(self)
|
||||
|
||||
#Single call, splitting causes what seems to be multiple invocations.
|
||||
class F2(Base):
|
||||
|
||||
def __init__(self, cond):
|
||||
if cond:
|
||||
pass
|
||||
if cond:
|
||||
pass
|
||||
Base.__init__(self)
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| overwriting_attribute.py:5:9:5:20 | AssignStmt | Assignment overwrites attribute var, which was previously defined in subclass $@. | overwriting_attribute.py:10:9:10:20 | AssignStmt | D |
|
||||
| overwriting_attribute.py:23:9:23:20 | AssignStmt | Assignment overwrites attribute var, which was previously defined in superclass $@. | overwriting_attribute.py:17:9:17:20 | AssignStmt | E |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/OverwritingAttributeInSuperClass.ql
|
||||
@@ -0,0 +1,23 @@
|
||||
#Attribute set in both superclass and subclass
|
||||
class C(object):
|
||||
|
||||
def __init__(self):
|
||||
self.var = 0
|
||||
|
||||
class D(C):
|
||||
|
||||
def __init__(self):
|
||||
self.var = 1 # self.var will be overwritten
|
||||
C.__init__(self)
|
||||
|
||||
#Attribute set in both superclass and subclass
|
||||
class E(object):
|
||||
|
||||
def __init__(self):
|
||||
self.var = 0 # self.var will be overwritten
|
||||
|
||||
class F(E):
|
||||
|
||||
def __init__(self):
|
||||
E.__init__(self)
|
||||
self.var = 1
|
||||
@@ -0,0 +1,2 @@
|
||||
| should_be_context_manager.py:3:1:3:22 | class MegaDel | Class MegaDel implements __del__ (presumably to release some resource). Consider making it a context manager. |
|
||||
| should_be_context_manager.py:16:1:16:22 | class MiniDel | Class MiniDel implements __del__ (presumably to release some resource). Consider making it a context manager. |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/ShouldBeContextManager.ql
|
||||
@@ -0,0 +1,22 @@
|
||||
#Should be context manager
|
||||
|
||||
class MegaDel(object):
|
||||
|
||||
def __del__(self):
|
||||
a = self.x + self.y
|
||||
if a:
|
||||
print(a)
|
||||
if sys._getframe().f_lineno > 100:
|
||||
print("Hello")
|
||||
sum = 0
|
||||
for a in range(100):
|
||||
sum += a
|
||||
print(sum)
|
||||
|
||||
class MiniDel(object):
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
@@ -0,0 +1 @@
|
||||
| subclass_shadowing.py:10:5:10:21 | FunctionExpr | Method shadow is shadowed by $@ in super class 'Base'. | subclass_shadowing.py:6:9:6:23 | AssignStmt | an attribute |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/SubclassShadowing.ql
|
||||
@@ -0,0 +1,30 @@
|
||||
#Subclass shadowing
|
||||
|
||||
class Base(object):
|
||||
|
||||
def __init__(self):
|
||||
self.shadow = 4
|
||||
|
||||
class Derived(Base):
|
||||
|
||||
def shadow(self):
|
||||
pass
|
||||
|
||||
|
||||
#OK if the super class defines the method as well.
|
||||
#Since the original method must exist for some reason.
|
||||
#See JSONEncoder.default for real example
|
||||
|
||||
class Base2(object):
|
||||
|
||||
def __init__(self, shadowy=None):
|
||||
if shadowy:
|
||||
self.shadow = shadowy
|
||||
|
||||
def shadow(self):
|
||||
pass
|
||||
|
||||
class Derived2(Base2):
|
||||
|
||||
def shadow(self):
|
||||
return 0
|
||||
@@ -0,0 +1,4 @@
|
||||
| undefined_attribute.py:27:16:27:29 | Attribute | Attribute 'may_exist' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:11:9:11:22 | Attribute | here |
|
||||
| undefined_attribute.py:184:16:184:32 | Attribute | Attribute 'return_queue' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:181:13:181:29 | Attribute | here |
|
||||
| undefined_attribute.py:257:16:257:31 | Attribute | Attribute 'glance_host' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:262:13:262:28 | Attribute | here |
|
||||
| undefined_attribute.py:258:16:258:31 | Attribute | Attribute 'glance_port' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:263:10:263:25 | Attribute | here |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/MaybeUndefinedClassAttribute.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| undefined_attribute.py:24:16:24:30 | Attribute | Attribute 'not_exists' is not defined in either the class body or in any method |
|
||||
| undefined_attribute.py:109:16:109:21 | Attribute | Attribute 'y' is not defined in either the class body or in any method |
|
||||
| undefined_attribute.py:250:16:250:31 | Attribute | Attribute 'glance_host' is not defined in either the class body or in any method |
|
||||
| undefined_attribute.py:251:16:251:31 | Attribute | Attribute 'glance_port' is not defined in either the class body or in any method |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/UndefinedClassAttribute.ql
|
||||
@@ -0,0 +1,263 @@
|
||||
#Non existent class attribute
|
||||
|
||||
class Attributes(object):
|
||||
|
||||
exists1 = 1
|
||||
|
||||
def __init__(self):
|
||||
self.exists2 = 2
|
||||
|
||||
def method(self):
|
||||
self.may_exist = 3
|
||||
|
||||
def ok1(self):
|
||||
print (self.exists1)
|
||||
|
||||
def ok2(self):
|
||||
print (self.exists2)
|
||||
|
||||
def ok3(self):
|
||||
self.local_exists = 4
|
||||
print (self.local_exists)
|
||||
|
||||
def neca1(self):
|
||||
print (self.not_exists)
|
||||
|
||||
def neca2(self):
|
||||
print (self.may_exist)
|
||||
|
||||
#This is OK
|
||||
class SetViaDict(object):
|
||||
|
||||
def __init__(self, x):
|
||||
self.__dict__['x'] = x
|
||||
|
||||
def use_x(self):
|
||||
return self.x
|
||||
|
||||
|
||||
#This is also OK
|
||||
class SetLocally(object):
|
||||
|
||||
def use_x(self):
|
||||
self.x = 1
|
||||
return self.x
|
||||
|
||||
|
||||
class UsesSetattr(object):
|
||||
|
||||
def __init__(self, vals):
|
||||
for k, v in vals.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def use_values(self):
|
||||
return self.x, self.y, self.z
|
||||
|
||||
#OK
|
||||
class GuardedByHasAttr(object):
|
||||
|
||||
def ok4(self):
|
||||
if hasattr(self, "x"):
|
||||
return self.x
|
||||
else:
|
||||
return None
|
||||
|
||||
class HasGetAttr(object):
|
||||
|
||||
def __getattr__(self, name):
|
||||
return name
|
||||
|
||||
def use_values(self):
|
||||
return self.x, self.y, self.z
|
||||
|
||||
class HasGetAttribute(object):
|
||||
|
||||
def __getattribute__(self, name):
|
||||
return name
|
||||
|
||||
def use_values(self):
|
||||
return self.x, self.y, self.z
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DecoratedInit(object):
|
||||
|
||||
@decorator
|
||||
def __init__(self):
|
||||
self.x = x
|
||||
|
||||
def use(self):
|
||||
return self.x
|
||||
|
||||
#This is not OK
|
||||
class NoInit(object):
|
||||
|
||||
def use_y(self):
|
||||
return self.y
|
||||
|
||||
#This is also OK
|
||||
class SetLocally2(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def use_y(self):
|
||||
self.x = 0
|
||||
self.y = 1
|
||||
if cond:
|
||||
print(self.y)
|
||||
else:
|
||||
return False
|
||||
return self.y
|
||||
|
||||
#Guarded
|
||||
class Guarded(object):
|
||||
|
||||
def __init__(self):
|
||||
self.guard = False
|
||||
|
||||
def set_x(self):
|
||||
self.guard = True
|
||||
self.x = 1
|
||||
|
||||
def use_x(self):
|
||||
if self.guard:
|
||||
return self.x
|
||||
else:
|
||||
return 0
|
||||
|
||||
#ODASA-2034
|
||||
class ODASA2034A(object):
|
||||
|
||||
def __init__(self, data):
|
||||
d = self.__dict__
|
||||
d['data'] = data
|
||||
|
||||
def use_data(self):
|
||||
return self.data
|
||||
|
||||
class ODASA2034B(object):
|
||||
|
||||
def __init__(self, key, value):
|
||||
d = self.__dict__
|
||||
d[key] = value
|
||||
|
||||
def use_data(self):
|
||||
return self.data
|
||||
|
||||
|
||||
class Test5(object):
|
||||
|
||||
def readFromStream(stream):
|
||||
word = stream.read(4)
|
||||
if word == "true":
|
||||
return BooleanObject(True)
|
||||
elif word == "fals":
|
||||
stream.read(1)
|
||||
return BooleanObject(False)
|
||||
assert False
|
||||
readFromStream = staticmethod(readFromStream)
|
||||
|
||||
|
||||
class Test1(object):
|
||||
|
||||
def __init__(self, application):
|
||||
self.rabbitmq_channel = None
|
||||
|
||||
def queue_declared(frame): # called in callback
|
||||
self.return_queue = frame.method.queue
|
||||
|
||||
def use_it(self):
|
||||
return self.return_queue
|
||||
|
||||
|
||||
#Check for FPs when overriding builtin methods
|
||||
|
||||
class PrintingDict(dict):
|
||||
|
||||
def pop(self):
|
||||
print ("pop")
|
||||
return dict.pop(self)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
print("__setitem__")
|
||||
return dict.__setitem__(self, key, value)
|
||||
|
||||
#Locally set by Call
|
||||
#ODASA-4612
|
||||
class WSGIContext(object):
|
||||
|
||||
def _app_call(self, env):
|
||||
self._response_headers = None
|
||||
|
||||
|
||||
class Odasa4612(WSGIContext):
|
||||
|
||||
def meth1(self, arg):
|
||||
val = self._app_call(arg)
|
||||
if self._response_headers is None:
|
||||
self._response_headers = []
|
||||
|
||||
|
||||
|
||||
class OK9(object):
|
||||
cls_attr = 0
|
||||
def __init__(self):
|
||||
self.attr = self.cls_attr
|
||||
|
||||
|
||||
|
||||
class Foo1(object):
|
||||
pass
|
||||
|
||||
foo = Foo1()
|
||||
setattr(foo, 'a', 1)
|
||||
assert foo.a == 1
|
||||
|
||||
class Foo2(object):
|
||||
pass
|
||||
|
||||
setattr(Foo2, 'a', 1)
|
||||
assert Foo2.a == 1
|
||||
|
||||
# False positive observed at customer
|
||||
|
||||
class Customer1(object):
|
||||
|
||||
def x(self):
|
||||
if not hasattr(self, "attr"):
|
||||
return None
|
||||
else:
|
||||
return self.attr
|
||||
|
||||
#ODASA-4619
|
||||
class Odasa4619a(object):
|
||||
|
||||
def call(self):
|
||||
host = self.glance_host
|
||||
port = self.glance_port
|
||||
|
||||
|
||||
class Odasa4619b(object):
|
||||
|
||||
def call(self):
|
||||
host = self.glance_host
|
||||
port = self.glance_port
|
||||
|
||||
@decorator
|
||||
def foo(self):
|
||||
(x, self.glance_host,
|
||||
self.glance_port, y) = bar()
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:28:1:28:23 | Class Useless1 | Class Useless1 defines only one public method, which should be replaced by a function. |
|
||||
| test.py:37:1:37:23 | Class Useless2 | Class Useless2 defines only one public method, which should be replaced by a function. |
|
||||
@@ -0,0 +1 @@
|
||||
Classes/UselessClass.ql
|
||||
65
python/ql/test/query-tests/Classes/useless/test.py
Normal file
65
python/ql/test/query-tests/Classes/useless/test.py
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
#Useless class
|
||||
|
||||
class Useful1(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def do_something(self):
|
||||
pass
|
||||
|
||||
def do_something_else(self):
|
||||
pass
|
||||
|
||||
def do_yet_another_thing(self):
|
||||
pass
|
||||
|
||||
|
||||
class Useful2(object):
|
||||
|
||||
def do_something(self):
|
||||
pass
|
||||
|
||||
def do_something_else(self):
|
||||
pass
|
||||
|
||||
|
||||
class Useless1(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def do_something(self):
|
||||
pass
|
||||
|
||||
|
||||
class Useless2(object):
|
||||
|
||||
def do_something(self):
|
||||
pass
|
||||
|
||||
class Stateful1(object):
|
||||
|
||||
def __init__(self):
|
||||
self.data = []
|
||||
|
||||
def do_something(self, x):
|
||||
self.data.append(x)
|
||||
|
||||
|
||||
class Stateful2(object):
|
||||
|
||||
def __init__(self):
|
||||
self.data = None
|
||||
|
||||
def do_something(self, x):
|
||||
self.data = x
|
||||
|
||||
class Inherited(object):
|
||||
pass
|
||||
|
||||
class Inherits(Inherited):
|
||||
|
||||
def do_something(self):
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
| exceptions_test.py:7:5:7:11 | ExceptStmt | Except block directly handles BaseException. |
|
||||
| exceptions_test.py:71:5:71:25 | ExceptStmt | Except block directly handles BaseException. |
|
||||
@@ -0,0 +1 @@
|
||||
Exceptions/CatchingBaseException.ql
|
||||
2
python/ql/test/query-tests/Exceptions/general/EmptyExcept.expected
Executable file
2
python/ql/test/query-tests/Exceptions/general/EmptyExcept.expected
Executable file
@@ -0,0 +1,2 @@
|
||||
| exceptions_test.py:7:5:7:11 | ExceptStmt | 'except' clause does nothing but pass and there is no explanatory comment. |
|
||||
| exceptions_test.py:13:5:13:21 | ExceptStmt | 'except' clause does nothing but pass and there is no explanatory comment. |
|
||||
@@ -0,0 +1 @@
|
||||
Exceptions/EmptyExcept.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| exceptions_test.py:51:5:51:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:33:1:33:28 | ControlFlowNode for ClassExpr | class 'NotException1' |
|
||||
| exceptions_test.py:54:5:54:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:36:1:36:28 | ControlFlowNode for ClassExpr | class 'NotException2' |
|
||||
| exceptions_test.py:112:5:112:22 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:107:12:107:14 | ControlFlowNode for FloatLiteral | instance of 'float' |
|
||||
| pypy_test.py:14:5:14:14 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | pypy_test.py:14:12:14:13 | ControlFlowNode for IntegerLiteral | instance of 'int' |
|
||||
@@ -0,0 +1 @@
|
||||
Exceptions/IllegalExceptionHandlerType.ql
|
||||
@@ -0,0 +1,3 @@
|
||||
| exceptions_test.py:40:5:40:23 | Raise | Illegal class 'NotException1' raised; will result in a TypeError being raised instead. |
|
||||
| exceptions_test.py:43:5:43:21 | Raise | Illegal class 'str' raised; will result in a TypeError being raised instead. |
|
||||
| exceptions_test.py:46:5:46:25 | Raise | Illegal class 'NotException2' raised; will result in a TypeError being raised instead. |
|
||||
@@ -0,0 +1 @@
|
||||
Exceptions/IllegalRaise.ql
|
||||
@@ -0,0 +1 @@
|
||||
| exceptions_test.py:64:1:64:22 | ExceptStmt | Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference. | file://:Compiled Code:0:0:0:0 | builtin-class AttributeError | AttributeError | exceptions_test.py:62:1:62:17 | ExceptStmt | except block | file://:Compiled Code:0:0:0:0 | builtin-class Exception | Exception |
|
||||
@@ -0,0 +1 @@
|
||||
Exceptions/IncorrectExceptOrder.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
| exceptions_test.py:170:11:170:24 | NotImplemented | NotImplemented is not an Exception. Did you mean NotImplementedError? |
|
||||
| exceptions_test.py:173:11:173:24 | NotImplemented | NotImplemented is not an Exception. Did you mean NotImplementedError? |
|
||||
@@ -0,0 +1 @@
|
||||
Exceptions/NotImplementedIsNotAnException.ql
|
||||
173
python/ql/test/query-tests/Exceptions/general/exceptions_test.py
Normal file
173
python/ql/test/query-tests/Exceptions/general/exceptions_test.py
Normal file
@@ -0,0 +1,173 @@
|
||||
|
||||
#Empty Except
|
||||
|
||||
def ee1(val):
|
||||
try:
|
||||
val.attr
|
||||
except:
|
||||
pass
|
||||
|
||||
def ee1(val):
|
||||
try:
|
||||
val.attr()
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def ee2(val):
|
||||
try:
|
||||
val.attr
|
||||
except Error:
|
||||
#But it is OK if there is a comment
|
||||
pass
|
||||
|
||||
#OK with an else clause as well...
|
||||
|
||||
def ee3(val):
|
||||
try:
|
||||
val.attr
|
||||
except Error:
|
||||
pass
|
||||
else:
|
||||
return 42
|
||||
|
||||
class NotException1(object):
|
||||
pass
|
||||
|
||||
class NotException2(object):
|
||||
pass
|
||||
|
||||
def illegal_raise_type():
|
||||
raise NotException1
|
||||
|
||||
def illegal_raise_value1():
|
||||
raise "Exception"
|
||||
|
||||
def illegal_raise_value2():
|
||||
raise NotException2()
|
||||
|
||||
def illegal_handler():
|
||||
try:
|
||||
illegal_raise()
|
||||
except NotException1:
|
||||
#Must do something
|
||||
print("NotException1")
|
||||
except NotException2:
|
||||
#Must do something
|
||||
print("NotException2")
|
||||
|
||||
|
||||
#Incorrect except order
|
||||
try:
|
||||
val.attr
|
||||
except Exception:
|
||||
print (2)
|
||||
except AttributeError:
|
||||
print (3)
|
||||
|
||||
#Catch BaseException
|
||||
def catch_base_exception():
|
||||
try:
|
||||
illegal_raise()
|
||||
except BaseException:
|
||||
#Consumes KeyboardInterrupt
|
||||
pass
|
||||
|
||||
def catch_base_exception_ok():
|
||||
try:
|
||||
illegal_raise()
|
||||
except BaseException:
|
||||
raise
|
||||
|
||||
def legal_handler1():
|
||||
try:
|
||||
illegal_raise()
|
||||
except (IOError, KeyError):
|
||||
print ("Caught exception")
|
||||
|
||||
pair = IOError, KeyError
|
||||
triple = pair, AttributeError
|
||||
|
||||
def legal_handler2():
|
||||
try:
|
||||
illegal_raise()
|
||||
except pair:
|
||||
print ("Caught exception")
|
||||
try:
|
||||
illegal_raise()
|
||||
except triple:
|
||||
print ("Caught exception")
|
||||
|
||||
def legal_handler3():
|
||||
try:
|
||||
illegal_raise()
|
||||
except could_be_anything():
|
||||
print ("Caught exception")
|
||||
|
||||
def a_number():
|
||||
return 4.0
|
||||
|
||||
def illegal_handler2():
|
||||
try:
|
||||
illegal_raise()
|
||||
except a_number():
|
||||
print ("Caught exception")
|
||||
|
||||
def stop_iter_ok(seq):
|
||||
try:
|
||||
next(seq)
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
#Guarded None in nested function
|
||||
def f(x=None):
|
||||
def inner(arg):
|
||||
if x:
|
||||
raise x
|
||||
|
||||
#ODASA-4705
|
||||
def g(cond):
|
||||
try:
|
||||
if cond:
|
||||
return may_raise_io_error()
|
||||
else:
|
||||
raise KeyError
|
||||
except IOError:
|
||||
pass # This is OK, as it is just passing to the following statement which handles the exception.
|
||||
return 0
|
||||
|
||||
def ee4(x):
|
||||
try:
|
||||
del x.attr
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def ee5(x):
|
||||
try:
|
||||
x[0]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
def ee6(x):
|
||||
try:
|
||||
del x[0]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
def ee7(x):
|
||||
try:
|
||||
delattr(x, "attr")
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def ee8(x):
|
||||
try:
|
||||
x.encode("martian-18")
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
|
||||
#These are so common, we give warnings not errors.
|
||||
def foo():
|
||||
raise NotImplemented
|
||||
|
||||
def bar():
|
||||
raise NotImplemented()
|
||||
20
python/ql/test/query-tests/Exceptions/general/pypy_test.py
Normal file
20
python/ql/test/query-tests/Exceptions/general/pypy_test.py
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
def test():
|
||||
class A(BaseException):
|
||||
class __metaclass__(type):
|
||||
def __getattribute__(self, name):
|
||||
if flag and name == '__bases__':
|
||||
fail("someone read bases attr")
|
||||
else:
|
||||
return type.__getattribute__(self, name)
|
||||
|
||||
try:
|
||||
a = A()
|
||||
raise a
|
||||
except 42:
|
||||
#Some comment
|
||||
pass
|
||||
except A:
|
||||
#Another comment
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:5:15:5:22 | ControlFlowNode for next() | Call to next() in a generator |
|
||||
| test.py:10:20:10:27 | ControlFlowNode for next() | Call to next() in a generator |
|
||||
@@ -0,0 +1 @@
|
||||
Exceptions/UnguardedNextInGenerator.ql
|
||||
49
python/ql/test/query-tests/Exceptions/generators/test.py
Normal file
49
python/ql/test/query-tests/Exceptions/generators/test.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#Unguarded calls to next()
|
||||
|
||||
def bad1(it):
|
||||
while True:
|
||||
yield next(it)
|
||||
|
||||
def bad2(seq):
|
||||
it = iter(seq)
|
||||
#Not OK as seq may be empty
|
||||
raise KeyError(next(it))
|
||||
yield 0
|
||||
|
||||
def ok1(seq):
|
||||
#Not a generator
|
||||
it = iter(seq)
|
||||
#Not OK as seq may be empty
|
||||
raise KeyError(next(it))
|
||||
|
||||
def ok2(seq):
|
||||
if seq:
|
||||
it = iter(seq)
|
||||
#OK seq is non-empty so next(it) will not raise StopIteration
|
||||
raise KeyError(next(it))
|
||||
yield 0
|
||||
|
||||
def explicit_raise_stop_iter(seq):
|
||||
for i in seq:
|
||||
yield seq
|
||||
raise StopIteration()
|
||||
|
||||
def ok3(seq):
|
||||
it = iter(seq)
|
||||
try:
|
||||
yield next(iter)
|
||||
except StopIteration:
|
||||
return
|
||||
|
||||
def ok4(seq, ctx):
|
||||
try:
|
||||
with ctx:
|
||||
yield next(iter)
|
||||
except StopIteration:
|
||||
return
|
||||
|
||||
#ODASA-6536
|
||||
def next_in_comp(seq, fields):
|
||||
seq_iter = iter(seq)
|
||||
values = [ next(seq_iter) if f.attname in NAMES else DEFAULT for f in fields ]
|
||||
return values
|
||||
@@ -0,0 +1,5 @@
|
||||
| wrong_arguments.py:57:1:57:7 | f0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 |
|
||||
| wrong_arguments.py:58:1:58:7 | f1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |
|
||||
| wrong_arguments.py:59:1:59:12 | f2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:9:1:9:14 | Function f2 | function f2 |
|
||||
| wrong_arguments.py:103:1:103:27 | f6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
|
||||
| wrong_arguments.py:115:1:115:13 | f1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/WrongNameForArgumentInCall.ql
|
||||
@@ -0,0 +1,25 @@
|
||||
| use_mox.py:28:1:28:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:7:1:7:10 | Function f0 | function f0 |
|
||||
| use_mox.py:29:1:29:5 | f1() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:10:1:10:13 | Function f1 | function f1 |
|
||||
| use_mox.py:32:1:32:8 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:15:5:15:20 | Function m0 | method C.m0 |
|
||||
| use_mox.py:33:1:33:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:18:5:18:23 | Function m1 | method C.m1 |
|
||||
| wrong_arguments.py:29:1:29:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 |
|
||||
| wrong_arguments.py:30:1:30:4 | f1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |
|
||||
| wrong_arguments.py:31:1:31:4 | f2() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:9:1:9:14 | Function f2 | function f2 |
|
||||
| wrong_arguments.py:32:1:32:4 | f3() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:12:1:12:24 | Function f3 | function f3 |
|
||||
| wrong_arguments.py:33:1:33:4 | f4() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:15:1:15:15 | Function f4 | function f4 |
|
||||
| wrong_arguments.py:34:1:34:4 | f5() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:18:1:18:25 | Function f5 | function f5 |
|
||||
| wrong_arguments.py:35:1:35:5 | f6() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
|
||||
| wrong_arguments.py:36:1:36:7 | f7() | Call to $@ with too few arguments; should be no fewer than 3. | wrong_arguments.py:24:1:24:16 | Function f7 | function f7 |
|
||||
| wrong_arguments.py:40:1:40:7 | f0() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 |
|
||||
| wrong_arguments.py:41:1:41:9 | f1() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |
|
||||
| wrong_arguments.py:42:1:42:9 | f5() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:18:1:18:25 | Function f5 | function f5 |
|
||||
| wrong_arguments.py:43:1:43:9 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
|
||||
| wrong_arguments.py:44:1:44:11 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
|
||||
| wrong_arguments.py:81:1:81:5 | l0() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:70:6:70:15 | Function lambda | function lambda |
|
||||
| wrong_arguments.py:82:1:82:7 | l1() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:71:6:71:21 | Function lambda | function lambda |
|
||||
| wrong_arguments.py:83:1:83:8 | l1d() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:72:7:72:25 | Function lambda | function lambda |
|
||||
| wrong_arguments.py:86:1:86:4 | l1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:71:6:71:21 | Function lambda | function lambda |
|
||||
| wrong_arguments.py:96:1:96:12 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
|
||||
| wrong_arguments.py:97:1:97:7 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
|
||||
| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:126:5:126:31 | Function spam | method Eggs2.spam |
|
||||
| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:121:5:121:19 | Function spam | method Eggs1.spam |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/WrongNumberArgumentsInCall.ql
|
||||
1
python/ql/test/query-tests/Expressions/Arguments/mox.py
Normal file
1
python/ql/test/query-tests/Expressions/Arguments/mox.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
33
python/ql/test/query-tests/Expressions/Arguments/use_mox.py
Normal file
33
python/ql/test/query-tests/Expressions/Arguments/use_mox.py
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
import mox
|
||||
|
||||
#Use it
|
||||
mox
|
||||
|
||||
def f0(x):
|
||||
pass
|
||||
|
||||
def f1(x, y):
|
||||
pass
|
||||
|
||||
class C(object):
|
||||
|
||||
def m0(self, x):
|
||||
pass
|
||||
|
||||
def m1(self, x, y):
|
||||
pass
|
||||
|
||||
# These are treated as magically OK since we are using mox
|
||||
|
||||
C.m0(1)
|
||||
C.m1(1,2)
|
||||
|
||||
#But normal functions are treated normally
|
||||
|
||||
f0()
|
||||
f1(1)
|
||||
|
||||
#As are normal methods
|
||||
C().m0()
|
||||
C().m1(1)
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
def f0(x):
|
||||
pass
|
||||
|
||||
def f1(x, y = None):
|
||||
pass
|
||||
|
||||
def f2(x, *y):
|
||||
pass
|
||||
|
||||
def f3(x, y = None, *z):
|
||||
pass
|
||||
|
||||
def f4(x, **y):
|
||||
pass
|
||||
|
||||
def f5(x, y = None, **z):
|
||||
pass
|
||||
|
||||
def f6(x, y):
|
||||
pass
|
||||
|
||||
def f7(x, y, z):
|
||||
pass
|
||||
|
||||
# Too few arguments
|
||||
|
||||
f0()
|
||||
f1()
|
||||
f2()
|
||||
f3()
|
||||
f4()
|
||||
f5()
|
||||
f6(1)
|
||||
f7(1,2)
|
||||
|
||||
#Too many arguments
|
||||
|
||||
f0(1,2)
|
||||
f1(1,2,3)
|
||||
f5(1,2,3)
|
||||
f6(1,2,3)
|
||||
f6(1,2,3,4)
|
||||
|
||||
#OK
|
||||
|
||||
#Not too few
|
||||
f7(*t)
|
||||
|
||||
#Not too many
|
||||
|
||||
f2(1,2,3,4,5,6)
|
||||
|
||||
|
||||
#Illegal name
|
||||
f0(y=1)
|
||||
f1(z=1)
|
||||
f2(x=0, y=1)
|
||||
|
||||
|
||||
#Ok name
|
||||
f0(x=0)
|
||||
f1(x=0, y=1)
|
||||
f4(q=4)
|
||||
|
||||
#This is correct, but a bit weird.
|
||||
f6(**{'x':1, 'y':2})
|
||||
|
||||
l0 = lambda : 0
|
||||
l1 = lambda x : 2 * x
|
||||
l1d = lambda x = 0 : 2 *x
|
||||
|
||||
#OK
|
||||
l0()
|
||||
l1(1)
|
||||
l1d()
|
||||
l1d(1)
|
||||
|
||||
#Too many
|
||||
l0(1)
|
||||
l1(1,2)
|
||||
l1d(1,2)
|
||||
|
||||
#Too few
|
||||
l1()
|
||||
|
||||
|
||||
t2 = (1,2)
|
||||
t3 = (1,2,3)
|
||||
|
||||
#Ok
|
||||
f(*t2)
|
||||
|
||||
#Too many
|
||||
f6(*(1,2,3))
|
||||
f6(*t3)
|
||||
|
||||
#Ok
|
||||
f6(**{'x':1, 'y':2})
|
||||
|
||||
#Illegal name
|
||||
f6(**{'x':1, 'y':2, 'z':3})
|
||||
|
||||
#Theoretically -1 arguments required. Don't report
|
||||
class C(object):
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
C().f()
|
||||
|
||||
|
||||
#Too many and wrong name -- check only wrong name is flagged.
|
||||
f1(x, y, z=1)
|
||||
|
||||
|
||||
#Overriding and call is wrong.
|
||||
class Eggs1(object):
|
||||
|
||||
def spam(self):
|
||||
pass
|
||||
|
||||
class Eggs2(Eggs1):
|
||||
|
||||
def spam(self, arg0, arg1):
|
||||
pass
|
||||
|
||||
e = Eggs1() if cond else Eggs2()
|
||||
e.spam(0)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:3:17:3:23 | Str | Formatting string mixes implicitly and explicitly numbered fields. |
|
||||
| test.py:8:17:8:23 | Str | Formatting string mixes implicitly and explicitly numbered fields. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Formatting/MixedExplicitImplicitIn3101Format.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.py:29:1:29:50 | Attribute() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:5:20:5:29 | Str | "{0}, {1}" |
|
||||
| test.py:30:1:30:51 | format() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:10:20:10:29 | Str | "{0}, {1}" |
|
||||
| test.py:32:1:32:50 | Attribute() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:6:20:6:27 | Str | "{}, {}" |
|
||||
| test.py:33:1:33:51 | format() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:11:20:11:27 | Str | "{}, {}" |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Formatting/UnusedArgumentIn3101Format.ql
|
||||
@@ -0,0 +1,8 @@
|
||||
| test.py:17:1:17:44 | Attribute() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:4:17:4:31 | Str | format "{name!r}, {0}" |
|
||||
| test.py:18:1:18:45 | format() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:9:17:9:31 | Str | format "{name!r}, {0}" |
|
||||
| test.py:20:1:20:49 | Attribute() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:4:17:4:31 | Str | format "{name!r}, {0}" |
|
||||
| test.py:21:1:21:50 | format() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:9:17:9:31 | Str | format "{name!r}, {0}" |
|
||||
| test.py:45:1:45:35 | format() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:37:14:37:18 | Str | any format used. |
|
||||
| test.py:45:1:45:35 | format() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:39:14:39:18 | Str | any format used. |
|
||||
| test.py:46:1:46:34 | Attribute() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:37:14:37:18 | Str | any format used. |
|
||||
| test.py:46:1:46:34 | Attribute() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:39:14:39:18 | Str | any format used. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:17:1:17:44 | Attribute() | Missing named argument for string format. Format $@ requires 'name', but it is omitted. | test.py:4:17:4:31 | Str | "{name!r}, {0}" |
|
||||
| test.py:18:1:18:45 | format() | Missing named argument for string format. Format $@ requires 'name', but it is omitted. | test.py:9:17:9:31 | Str | "{name!r}, {0}" |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql
|
||||
@@ -0,0 +1,6 @@
|
||||
| test.py:20:1:20:49 | Attribute() | Too few arguments for string format. Format $@ requires at least 1, but 0 are provided. | test.py:4:17:4:31 | Str | "{name!r}, {0}" |
|
||||
| test.py:21:1:21:50 | format() | Too few arguments for string format. Format $@ requires at least 1, but 0 are provided. | test.py:9:17:9:31 | Str | "{name!r}, {0}" |
|
||||
| test.py:23:1:23:32 | Attribute() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:5:20:5:29 | Str | "{0}, {1}" |
|
||||
| test.py:24:1:24:33 | format() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:10:20:10:29 | Str | "{0}, {1}" |
|
||||
| test.py:26:1:26:32 | Attribute() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:6:20:6:27 | Str | "{}, {}" |
|
||||
| test.py:27:1:27:33 | format() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:11:20:11:27 | Str | "{}, {}" |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql
|
||||
108
python/ql/test/query-tests/Expressions/Formatting/test.py
Executable file
108
python/ql/test/query-tests/Expressions/Formatting/test.py
Executable file
@@ -0,0 +1,108 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
mixed_format1 = "{}{1}"
|
||||
named_format1 = "{name!r}, {0}"
|
||||
explicit_format1 = "{0}, {1}"
|
||||
implicit_format1 = "{}, {}"
|
||||
|
||||
mixed_format2 = "{}{1}"
|
||||
named_format2 = "{name!r}, {0}"
|
||||
explicit_format2 = "{0}, {1}"
|
||||
implicit_format2 = "{}, {}"
|
||||
|
||||
|
||||
mixed_format1.format("Hello", "World")
|
||||
format(mixed_format2, "Hello", "World")
|
||||
|
||||
named_format1.format("Hello", world="World")
|
||||
format(named_format2, "Hello", world="World")
|
||||
|
||||
named_format1.format(name="Hello", world="World")
|
||||
format(named_format2, name="Hello", world="World")
|
||||
|
||||
explicit_format1.format("Hello")
|
||||
format(explicit_format2, "Hello")
|
||||
|
||||
implicit_format1.format("Hello")
|
||||
format(implicit_format2, "Hello")
|
||||
|
||||
explicit_format1.format("Hello", "World", "Extra")
|
||||
format(explicit_format2, "Hello", "World", "Extra")
|
||||
|
||||
implicit_format1.format("Hello", "World", "Extra")
|
||||
format(implicit_format2, "Hello", "World", "Extra")
|
||||
|
||||
#OK ODASA-3197
|
||||
if cond:
|
||||
x_or_y = "{x}"
|
||||
else:
|
||||
x_or_y = "{y}"
|
||||
|
||||
format(x_or_y, x="x", y="y")
|
||||
x_or_y.format(x="x", y="y")
|
||||
|
||||
#Still fail for multiple formats
|
||||
format(x_or_y, x="x", y="y", z="z")
|
||||
x_or_y.format(x="x", y="y", z="z")
|
||||
|
||||
#False positive reported by customer. -- Verify fix.
|
||||
"<td class={}>{{}}></td>".format(html_class)
|
||||
|
||||
"{{0}}{0}".format("X")
|
||||
"{{{}}}".format("X")
|
||||
|
||||
#Crash JDK regex engine -- unless possessive quantifiers are used.
|
||||
(
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
|
||||
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}"
|
||||
)
|
||||
|
||||
#Make sure nested braces are handled properly:
|
||||
"ANS_{}=${{{}}}".format(x, xID)
|
||||
|
||||
def foo(msg_width):
|
||||
stdout.write(u'{}\r{}{:<{}}'.format(1, 2, 3, 4))
|
||||
stdout.write(u'{}\r{}{:<{width}}'.format(1, 2, 3, width=msg_width))
|
||||
|
||||
#Check parsing with punctuation.
|
||||
"invalid value of type {.__name__}: {}".format(int, 1)
|
||||
|
||||
def varying_format(cond):
|
||||
fmt = "{}" if cond else "{}, {}"
|
||||
return fmt.format("hello", "world")
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:17:12:17:22 | Str | Backspace escape in regular expression at offset 1. |
|
||||
| test.py:19:12:19:28 | Str | Backspace escape in regular expression at offset 8. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Regex/BackspaceEscape.ql
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.py:41:12:41:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. |
|
||||
| test.py:42:12:42:19 | Str | This regular expression includes duplicate character '0' in a set of characters. |
|
||||
| test.py:43:12:43:21 | Str | This regular expression includes duplicate character '-' in a set of characters. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Regex/DuplicateCharacterInSet.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:22:12:22:29 | Str | Regular expression is missing '?' in named group. |
|
||||
| test.py:23:12:23:33 | Str | Regular expression is missing '?' in named group. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Regex/MissingPartSpecialGroup.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.py:4:12:4:19 | Str | This regular expression includes an unmatchable caret at offset 1. |
|
||||
| test.py:5:12:5:23 | Str | This regular expression includes an unmatchable caret at offset 5. |
|
||||
| test.py:6:12:6:21 | Str | This regular expression includes an unmatchable caret at offset 2. |
|
||||
| test.py:74:12:74:27 | Str | This regular expression includes an unmatchable caret at offset 8. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Regex/UnmatchableCaret.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.py:29:12:29:19 | Str | This regular expression includes an unmatchable dollar at offset 3. |
|
||||
| test.py:30:12:30:23 | Str | This regular expression includes an unmatchable dollar at offset 3. |
|
||||
| test.py:31:12:31:20 | Str | This regular expression includes an unmatchable dollar at offset 2. |
|
||||
| test.py:75:12:75:26 | Str | This regular expression includes an unmatchable dollar at offset 3. |
|
||||
@@ -0,0 +1 @@
|
||||
Expressions/Regex/UnmatchableDollar.ql
|
||||
1
python/ql/test/query-tests/Expressions/Regex/options
Normal file
1
python/ql/test/query-tests/Expressions/Regex/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=2
|
||||
135
python/ql/test/query-tests/Expressions/Regex/test.py
Normal file
135
python/ql/test/query-tests/Expressions/Regex/test.py
Normal file
@@ -0,0 +1,135 @@
|
||||
import re
|
||||
|
||||
#Unmatchable caret
|
||||
re.compile(b' ^abc')
|
||||
re.compile(b"(?s) ^abc")
|
||||
re.compile(b"\[^123]")
|
||||
|
||||
#Likely false positives for unmatchable caret
|
||||
re.compile(b"[^123]")
|
||||
re.compile(b"[123^]")
|
||||
re.sub(b'(?m)^(?!$)', indent*' ', s)
|
||||
re.compile(b"()^abc")
|
||||
re.compile(b"(?:(?:\n\r?)|^)( *)\S")
|
||||
re.compile(b"^diff (?:-r [0-9a-f]+ ){1,2}(.*)$")
|
||||
|
||||
#Backspace escape
|
||||
re.compile(br"[\b\t ]") # Should warn
|
||||
re.compile(br"E\d+\b.*") # Fine
|
||||
re.compile(br"E\d+\b[ \b\t]") #Both
|
||||
|
||||
#Missing part in named group
|
||||
re.compile(br'(P<name>[\w]+)')
|
||||
re.compile(br'(_(P<name>[\w]+)|)')
|
||||
#This is OK...
|
||||
re.compile(br'(?P<name>\w+)')
|
||||
|
||||
|
||||
#Unmatchable dollar
|
||||
re.compile(b"abc$ ")
|
||||
re.compile(b"abc$ (?s)")
|
||||
re.compile(b"\[$] ")
|
||||
|
||||
#Likely false positives for unmatchable dollar
|
||||
re.compile(b"[$] ")
|
||||
re.compile(b"\$ ")
|
||||
re.compile(b"abc$(?m)")
|
||||
re.compile(b"abc$()")
|
||||
|
||||
|
||||
#Duplicate character in set
|
||||
re.compile(b"[AA]")
|
||||
re.compile(b"[000]")
|
||||
re.compile(b"[-0-9-]")
|
||||
|
||||
#Possible false positives
|
||||
re.compile(b"[S\S]")
|
||||
re.compile(b"[0\000]")
|
||||
re.compile(b"[\0000]")
|
||||
re.compile(b"[^^]")
|
||||
re.compile(b"[-0-9]")
|
||||
re.compile(b"[]]")
|
||||
re.compile(b"^^^x.*")
|
||||
re.compile(b".*x$$$")
|
||||
re.compile(b"x*^y")
|
||||
re.compile(b"x$y*")
|
||||
|
||||
# False positive for unmatchable caret
|
||||
re.compile(br'(?!DEFAULT_PREFS)(?!CAN_SET_ANON)^[A-Z_]+$')
|
||||
|
||||
#Equivalent for unmatchable dollar
|
||||
re.compile(br'^[A-Z_]+(?!DEFAULT_PREFS)(?!CAN_SET_ANON)$')
|
||||
|
||||
#And for negative look-behind assertions
|
||||
re.compile(br'(?<!DEFAULT_PREFS)(?<!CAN_SET_ANON)^[A-Z_]+$')
|
||||
re.compile(br'^[A-Z_]+(?<!DEFAULT_PREFS)(?<!CAN_SET_ANON)$')
|
||||
|
||||
|
||||
#OK
|
||||
re.compile(br'(?=foo)^\w+')
|
||||
re.compile(br'\w+$(?<=foo)')
|
||||
|
||||
|
||||
#Not OK
|
||||
re.compile(br'(?<=foo)^\w+')
|
||||
re.compile(br'\w+$(?=foo)')
|
||||
|
||||
|
||||
#OK -- ODASA-ODASA-3968
|
||||
re.compile('(?:[^%]|^)?%\((\w*)\)[a-z]')
|
||||
|
||||
#ODASA-3985
|
||||
#Half Surrogate pairs
|
||||
re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]')
|
||||
#Outside BMP
|
||||
re.compile(u'[\U00010000-\U0010ffff]')
|
||||
|
||||
#ODASA-6394 -- Flags defined by keyword
|
||||
REGEX0 = re.compile(r''' ^\s* ''', re.VERBOSE)
|
||||
|
||||
REGEX1 = re.compile(r''' ^\s* ''', flags=re.VERBOSE)
|
||||
|
||||
REGEX2 = re.compile(r'''
|
||||
^\s*
|
||||
(?P<modifier>[+-]?)
|
||||
(?: (?P<week> \d+ (?:\.\d*)? ) \s* [wW] )? \s*
|
||||
(?: (?P<day> \d+ (?:\.\d*)? ) \s* [dD] )? \s*
|
||||
(?: (?P<hour> \d+ (?:\.\d*)? ) \s* [hH] )? \s*
|
||||
(?: (?P<minute> \d+ (?:\.\d*)? ) \s* [mM] )? \s*
|
||||
(?: (?P<second> \d+ (?:\.\d*)? ) \s* [sS] )? \s*
|
||||
$
|
||||
''',
|
||||
flags=re.VERBOSE)
|
||||
|
||||
REGEX3 = re.compile(r'''
|
||||
^\s*
|
||||
(?P<modifier>[+-]?)
|
||||
(?: (?P<week> \d+ (?:\.\d*)? ) \s* [wW] )? \s*
|
||||
(?: (?P<day> \d+ (?:\.\d*)? ) \s* [dD] )? \s*
|
||||
(?: (?P<hour> \d+ (?:\.\d*)? ) \s* [hH] )? \s*
|
||||
(?: (?P<minute> \d+ (?:\.\d*)? ) \s* [mM] )? \s*
|
||||
(?: (?P<second> \d+ (?:\.\d*)? ) \s* [sS] )? \s*
|
||||
$
|
||||
''',
|
||||
re.VERBOSE)
|
||||
|
||||
#ODASA-6780
|
||||
DYLIB_RE = re.compile(r"""(?x)
|
||||
(?P<location>^.*)(?:^|/)
|
||||
(?P<name>
|
||||
(?P<shortname>\w+?)
|
||||
(?:\.(?P<version>[^._]+))?
|
||||
(?:_(?P<suffix>[^._]+))?
|
||||
\.dylib$
|
||||
)
|
||||
""")
|
||||
|
||||
#ODASA-6786
|
||||
VERBOSE_REGEX = r"""
|
||||
\[ # [
|
||||
(?P<header>[^]]+) # very permissive!
|
||||
\] # ]
|
||||
"""
|
||||
|
||||
# Compiled regular expression marking it as verbose
|
||||
ODASA_6786 = re.compile(VERBOSE_REGEX, re.VERBOSE)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user