From 38acea633fefe1dae5d54ca1ef2d39d846e276c9 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 27 Jul 2020 17:58:21 +0200 Subject: [PATCH 01/27] Python: Dataflow, expand callable to classes --- .../dataflow/internal/DataFlowPrivate.qll | 58 ++++++++++++++++++- .../consistency/dataflow-consistency.expected | 2 + 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 95c72ef7e14..4221c90cf74 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -97,8 +97,64 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { //-------- // Global flow //-------- +/** + * IPA type for DataFlowCallable. + * A callable is either a callable value or a class. + */ +newtype TDataFlowCallable = + TCallableValue(CallableValue callable) or + TClassValue(ClassValue c) + /** Represents a callable */ -class DataFlowCallable = CallableValue; +abstract class DataFlowCallable extends TDataFlowCallable { + /** Gets a textual representation of this element. */ + abstract string toString(); + + /** Gets a call to this callable. */ + abstract CallNode getACall(); + + /** Gets the scope of this callable */ + abstract Scope getScope(); + + /** Gets the specified parameter of this callable */ + abstract NameNode getParameter(int n); + + /** Gets the name of this callable. */ + abstract string getName(); +} + +class DataFlowCallableValue extends DataFlowCallable, TCallableValue { + CallableValue callable; + + DataFlowCallableValue() { this = TCallableValue(callable) } + + override string toString() { result = callable.toString() } + + override CallNode getACall() { result = callable.getACall() } + + override Scope getScope() { result = callable.getScope() } + + override NameNode getParameter(int n) { result = callable.getParameter(n) } + + override string getName() { result = callable.getName() } +} + +class DataFlowClassValue extends DataFlowCallable, TClassValue { + ClassValue c; + + DataFlowClassValue() { this = TClassValue(c) } + + override string toString() { result = c.toString() } + + override CallNode getACall() { result = c.getACall() } + + override Scope getScope() { result = c.getScope() } + + override NameNode getParameter(int n) { result.getNode() = c.getScope().getInitMethod().getArg(n+1).asName() } + + override string getName() { result = c.getName() } +} + /** Represents a call to a callable */ class DataFlowCall extends CallNode { diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected index eaf7a166e23..2ae31eb1126 100644 --- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected @@ -101,3 +101,5 @@ argHasPostUpdate | test.py:74:17:74:17 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | | test.py:81:13:81:13 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | | test.py:86:13:86:13 | ControlFlowNode for t | ArgumentNode is missing PostUpdateNode. | +| test.py:158:15:158:15 | ControlFlowNode for l | ArgumentNode is missing PostUpdateNode. | +| test.py:159:15:159:15 | ControlFlowNode for d | ArgumentNode is missing PostUpdateNode. | From eab64f125b52a9684229da37ee55a21accdaab8a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 28 Jul 2020 20:32:12 +0200 Subject: [PATCH 02/27] Python: Dataflow, start on test for classes --- .../experimental/dataflow/coverage/classes.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/coverage/classes.py diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py new file mode 100644 index 00000000000..fef83372176 --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -0,0 +1,72 @@ +# These are included so that we can easily evaluate the test code +SOURCE = "source" +def SINK(x): + print(x) + +# Callable types +# These are the types to which the function call operation (see section Calls) can be applied: + +# User-defined functions +# A user-defined function object is created by a function definition (see section Function definitions). It should be called with an argument list containing the same number of items as the function's formal parameter list. +def f(a, b): + return a + +SINK(f(SOURCE, 3)) + +# Instance methods +# An instance method object combines a class, a class instance and any callable object (normally a user-defined function). +class C(object): + + def method(self, a, cls): + assert cls is self.__class__ + return a + + @classmethod + def classmethod(cls, a): + return a + + @staticmethod + def staticmethod(): + return a + +c = C() + +# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. +func_obj = c.method.__func__ + +# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). +SINK(c.method(SOURCE, C)) +SINK(C.method(c, SOURCE, C)) +SINK(func_obj(c, SOURCE, C)) + + +# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. +c_func_obj = C.classmethod.__func__ + +# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. +SINK(c.classmethod(SOURCE)) +SINK(C.classmethod(SOURCE)) +SINK(c_func_obj(C, SOURCE)) + +# Generator functions +# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. + +# Coroutine functions +# A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section. + +# Asynchronous generator functions +# A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function. + +# Calling the asynchronous iterator’s aiterator.__anext__() method will return an awaitable which when awaited will execute until it provides a value using the yield expression. When the function executes an empty return statement or falls off the end, a StopAsyncIteration exception is raised and the asynchronous iterator will have reached the end of the set of values to be yielded. + +# Built-in functions +# A built-in function object is a wrapper around a C function. Examples of built-in functions are len() and math.sin() (math is a standard built-in module). The number and type of the arguments are determined by the C function. Special read-only attributes: __doc__ is the function’s documentation string, or None if unavailable; __name__ is the function’s name; __self__ is set to None (but see the next item); __module__ is the name of the module the function was defined in or None if unavailable. + +# Built-in methods +# This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is alist.append(), assuming alist is a list object. In this case, the special read-only attribute __self__ is set to the object denoted by alist. + +# Classes +# Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override __new__(). The arguments of the call are passed to __new__() and, in the typical case, to __init__() to initialize the new instance. + +# Class Instances +# Instances of arbitrary classes can be made callable by defining a __call__() method in their class. From 488a7f4d0142d1f4b2d48a8afb5cc422e572e8a2 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 28 Jul 2020 21:46:45 +0200 Subject: [PATCH 03/27] Python: update test expectations --- .../test/experimental/dataflow/coverage/classes.py | 4 ++-- .../experimental/dataflow/coverage/dataflow.expected | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index fef83372176..e16d1d89179 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -37,7 +37,7 @@ func_obj = c.method.__func__ # When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). SINK(c.method(SOURCE, C)) SINK(C.method(c, SOURCE, C)) -SINK(func_obj(c, SOURCE, C)) +SINK(func_obj(c, SOURCE, C)) # Path not found # When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. @@ -46,7 +46,7 @@ c_func_obj = C.classmethod.__func__ # When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. SINK(c.classmethod(SOURCE)) SINK(C.classmethod(SOURCE)) -SINK(c_func_obj(C, SOURCE)) +SINK(c_func_obj(C, SOURCE)) # Path not found # Generator functions # A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 26384d0e28c..3dd049be724 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -1,3 +1,15 @@ +| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:14:6:14:17 | ControlFlowNode for f() | +| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:38:6:38:24 | ControlFlowNode for Attribute() | +| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:39:6:39:27 | ControlFlowNode for Attribute() | +| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:40:6:40:27 | ControlFlowNode for func_obj() | +| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:47:6:47:26 | ControlFlowNode for Attribute() | +| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:48:6:48:26 | ControlFlowNode for Attribute() | +| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:49:6:49:26 | ControlFlowNode for c_func_obj() | +| classes.py:14:8:14:13 | ControlFlowNode for SOURCE | classes.py:14:6:14:17 | ControlFlowNode for f() | +| classes.py:38:15:38:20 | ControlFlowNode for SOURCE | classes.py:38:6:38:24 | ControlFlowNode for Attribute() | +| classes.py:39:18:39:23 | ControlFlowNode for SOURCE | classes.py:39:6:39:27 | ControlFlowNode for Attribute() | +| classes.py:47:20:47:25 | ControlFlowNode for SOURCE | classes.py:47:6:47:26 | ControlFlowNode for Attribute() | +| classes.py:48:20:48:25 | ControlFlowNode for SOURCE | classes.py:48:6:48:26 | ControlFlowNode for Attribute() | | test.py:20:9:20:14 | ControlFlowNode for SOURCE | test.py:21:10:21:10 | ControlFlowNode for x | | test.py:25:9:25:16 | ControlFlowNode for Str | test.py:26:10:26:10 | ControlFlowNode for x | | test.py:29:9:29:17 | ControlFlowNode for Str | test.py:30:10:30:10 | ControlFlowNode for x | From d32e2772a0bcebdd7bcaf54905574888a4706361 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 29 Jul 2020 15:52:56 +0200 Subject: [PATCH 04/27] Python: some doc, a generator, and a corotuine --- .../experimental/dataflow/coverage/classes.py | 64 +++++++++++++++++-- .../dataflow/coverage/dataflow.expected | 31 +++++---- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index e16d1d89179..d7b7b1f6b73 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1,4 +1,15 @@ -# These are included so that we can easily evaluate the test code +# User-defined methods, both instance methods and class methods, can be called in many non-standard ways +# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` function will +# be called by the syntactic constru +# This should cover all the class calls that we hope to support. It is based on https://docs.python.org/3/reference/datamodel.html. +# +# Intended sources should be the variable `SOURCE` and intended sinks should be. +# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). +# +# Functions whose name ends with "_with_local_flow" will also be tested for local flow. + + +# These are included so that we can easily evaluate the test code. SOURCE = "source" def SINK(x): print(x) @@ -17,17 +28,26 @@ SINK(f(SOURCE, 3)) # An instance method object combines a class, a class instance and any callable object (normally a user-defined function). class C(object): - def method(self, a, cls): + def method(self, x, cls): assert cls is self.__class__ - return a + return x @classmethod - def classmethod(cls, a): - return a + def classmethod(cls, x): + return x @staticmethod - def staticmethod(): - return a + def staticmethod(x): + return x + + def gen(self, x, count): + n = count + while n > 0: + yield x + n -= 1 + + async def coro(self, x): + return x c = C() @@ -50,9 +70,39 @@ SINK(c_func_obj(C, SOURCE)) # Path not found # Generator functions # A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. +def gen(x, count): + n = count + while n > 0: + yield x + n -= 1 + +iter = gen(SOURCE, 1) +SINK(iter.__next__()) # Returns SOURCE, path not found +SINK(iter.__next__()) # throws StopIteration + +oiter = c.gen(SOURCE, 1) +SINK(oiter.__next__()) +SINK(oiter.__next__()) # Coroutine functions # A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section. +async def coro(x): + return x + +import asyncio +SINK(asyncio.run(coro(SOURCE))) +SINK(asyncio.run(c.coro(SOURCE))) + +class A: + + def __await__(self, x): + yield x + +async def agen(x): + a = A() + return await a(SOURCE) + +SINK(asyncio.run(agen(SOURCE))) # fails with TypeError: 'A' object is not callable (possible query?) # Asynchronous generator functions # A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function. diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 3dd049be724..cb8822df9b3 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -1,15 +1,22 @@ -| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:14:6:14:17 | ControlFlowNode for f() | -| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:38:6:38:24 | ControlFlowNode for Attribute() | -| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:39:6:39:27 | ControlFlowNode for Attribute() | -| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:40:6:40:27 | ControlFlowNode for func_obj() | -| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:47:6:47:26 | ControlFlowNode for Attribute() | -| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:48:6:48:26 | ControlFlowNode for Attribute() | -| classes.py:2:10:2:17 | ControlFlowNode for Str | classes.py:49:6:49:26 | ControlFlowNode for c_func_obj() | -| classes.py:14:8:14:13 | ControlFlowNode for SOURCE | classes.py:14:6:14:17 | ControlFlowNode for f() | -| classes.py:38:15:38:20 | ControlFlowNode for SOURCE | classes.py:38:6:38:24 | ControlFlowNode for Attribute() | -| classes.py:39:18:39:23 | ControlFlowNode for SOURCE | classes.py:39:6:39:27 | ControlFlowNode for Attribute() | -| classes.py:47:20:47:25 | ControlFlowNode for SOURCE | classes.py:47:6:47:26 | ControlFlowNode for Attribute() | -| classes.py:48:20:48:25 | ControlFlowNode for SOURCE | classes.py:48:6:48:26 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:25:6:25:17 | ControlFlowNode for f() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:58:6:58:24 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:59:6:59:27 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:60:6:60:27 | ControlFlowNode for func_obj() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:67:6:67:26 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:68:6:68:26 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:69:6:69:26 | ControlFlowNode for c_func_obj() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:80:6:80:20 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:81:6:81:20 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:84:6:84:21 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:85:6:85:21 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:93:6:93:30 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:94:6:94:32 | ControlFlowNode for Attribute() | +| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:105:6:105:30 | ControlFlowNode for Attribute() | +| classes.py:25:8:25:13 | ControlFlowNode for SOURCE | classes.py:25:6:25:17 | ControlFlowNode for f() | +| classes.py:58:15:58:20 | ControlFlowNode for SOURCE | classes.py:58:6:58:24 | ControlFlowNode for Attribute() | +| classes.py:59:18:59:23 | ControlFlowNode for SOURCE | classes.py:59:6:59:27 | ControlFlowNode for Attribute() | +| classes.py:67:20:67:25 | ControlFlowNode for SOURCE | classes.py:67:6:67:26 | ControlFlowNode for Attribute() | +| classes.py:68:20:68:25 | ControlFlowNode for SOURCE | classes.py:68:6:68:26 | ControlFlowNode for Attribute() | | test.py:20:9:20:14 | ControlFlowNode for SOURCE | test.py:21:10:21:10 | ControlFlowNode for x | | test.py:25:9:25:16 | ControlFlowNode for Str | test.py:26:10:26:10 | ControlFlowNode for x | | test.py:29:9:29:17 | ControlFlowNode for Str | test.py:30:10:30:10 | ControlFlowNode for x | From 81ad4552c9e2567c9177079f1411587de3eb85a7 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 5 Aug 2020 13:30:30 +0200 Subject: [PATCH 05/27] Python: full list of magic methods to be tested --- .../experimental/dataflow/coverage/classes.py | 191 +++++++++++- .../dataflow/coverage/dataflow.expected | 286 +++++++++--------- 2 files changed, 325 insertions(+), 152 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 8315602c152..e543bede80c 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1,7 +1,9 @@ # User-defined methods, both instance methods and class methods, can be called in many non-standard ways -# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` function will -# be called by the syntactic constru -# This should cover all the class calls that we hope to support. It is based on https://docs.python.org/3/reference/datamodel.html. +# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` function on a +# class `C` will be called by the syntactic construct `await c` when `c` is an instance of `C`. +# +# These tests should cover all the class calls that we hope to support. +# It is based on https://docs.python.org/3/reference/datamodel.html, and headings refer there. # # Intended sources should be the variable `SOURCE` and intended sinks should be. # arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). @@ -10,10 +12,14 @@ # These are included so that we can easily evaluate the test code. +NONSOURCE = "not a source" SOURCE = "source" def SINK(x): print(x) +def SINK_F(x): + print("Unexpected flow: ", x) + # Callable types # These are the types to which the function call operation (see section Calls) can be applied: @@ -78,11 +84,11 @@ def gen(x, count): iter = gen(SOURCE, 1) SINK(iter.__next__()) -SINK(iter.__next__()) # throws StopIteration, FP +SINK_F(iter.__next__()) # throws StopIteration, FP oiter = c.gen(SOURCE, 1) SINK(oiter.__next__()) -SINK(oiter.__next__()) # throws StopIteration, FP +SINK_F(oiter.__next__()) # throws StopIteration, FP # Coroutine functions # A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section. @@ -95,14 +101,15 @@ SINK(asyncio.run(c.coro(SOURCE))) class A: - def __await__(self, x): - yield x + def __await__(self): + # yield SOURCE -- see https://groups.google.com/g/dev-python/c/_lrrc-vp9TI?pli=1 + return (yield from asyncio.coroutine(lambda: SOURCE)()) async def agen(x): a = A() - return await a(SOURCE) + return await a -SINK(asyncio.run(agen(SOURCE))) # fails with TypeError: 'A' object is not callable (possible query?) +SINK(asyncio.run(agen(SOURCE))) # Asynchronous generator functions # A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function. @@ -120,3 +127,169 @@ SINK(asyncio.run(agen(SOURCE))) # fails with TypeError: 'A' object is not callab # Class Instances # Instances of arbitrary classes can be made callable by defining a __call__() method in their class. + +class Customized: + + a = NONSOURCE + b = NONSOURCE + + def __new__(cls): + cls.a = SOURCE + return super().__new__(cls) + + def __init__(self): + self.b = SOURCE + +customized = Customized() +SINK(Customized.a) +SINK_F(Customized.b) +SINK(customized.a) +SINK(customized.b) + +# def __del__(self): + +# If a class sets __iter__() to None, calling iter() on its instances will raise a TypeError (without falling back to __getitem__()). + +# 3.3.1. Basic customization +# object.__new__(cls[, ...]) +# object.__init__(self[, ...]) +# object.__del__(self) +# object.__repr__(self) +# object.__str__(self)¶ +# object.__bytes__(self) +# object.__format__(self, format_spec) +# object.__lt__(self, other) +# object.__le__(self, other) +# object.__eq__(self, other) +# object.__ne__(self, other) +# object.__gt__(self, other) +# object.__ge__(self, other) +# object.__hash__(self) +# object.__bool__(self) +# len + +# 3.3.2. Customizing attribute access +# object.__getattr__(self, name) +# object.__getattribute__(self, name) +# object.__setattr__(self, name, value) +# object.__delattr__(self, name) +# object.__dir__(self) + +# 3.3.2.2. Implementing Descriptors +# object.__get__(self, instance, owner=None) +# object.__set__(self, instance, value) +# object.__delete__(self, instance) +# object.__set_name__(self, owner, name) + +# 3.3.2.4. __slots__ +# object.__slots__ +# __weakref__ +# __dict__ + +# 3.3.3. Customizing class creation +# classmethod object.__init_subclass__(cls) + +# 3.3.3.1. Metaclasses +# By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace). + +# 3.3.3.2. Resolving MRO entries +# __mro_entries__ + +# 3.3.3.4. Preparing the class namespace +# metaclass.__prepare__(name, bases, **kwds) + +# 3.3.4. Customizing instance and subclass checks +# class.__instancecheck__(self, instance) +# class.__subclasscheck__(self, subclass) + +# 3.3.5. Emulating generic types +# classmethod object.__class_getitem__(cls, key) + +# 3.3.6. Emulating callable objects +# object.__call__(self[, args...]) + +# 3.3.7. Emulating container types +# object.__len__(self) +# object.__length_hint__(self) +# object.__getitem__(self, key) +# object.__setitem__(self, key, value) +# object.__delitem__(self, key) +# object.__missing__(self, key) +# object.__iter__(self) +# object.__reversed__(self) +# object.__contains__(self, item) + +# 3.3.8. Emulating numeric types +# object.__add__(self, other) +# object.__sub__(self, other) +# object.__mul__(self, other) +# object.__matmul__(self, other) +# object.__truediv__(self, other) +# object.__floordiv__(self, other) +# object.__mod__(self, other) +# object.__divmod__(self, other) +# object.__pow__(self, other[, modulo]) +# object.__lshift__(self, other) +# object.__rshift__(self, other) +# object.__and__(self, other) +# object.__xor__(self, other) +# object.__or__(self, other) +# object.__radd__(self, other) +# object.__rsub__(self, other) +# object.__rmul__(self, other) +# object.__rmatmul__(self, other) +# object.__rtruediv__(self, other) +# object.__rfloordiv__(self, other) +# object.__rmod__(self, other) +# object.__rdivmod__(self, other) +# object.__rpow__(self, other[, modulo]) +# object.__rlshift__(self, other) +# object.__rrshift__(self, other) +# object.__rand__(self, other) +# object.__rxor__(self, other) +# object.__ror__(self, other) +# object.__iadd__(self, other) +# object.__isub__(self, other) +# object.__imul__(self, other) +# object.__imatmul__(self, other) +# object.__itruediv__(self, other) +# object.__ifloordiv__(self, other) +# object.__imod__(self, other) +# object.__ipow__(self, other[, modulo]) +# object.__ilshift__(self, other) +# object.__irshift__(self, other) +# object.__iand__(self, other) +# object.__ixor__(self, other) +# object.__ior__(self, other) +# object.__neg__(self) +# object.__pos__(self) +# object.__abs__(self) +# object.__invert__(self) +# object.__complex__(self) +# object.__int__(self) +# object.__float__(self) +# object.__index__(self) +# object.__round__(self[, ndigits]) +# object.__trunc__(self) +# object.__floor__(self) +# object.__ceil__(self) + +# 3.3.9. With Statement Context Managers +# object.__enter__(self) +# object.__exit__(self, exc_type, exc_value, traceback) + +# 3.4.1. Awaitable Objects +# object.__await__(self) + +# 3.4.2. Coroutine Objects +# coroutine.send(value) +# coroutine.throw(type[, value[, traceback]]) +# coroutine.close() + +# 3.4.3. Asynchronous Iterators +# object.__aiter__(self) +# object.__anext__(self) + +# 3.4.4. Asynchronous Context Managers +# object.__aenter__(self) +# object.__aexit__(self, exc_type, exc_value, traceback) diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index e1bc2ea2d07..6713969a2e4 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -1,82 +1,82 @@ edges -| classes.py:13:1:13:6 | GSSA Variable SOURCE | classes.py:25:6:25:17 | ControlFlowNode for f() | -| classes.py:13:1:13:6 | GSSA Variable SOURCE | classes.py:25:6:25:17 | GSSA Variable SOURCE | -| classes.py:13:1:13:6 | GSSA Variable SOURCE | classes.py:25:8:25:13 | ControlFlowNode for SOURCE | -| classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:13:1:13:6 | GSSA Variable SOURCE | -| classes.py:25:6:25:17 | GSSA Variable SOURCE | classes.py:52:5:52:7 | ControlFlowNode for C() | -| classes.py:25:6:25:17 | GSSA Variable SOURCE | classes.py:58:6:58:24 | ControlFlowNode for Attribute() | -| classes.py:25:6:25:17 | GSSA Variable SOURCE | classes.py:58:6:58:24 | GSSA Variable SOURCE | -| classes.py:25:6:25:17 | GSSA Variable SOURCE | classes.py:58:15:58:20 | ControlFlowNode for SOURCE | -| classes.py:25:8:25:13 | ControlFlowNode for SOURCE | classes.py:25:6:25:17 | ControlFlowNode for f() | -| classes.py:52:1:52:1 | GSSA Variable c | classes.py:58:6:58:24 | ControlFlowNode for Attribute() | -| classes.py:52:1:52:1 | GSSA Variable c | classes.py:58:6:58:24 | GSSA Variable c | -| classes.py:52:5:52:7 | ControlFlowNode for C() | classes.py:52:1:52:1 | GSSA Variable c | -| classes.py:58:6:58:24 | GSSA Variable SOURCE | classes.py:59:6:59:27 | ControlFlowNode for Attribute() | -| classes.py:58:6:58:24 | GSSA Variable SOURCE | classes.py:59:18:59:23 | ControlFlowNode for SOURCE | -| classes.py:58:6:58:24 | GSSA Variable SOURCE | classes.py:60:6:60:27 | ControlFlowNode for func_obj() | -| classes.py:58:6:58:24 | GSSA Variable SOURCE | classes.py:67:6:67:26 | ControlFlowNode for Attribute() | -| classes.py:58:6:58:24 | GSSA Variable SOURCE | classes.py:67:6:67:26 | GSSA Variable SOURCE | -| classes.py:58:6:58:24 | GSSA Variable SOURCE | classes.py:67:20:67:25 | ControlFlowNode for SOURCE | -| classes.py:58:6:58:24 | GSSA Variable c | classes.py:59:6:59:27 | ControlFlowNode for Attribute() | -| classes.py:58:6:58:24 | GSSA Variable c | classes.py:59:6:59:27 | GSSA Variable c | -| classes.py:58:15:58:20 | ControlFlowNode for SOURCE | classes.py:58:6:58:24 | ControlFlowNode for Attribute() | -| classes.py:59:6:59:27 | GSSA Variable c | classes.py:60:6:60:27 | ControlFlowNode for func_obj() | -| classes.py:59:6:59:27 | GSSA Variable c | classes.py:60:6:60:27 | GSSA Variable c | -| classes.py:59:18:59:23 | ControlFlowNode for SOURCE | classes.py:59:6:59:27 | ControlFlowNode for Attribute() | -| classes.py:60:6:60:27 | GSSA Variable c | classes.py:67:6:67:26 | ControlFlowNode for Attribute() | -| classes.py:60:6:60:27 | GSSA Variable c | classes.py:67:6:67:26 | GSSA Variable c | -| classes.py:67:6:67:26 | GSSA Variable SOURCE | classes.py:68:6:68:26 | ControlFlowNode for Attribute() | -| classes.py:67:6:67:26 | GSSA Variable SOURCE | classes.py:68:6:68:26 | GSSA Variable SOURCE | -| classes.py:67:6:67:26 | GSSA Variable SOURCE | classes.py:68:20:68:25 | ControlFlowNode for SOURCE | -| classes.py:67:6:67:26 | GSSA Variable c | classes.py:68:6:68:26 | ControlFlowNode for Attribute() | -| classes.py:67:6:67:26 | GSSA Variable c | classes.py:69:6:69:26 | ControlFlowNode for c_func_obj() | -| classes.py:67:6:67:26 | GSSA Variable c | classes.py:79:8:79:21 | ControlFlowNode for gen() | -| classes.py:67:6:67:26 | GSSA Variable c | classes.py:80:6:80:20 | ControlFlowNode for Attribute() | -| classes.py:67:6:67:26 | GSSA Variable c | classes.py:81:6:81:20 | ControlFlowNode for Attribute() | -| classes.py:67:6:67:26 | GSSA Variable c | classes.py:83:9:83:24 | ControlFlowNode for Attribute() | -| classes.py:67:6:67:26 | GSSA Variable c | classes.py:83:9:83:24 | GSSA Variable c | -| classes.py:67:20:67:25 | ControlFlowNode for SOURCE | classes.py:67:6:67:26 | ControlFlowNode for Attribute() | -| classes.py:68:6:68:26 | GSSA Variable SOURCE | classes.py:69:6:69:26 | ControlFlowNode for c_func_obj() | -| classes.py:68:6:68:26 | GSSA Variable SOURCE | classes.py:79:8:79:21 | ControlFlowNode for gen() | -| classes.py:68:6:68:26 | GSSA Variable SOURCE | classes.py:79:8:79:21 | GSSA Variable SOURCE | -| classes.py:68:20:68:25 | ControlFlowNode for SOURCE | classes.py:68:6:68:26 | ControlFlowNode for Attribute() | -| classes.py:79:1:79:4 | GSSA Variable iter | classes.py:80:6:80:20 | ControlFlowNode for Attribute() | -| classes.py:79:1:79:4 | GSSA Variable iter | classes.py:80:6:80:20 | GSSA Variable iter | -| classes.py:79:8:79:21 | ControlFlowNode for gen() | classes.py:79:1:79:4 | GSSA Variable iter | -| classes.py:79:8:79:21 | GSSA Variable SOURCE | classes.py:80:6:80:20 | ControlFlowNode for Attribute() | -| classes.py:79:8:79:21 | GSSA Variable SOURCE | classes.py:81:6:81:20 | ControlFlowNode for Attribute() | -| classes.py:79:8:79:21 | GSSA Variable SOURCE | classes.py:83:9:83:24 | ControlFlowNode for Attribute() | -| classes.py:79:8:79:21 | GSSA Variable SOURCE | classes.py:83:9:83:24 | GSSA Variable SOURCE | -| classes.py:80:6:80:20 | GSSA Variable iter | classes.py:81:6:81:20 | ControlFlowNode for Attribute() | -| classes.py:80:6:80:20 | GSSA Variable iter | classes.py:81:6:81:20 | GSSA Variable iter | -| classes.py:81:6:81:20 | GSSA Variable iter | classes.py:83:9:83:24 | ControlFlowNode for Attribute() | -| classes.py:81:6:81:20 | GSSA Variable iter | classes.py:84:6:84:21 | ControlFlowNode for Attribute() | -| classes.py:81:6:81:20 | GSSA Variable iter | classes.py:85:6:85:21 | ControlFlowNode for Attribute() | -| classes.py:81:6:81:20 | GSSA Variable iter | classes.py:93:6:93:30 | ControlFlowNode for Attribute() | -| classes.py:81:6:81:20 | GSSA Variable iter | classes.py:94:6:94:32 | ControlFlowNode for Attribute() | -| classes.py:81:6:81:20 | GSSA Variable iter | classes.py:105:6:105:30 | ControlFlowNode for Attribute() | -| classes.py:83:1:83:5 | GSSA Variable oiter | classes.py:84:6:84:21 | ControlFlowNode for Attribute() | -| classes.py:83:1:83:5 | GSSA Variable oiter | classes.py:84:6:84:21 | GSSA Variable oiter | -| classes.py:83:9:83:24 | ControlFlowNode for Attribute() | classes.py:83:1:83:5 | GSSA Variable oiter | -| classes.py:83:9:83:24 | GSSA Variable SOURCE | classes.py:84:6:84:21 | ControlFlowNode for Attribute() | -| classes.py:83:9:83:24 | GSSA Variable SOURCE | classes.py:85:6:85:21 | ControlFlowNode for Attribute() | -| classes.py:83:9:83:24 | GSSA Variable SOURCE | classes.py:93:18:93:29 | GSSA Variable SOURCE | -| classes.py:83:9:83:24 | GSSA Variable c | classes.py:84:6:84:21 | ControlFlowNode for Attribute() | -| classes.py:83:9:83:24 | GSSA Variable c | classes.py:85:6:85:21 | ControlFlowNode for Attribute() | -| classes.py:83:9:83:24 | GSSA Variable c | classes.py:93:6:93:30 | ControlFlowNode for Attribute() | -| classes.py:83:9:83:24 | GSSA Variable c | classes.py:94:18:94:31 | GSSA Variable c | -| classes.py:84:6:84:21 | GSSA Variable oiter | classes.py:85:6:85:21 | ControlFlowNode for Attribute() | -| classes.py:84:6:84:21 | GSSA Variable oiter | classes.py:85:6:85:21 | GSSA Variable oiter | -| classes.py:85:6:85:21 | GSSA Variable oiter | classes.py:93:6:93:30 | ControlFlowNode for Attribute() | -| classes.py:85:6:85:21 | GSSA Variable oiter | classes.py:94:6:94:32 | ControlFlowNode for Attribute() | -| classes.py:85:6:85:21 | GSSA Variable oiter | classes.py:105:6:105:30 | ControlFlowNode for Attribute() | -| classes.py:93:18:93:29 | GSSA Variable SOURCE | classes.py:93:6:93:30 | ControlFlowNode for Attribute() | -| classes.py:93:18:93:29 | GSSA Variable SOURCE | classes.py:94:18:94:31 | GSSA Variable SOURCE | -| classes.py:94:18:94:31 | GSSA Variable SOURCE | classes.py:94:6:94:32 | ControlFlowNode for Attribute() | -| classes.py:94:18:94:31 | GSSA Variable SOURCE | classes.py:105:18:105:29 | GSSA Variable SOURCE | -| classes.py:94:18:94:31 | GSSA Variable c | classes.py:94:6:94:32 | ControlFlowNode for Attribute() | -| classes.py:94:18:94:31 | GSSA Variable c | classes.py:105:6:105:30 | ControlFlowNode for Attribute() | -| classes.py:105:18:105:29 | GSSA Variable SOURCE | classes.py:105:6:105:30 | ControlFlowNode for Attribute() | +| classes.py:16:1:16:6 | GSSA Variable SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | +| classes.py:16:1:16:6 | GSSA Variable SOURCE | classes.py:31:6:31:17 | GSSA Variable SOURCE | +| classes.py:16:1:16:6 | GSSA Variable SOURCE | classes.py:31:8:31:13 | ControlFlowNode for SOURCE | +| classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:16:1:16:6 | GSSA Variable SOURCE | +| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:58:5:58:7 | ControlFlowNode for C() | +| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | +| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:64:6:64:24 | GSSA Variable SOURCE | +| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:64:15:64:20 | ControlFlowNode for SOURCE | +| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | +| classes.py:58:1:58:1 | GSSA Variable c | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | +| classes.py:58:1:58:1 | GSSA Variable c | classes.py:64:6:64:24 | GSSA Variable c | +| classes.py:58:5:58:7 | ControlFlowNode for C() | classes.py:58:1:58:1 | GSSA Variable c | +| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | +| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:65:18:65:23 | ControlFlowNode for SOURCE | +| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | +| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | +| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:73:6:73:26 | GSSA Variable SOURCE | +| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:73:20:73:25 | ControlFlowNode for SOURCE | +| classes.py:64:6:64:24 | GSSA Variable c | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | +| classes.py:64:6:64:24 | GSSA Variable c | classes.py:65:6:65:27 | GSSA Variable c | +| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | +| classes.py:65:6:65:27 | GSSA Variable c | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | +| classes.py:65:6:65:27 | GSSA Variable c | classes.py:66:6:66:27 | GSSA Variable c | +| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | +| classes.py:66:6:66:27 | GSSA Variable c | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | +| classes.py:66:6:66:27 | GSSA Variable c | classes.py:73:6:73:26 | GSSA Variable c | +| classes.py:73:6:73:26 | GSSA Variable SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | +| classes.py:73:6:73:26 | GSSA Variable SOURCE | classes.py:74:6:74:26 | GSSA Variable SOURCE | +| classes.py:73:6:73:26 | GSSA Variable SOURCE | classes.py:74:20:74:25 | ControlFlowNode for SOURCE | +| classes.py:73:6:73:26 | GSSA Variable c | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | +| classes.py:73:6:73:26 | GSSA Variable c | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | +| classes.py:73:6:73:26 | GSSA Variable c | classes.py:85:8:85:21 | ControlFlowNode for gen() | +| classes.py:73:6:73:26 | GSSA Variable c | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | +| classes.py:73:6:73:26 | GSSA Variable c | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | +| classes.py:73:6:73:26 | GSSA Variable c | classes.py:89:9:89:24 | ControlFlowNode for Attribute() | +| classes.py:73:6:73:26 | GSSA Variable c | classes.py:89:9:89:24 | GSSA Variable c | +| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | +| classes.py:74:6:74:26 | GSSA Variable SOURCE | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | +| classes.py:74:6:74:26 | GSSA Variable SOURCE | classes.py:85:8:85:21 | ControlFlowNode for gen() | +| classes.py:74:6:74:26 | GSSA Variable SOURCE | classes.py:85:8:85:21 | GSSA Variable SOURCE | +| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | +| classes.py:85:1:85:4 | GSSA Variable iter | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | +| classes.py:85:1:85:4 | GSSA Variable iter | classes.py:86:6:86:20 | GSSA Variable iter | +| classes.py:85:8:85:21 | ControlFlowNode for gen() | classes.py:85:1:85:4 | GSSA Variable iter | +| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | +| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | +| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:89:9:89:24 | ControlFlowNode for Attribute() | +| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:89:9:89:24 | GSSA Variable SOURCE | +| classes.py:86:6:86:20 | GSSA Variable iter | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | +| classes.py:86:6:86:20 | GSSA Variable iter | classes.py:87:8:87:22 | GSSA Variable iter | +| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:89:9:89:24 | ControlFlowNode for Attribute() | +| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | +| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | +| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | +| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | +| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | +| classes.py:89:1:89:5 | GSSA Variable oiter | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | +| classes.py:89:1:89:5 | GSSA Variable oiter | classes.py:90:6:90:21 | GSSA Variable oiter | +| classes.py:89:9:89:24 | ControlFlowNode for Attribute() | classes.py:89:1:89:5 | GSSA Variable oiter | +| classes.py:89:9:89:24 | GSSA Variable SOURCE | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | +| classes.py:89:9:89:24 | GSSA Variable SOURCE | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | +| classes.py:89:9:89:24 | GSSA Variable SOURCE | classes.py:99:18:99:29 | GSSA Variable SOURCE | +| classes.py:89:9:89:24 | GSSA Variable c | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | +| classes.py:89:9:89:24 | GSSA Variable c | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | +| classes.py:89:9:89:24 | GSSA Variable c | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | +| classes.py:89:9:89:24 | GSSA Variable c | classes.py:100:18:100:31 | GSSA Variable c | +| classes.py:90:6:90:21 | GSSA Variable oiter | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | +| classes.py:90:6:90:21 | GSSA Variable oiter | classes.py:91:8:91:23 | GSSA Variable oiter | +| classes.py:91:8:91:23 | GSSA Variable oiter | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | +| classes.py:91:8:91:23 | GSSA Variable oiter | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | +| classes.py:91:8:91:23 | GSSA Variable oiter | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | +| classes.py:99:18:99:29 | GSSA Variable SOURCE | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | +| classes.py:99:18:99:29 | GSSA Variable SOURCE | classes.py:100:18:100:31 | GSSA Variable SOURCE | +| classes.py:100:18:100:31 | GSSA Variable SOURCE | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | +| classes.py:100:18:100:31 | GSSA Variable SOURCE | classes.py:112:18:112:29 | GSSA Variable SOURCE | +| classes.py:100:18:100:31 | GSSA Variable c | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | +| classes.py:100:18:100:31 | GSSA Variable c | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | +| classes.py:112:18:112:29 | GSSA Variable SOURCE | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | | test.py:35:9:35:14 | ControlFlowNode for SOURCE | test.py:36:10:36:10 | ControlFlowNode for x | | test.py:40:9:40:16 | ControlFlowNode for Str | test.py:41:10:41:10 | ControlFlowNode for x | | test.py:44:9:44:17 | ControlFlowNode for Str | test.py:45:10:45:10 | ControlFlowNode for x | @@ -87,52 +87,52 @@ edges | test.py:297:12:297:17 | ControlFlowNode for SOURCE | test.py:297:10:297:18 | ControlFlowNode for f() | | test.py:301:28:301:33 | ControlFlowNode for SOURCE | test.py:301:10:301:34 | ControlFlowNode for second() | nodes -| classes.py:13:1:13:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:13:10:13:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| classes.py:25:6:25:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| classes.py:25:6:25:17 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:25:8:25:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:52:1:52:1 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:52:5:52:7 | ControlFlowNode for C() | semmle.label | ControlFlowNode for C() | -| classes.py:58:6:58:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:58:6:58:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:58:6:58:24 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:58:15:58:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:59:6:59:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:59:6:59:27 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:59:18:59:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:60:6:60:27 | ControlFlowNode for func_obj() | semmle.label | ControlFlowNode for func_obj() | -| classes.py:60:6:60:27 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:67:6:67:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:67:6:67:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:67:6:67:26 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:67:20:67:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:68:6:68:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:68:6:68:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:68:20:68:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:69:6:69:26 | ControlFlowNode for c_func_obj() | semmle.label | ControlFlowNode for c_func_obj() | -| classes.py:79:1:79:4 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:79:8:79:21 | ControlFlowNode for gen() | semmle.label | ControlFlowNode for gen() | -| classes.py:79:8:79:21 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:80:6:80:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:80:6:80:20 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:81:6:81:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:81:6:81:20 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:83:1:83:5 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:83:9:83:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:83:9:83:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:83:9:83:24 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:84:6:84:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:84:6:84:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:85:6:85:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:85:6:85:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:93:6:93:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:93:18:93:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:94:6:94:32 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:94:18:94:31 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:94:18:94:31 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:105:6:105:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:105:18:105:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:16:1:16:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:16:10:16:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| classes.py:31:6:31:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | +| classes.py:31:6:31:17 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:58:1:58:1 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:58:5:58:7 | ControlFlowNode for C() | semmle.label | ControlFlowNode for C() | +| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:64:6:64:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:64:6:64:24 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:65:6:65:27 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | semmle.label | ControlFlowNode for func_obj() | +| classes.py:66:6:66:27 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:73:6:73:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:73:6:73:26 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:74:6:74:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | semmle.label | ControlFlowNode for c_func_obj() | +| classes.py:85:1:85:4 | GSSA Variable iter | semmle.label | GSSA Variable iter | +| classes.py:85:8:85:21 | ControlFlowNode for gen() | semmle.label | ControlFlowNode for gen() | +| classes.py:85:8:85:21 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:86:6:86:20 | GSSA Variable iter | semmle.label | GSSA Variable iter | +| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:87:8:87:22 | GSSA Variable iter | semmle.label | GSSA Variable iter | +| classes.py:89:1:89:5 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | +| classes.py:89:9:89:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:89:9:89:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:89:9:89:24 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:90:6:90:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | +| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:91:8:91:23 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | +| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:99:18:99:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:100:18:100:31 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:100:18:100:31 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:112:18:112:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | | test.py:35:9:35:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:36:10:36:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:40:9:40:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | @@ -152,25 +152,25 @@ nodes | test.py:301:10:301:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:301:28:301:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select -| classes.py:25:6:25:17 | ControlFlowNode for f() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:25:6:25:17 | ControlFlowNode for f() | | -| classes.py:25:6:25:17 | ControlFlowNode for f() | classes.py:25:8:25:13 | ControlFlowNode for SOURCE | classes.py:25:6:25:17 | ControlFlowNode for f() | | -| classes.py:58:6:58:24 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:58:6:58:24 | ControlFlowNode for Attribute() | | -| classes.py:58:6:58:24 | ControlFlowNode for Attribute() | classes.py:58:15:58:20 | ControlFlowNode for SOURCE | classes.py:58:6:58:24 | ControlFlowNode for Attribute() | | -| classes.py:59:6:59:27 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:59:6:59:27 | ControlFlowNode for Attribute() | | -| classes.py:59:6:59:27 | ControlFlowNode for Attribute() | classes.py:59:18:59:23 | ControlFlowNode for SOURCE | classes.py:59:6:59:27 | ControlFlowNode for Attribute() | | -| classes.py:60:6:60:27 | ControlFlowNode for func_obj() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:60:6:60:27 | ControlFlowNode for func_obj() | | -| classes.py:67:6:67:26 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:67:6:67:26 | ControlFlowNode for Attribute() | | -| classes.py:67:6:67:26 | ControlFlowNode for Attribute() | classes.py:67:20:67:25 | ControlFlowNode for SOURCE | classes.py:67:6:67:26 | ControlFlowNode for Attribute() | | -| classes.py:68:6:68:26 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:68:6:68:26 | ControlFlowNode for Attribute() | | -| classes.py:68:6:68:26 | ControlFlowNode for Attribute() | classes.py:68:20:68:25 | ControlFlowNode for SOURCE | classes.py:68:6:68:26 | ControlFlowNode for Attribute() | | -| classes.py:69:6:69:26 | ControlFlowNode for c_func_obj() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:69:6:69:26 | ControlFlowNode for c_func_obj() | | -| classes.py:80:6:80:20 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:80:6:80:20 | ControlFlowNode for Attribute() | | -| classes.py:81:6:81:20 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:81:6:81:20 | ControlFlowNode for Attribute() | | -| classes.py:84:6:84:21 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:84:6:84:21 | ControlFlowNode for Attribute() | | -| classes.py:85:6:85:21 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:85:6:85:21 | ControlFlowNode for Attribute() | | -| classes.py:93:6:93:30 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:93:6:93:30 | ControlFlowNode for Attribute() | | -| classes.py:94:6:94:32 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:94:6:94:32 | ControlFlowNode for Attribute() | | -| classes.py:105:6:105:30 | ControlFlowNode for Attribute() | classes.py:13:10:13:17 | ControlFlowNode for Str | classes.py:105:6:105:30 | ControlFlowNode for Attribute() | | +| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:31:6:31:17 | ControlFlowNode for f() | | +| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | | +| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | | +| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | | +| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | | +| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | | +| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | | +| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | | +| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | | +| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | | +| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | | +| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | | +| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | | +| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | | +| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | | +| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | | +| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | | +| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | | +| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | | | test.py:36:10:36:10 | ControlFlowNode for x | test.py:35:9:35:14 | ControlFlowNode for SOURCE | test.py:36:10:36:10 | ControlFlowNode for x | | | test.py:41:10:41:10 | ControlFlowNode for x | test.py:40:9:40:16 | ControlFlowNode for Str | test.py:41:10:41:10 | ControlFlowNode for x | | | test.py:45:10:45:10 | ControlFlowNode for x | test.py:44:9:44:17 | ControlFlowNode for Str | test.py:45:10:45:10 | ControlFlowNode for x | | From a89624698d3fccffa34b4cab8c819f7ad0bc0764 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 5 Aug 2020 14:28:28 +0200 Subject: [PATCH 06/27] Python: format ql --- .../src/experimental/dataflow/internal/DataFlowPrivate.qll | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 4221c90cf74..1f46fd341e9 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -150,12 +150,13 @@ class DataFlowClassValue extends DataFlowCallable, TClassValue { override Scope getScope() { result = c.getScope() } - override NameNode getParameter(int n) { result.getNode() = c.getScope().getInitMethod().getArg(n+1).asName() } + override NameNode getParameter(int n) { + result.getNode() = c.getScope().getInitMethod().getArg(n + 1).asName() + } override string getName() { result = c.getName() } } - /** Represents a call to a callable */ class DataFlowCall extends CallNode { DataFlowCallable callable; From e642808a7576ec49226932bce70d55f84e53b9a5 Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 5 Aug 2020 15:12:27 +0200 Subject: [PATCH 07/27] Update python/ql/test/experimental/dataflow/coverage/classes.py Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- python/ql/test/experimental/dataflow/coverage/classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index e543bede80c..d94a5922199 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -5,7 +5,7 @@ # These tests should cover all the class calls that we hope to support. # It is based on https://docs.python.org/3/reference/datamodel.html, and headings refer there. # -# Intended sources should be the variable `SOURCE` and intended sinks should be. +# Intended sources should be the variable `SOURCE` and intended sinks should be # arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). # # Functions whose name ends with "_with_local_flow" will also be tested for local flow. From 614103c3b6bcea797cdeaeda2733862fa5cad0ec Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 6 Aug 2020 15:40:41 +0200 Subject: [PATCH 08/27] Python: Test calls rather than flows --- .../experimental/dataflow/basic/callGraph.ql | 2 +- .../dataflow/{basic => }/callGraphConfig.qll | 0 .../experimental/dataflow/coverage/classes.py | 12 +-- .../coverage/classesCallGraph.expected | 81 +++++++++++++++++++ .../dataflow/coverage/classesCallGraph.ql | 10 +++ 5 files changed, 98 insertions(+), 7 deletions(-) rename python/ql/test/experimental/dataflow/{basic => }/callGraphConfig.qll (100%) create mode 100644 python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected create mode 100644 python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql diff --git a/python/ql/test/experimental/dataflow/basic/callGraph.ql b/python/ql/test/experimental/dataflow/basic/callGraph.ql index 53747b31739..2e8d6956c70 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraph.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraph.ql @@ -1,4 +1,4 @@ -import callGraphConfig +import experimental.dataflow.callGraphConfig from DataFlow::Node source, DataFlow::Node sink where exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) diff --git a/python/ql/test/experimental/dataflow/basic/callGraphConfig.qll b/python/ql/test/experimental/dataflow/callGraphConfig.qll similarity index 100% rename from python/ql/test/experimental/dataflow/basic/callGraphConfig.qll rename to python/ql/test/experimental/dataflow/callGraphConfig.qll diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index d94a5922199..424ff1dcdb4 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -128,6 +128,10 @@ SINK(asyncio.run(agen(SOURCE))) # Class Instances # Instances of arbitrary classes can be made callable by defining a __call__() method in their class. +# If a class sets __iter__() to None, calling iter() on its instances will raise a TypeError (without falling back to __getitem__()). + +# 3.3.1. Basic customization + class Customized: a = NONSOURCE @@ -140,19 +144,15 @@ class Customized: def __init__(self): self.b = SOURCE +# object.__new__(cls[, ...]) +# object.__init__(self[, ...]) customized = Customized() SINK(Customized.a) SINK_F(Customized.b) SINK(customized.a) SINK(customized.b) -# def __del__(self): -# If a class sets __iter__() to None, calling iter() on its instances will raise a TypeError (without falling back to __getitem__()). - -# 3.3.1. Basic customization -# object.__new__(cls[, ...]) -# object.__init__(self[, ...]) # object.__del__(self) # object.__repr__(self) # object.__str__(self)¶ diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected new file mode 100644 index 00000000000..83773c878c7 --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -0,0 +1,81 @@ +| classes.py:29:10:29:10 | ControlFlowNode for a | classes.py:17:10:17:10 | SSA variable x | +| classes.py:29:10:29:10 | ControlFlowNode for a | classes.py:31:6:31:17 | ControlFlowNode for f() | +| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:31:6:31:17 | ControlFlowNode for f() | +| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | +| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:28:7:28:7 | SSA variable a | +| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | +| classes.py:31:16:31:16 | ControlFlowNode for IntegerLiteral | classes.py:28:10:28:10 | SSA variable b | +| classes.py:39:16:39:16 | ControlFlowNode for x | classes.py:17:10:17:10 | SSA variable x | +| classes.py:39:16:39:16 | ControlFlowNode for x | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | +| classes.py:39:16:39:16 | ControlFlowNode for x | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | +| classes.py:43:16:43:16 | ControlFlowNode for x | classes.py:17:10:17:10 | SSA variable x | +| classes.py:43:16:43:16 | ControlFlowNode for x | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | +| classes.py:43:16:43:16 | ControlFlowNode for x | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | +| classes.py:56:14:56:14 | ControlFlowNode for x | classes.py:100:18:100:31 | ControlFlowNode for Attribute() | +| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | +| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | +| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:37:16:37:19 | SSA variable self | +| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:37:22:37:22 | SSA variable x | +| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | +| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:17:10:17:10 | SSA variable x | +| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:37:22:37:22 | SSA variable x | +| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:37:25:37:27 | SSA variable cls | +| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | +| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | +| classes.py:65:15:65:15 | ControlFlowNode for c | classes.py:37:16:37:19 | SSA variable self | +| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | +| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:37:22:37:22 | SSA variable x | +| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | +| classes.py:65:26:65:26 | ControlFlowNode for C | classes.py:37:25:37:27 | SSA variable cls | +| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | +| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | +| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | +| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:42:21:42:23 | SSA variable cls | +| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:42:26:42:26 | SSA variable x | +| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | +| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | +| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | +| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:42:21:42:23 | SSA variable cls | +| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:42:26:42:26 | SSA variable x | +| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | +| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | +| classes.py:85:12:85:17 | ControlFlowNode for SOURCE | classes.py:79:9:79:9 | SSA variable x | +| classes.py:85:20:85:20 | ControlFlowNode for IntegerLiteral | classes.py:79:12:79:16 | SSA variable count | +| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | +| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | classes.py:20:12:20:12 | SSA variable x | +| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | +| classes.py:89:15:89:20 | ControlFlowNode for SOURCE | classes.py:49:13:49:16 | SSA variable self | +| classes.py:89:15:89:20 | ControlFlowNode for SOURCE | classes.py:49:19:49:19 | SSA variable x | +| classes.py:89:23:89:23 | ControlFlowNode for IntegerLiteral | classes.py:49:19:49:19 | SSA variable x | +| classes.py:89:23:89:23 | ControlFlowNode for IntegerLiteral | classes.py:49:22:49:26 | SSA variable count | +| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | +| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | classes.py:20:12:20:12 | SSA variable x | +| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | +| classes.py:96:10:96:10 | ControlFlowNode for x | classes.py:99:18:99:29 | ControlFlowNode for coro() | +| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | +| classes.py:99:23:99:28 | ControlFlowNode for SOURCE | classes.py:95:16:95:16 | SSA variable x | +| classes.py:99:23:99:28 | ControlFlowNode for SOURCE | classes.py:99:18:99:29 | ControlFlowNode for coro() | +| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | +| classes.py:100:25:100:30 | ControlFlowNode for SOURCE | classes.py:55:20:55:23 | SSA variable self | +| classes.py:100:25:100:30 | ControlFlowNode for SOURCE | classes.py:55:26:55:26 | SSA variable x | +| classes.py:100:25:100:30 | ControlFlowNode for SOURCE | classes.py:100:18:100:31 | ControlFlowNode for Attribute() | +| classes.py:110:10:110:16 | ControlFlowNode for Await | classes.py:112:18:112:29 | ControlFlowNode for agen() | +| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | +| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | +| classes.py:112:23:112:28 | ControlFlowNode for SOURCE | classes.py:108:16:108:16 | SSA variable x | +| classes.py:142:12:142:31 | ControlFlowNode for Attribute() | classes.py:142:12:142:31 | ControlFlowNode for Attribute() | +| classes.py:150:6:150:17 | ControlFlowNode for Attribute | classes.py:17:10:17:10 | SSA variable x | +| classes.py:151:8:151:19 | ControlFlowNode for Attribute | classes.py:20:12:20:12 | SSA variable x | +| classes.py:152:6:152:17 | ControlFlowNode for Attribute | classes.py:17:10:17:10 | SSA variable x | +| classes.py:153:6:153:17 | ControlFlowNode for Attribute | classes.py:17:10:17:10 | SSA variable x | diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql new file mode 100644 index 00000000000..240085be57f --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql @@ -0,0 +1,10 @@ +import experimental.dataflow.callGraphConfig + +from DataFlow::Node source, DataFlow::Node sink +where + source.getLocation().getFile().getBaseName() = "classes.py" and + sink.getLocation().getFile().getBaseName() = "classes.py" and + exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) +select source, sink + +// Rewrite this to just have 1-step paths? From 3db1ceeb70cbe8dc04dfa90ecd222d9f16f8df73 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 6 Aug 2020 15:42:14 +0200 Subject: [PATCH 09/27] Python: format ql --- .../ql/test/experimental/dataflow/coverage/classesCallGraph.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql index 240085be57f..0416cc7f6ea 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql @@ -6,5 +6,4 @@ where sink.getLocation().getFile().getBaseName() = "classes.py" and exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) select source, sink - // Rewrite this to just have 1-step paths? From d84294df3d0253d588736868c4d18b316c4b7711 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 7 Aug 2020 20:07:02 +0200 Subject: [PATCH 10/27] Python: Check that tests are valid --- .../experimental/dataflow/coverage/classes.py | 1145 +++++++++++++++-- .../experimental/dataflow/coverage/test.py | 20 +- .../dataflow/coverage/validTest.py | 33 + 3 files changed, 1105 insertions(+), 93 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/coverage/validTest.py diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 424ff1dcdb4..3cbe6e97791 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -9,16 +9,28 @@ # arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). # # Functions whose name ends with "_with_local_flow" will also be tested for local flow. +# +# All functions starting with "test_" should run and print `"OK"`. +# This can be checked by running validTest.py. - -# These are included so that we can easily evaluate the test code. +# These are defined so that we can evaluate the test code. NONSOURCE = "not a source" SOURCE = "source" + +def is_source(x): + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + def SINK(x): - print(x) + if is_source(x): + print("OK") + else: + print("Unexpected flow", x) def SINK_F(x): - print("Unexpected flow: ", x) + if is_source(x): + print("Unexpected flow", x) + else: + print("OK") # Callable types # These are the types to which the function call operation (see section Calls) can be applied: @@ -84,11 +96,11 @@ def gen(x, count): iter = gen(SOURCE, 1) SINK(iter.__next__()) -SINK_F(iter.__next__()) # throws StopIteration, FP +# SINK_F(iter.__next__()) # throws StopIteration, FP oiter = c.gen(SOURCE, 1) SINK(oiter.__next__()) -SINK_F(oiter.__next__()) # throws StopIteration, FP +# SINK_F(oiter.__next__()) # throws StopIteration, FP # Coroutine functions # A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section. @@ -144,42 +156,305 @@ class Customized: def __init__(self): self.b = SOURCE -# object.__new__(cls[, ...]) -# object.__init__(self[, ...]) +# testing __new__ and __init__ customized = Customized() SINK(Customized.a) SINK_F(Customized.b) SINK(customized.a) SINK(customized.b) +def OK(): + print("OK") + +# object.__new__(cls[, ...]) +class With_new: + + def __new__(cls): + OK() + return super().__new__(cls) + +def test_new(): + with_new = With_new() + +# object.__init__(self[, ...]) +class With_init: + + def __init__(self): + OK() + +def test_init(): + with_init = With_init() # object.__del__(self) +class With_del: + + def __del__(self): + OK() + +def test_del(): + with_del = With_del() + del with_del + # object.__repr__(self) -# object.__str__(self)¶ +class With_repr: + + def __repr__(self): + OK() + return "With_repr()" + +def test_repr(): + with_repr = With_repr() + repr(with_repr) + +# object.__str__(self) +class With_str: + + def __str__(self): + OK() + return "Awesome" + +def test_str(): + with_str = With_str() + str(with_str) + # object.__bytes__(self) +class With_bytes: + + def __bytes__(self): + OK() + return b"Awesome" + +def test_bytes(): + with_bytes = With_bytes() + bytes(with_bytes) + # object.__format__(self, format_spec) +class With_format: + + def __format__(self, format_spec): + OK() + return "Awesome" + +def test_format(): + with_format = With_format() + format(with_format) + +def test_format_str(): + with_format = With_format() + "{0}".format(with_format) + +def test_format_fstr(): + with_format = With_format() + f"{with_format}" + # object.__lt__(self, other) +class With_lt: + + def __lt__(self, other): + OK() + return "" + +def test_lt(): + with_lt = With_lt() + with_lt < with_lt + # object.__le__(self, other) +class With_le: + + def __le__(self, other): + OK() + return "" + +def test_le(): + with_le = With_le() + with_le <= with_le + # object.__eq__(self, other) +class With_eq: + + def __eq__(self, other): + OK() + return "" + +def test_eq(): + with_eq = With_eq() + with_eq == with_eq + # object.__ne__(self, other) +class With_ne: + + def __ne__(self, other): + OK() + return "" + +def test_ne(): + with_ne = With_ne() + with_ne != with_ne + # object.__gt__(self, other) +class With_gt: + + def __gt__(self, other): + OK() + return "" + +def test_gt(): + with_gt = With_gt() + with_gt > with_gt + # object.__ge__(self, other) +class With_ge: + + def __ge__(self, other): + OK() + return "" + +def test_ge(): + with_ge = With_ge() + with_ge >= with_ge + # object.__hash__(self) +class With_hash: + + def __hash__(self): + OK() + return 0 + +def test_hash(): + with_hash = With_hash() + hash(with_hash) + +def test_hash_set(): + with_hash = With_hash() + len(set([with_hash])) + +def test_hash_frozenset(): + with_hash = With_hash() + len(frozenset([with_hash])) + +def test_hash_dict(): + with_hash = With_hash() + len(dict({with_hash: 0})) + # object.__bool__(self) -# len +class With_bool: + + def __bool__(self): + OK() + return True + +def test_bool(): + with_bool = With_bool() + bool(with_bool) + +def test_bool_if(): + with_bool = With_bool() + if with_bool: + pass # 3.3.2. Customizing attribute access # object.__getattr__(self, name) +class With_getattr: + + def __getattr__(self, name): + OK() + return "" + +def test_getattr(): + with_getattr = With_getattr() + with_getattr.foo + # object.__getattribute__(self, name) +class With_getattribute: + + def __getattribute__(self, name): + OK() + return "" + +def test_getattribute(): + with_getattribute = With_getattribute() + with_getattribute.foo + # object.__setattr__(self, name, value) +class With_setattr: + + def __setattr__(self, name, value): + OK() + +def test_setattr(): + with_setattr = With_setattr() + with_setattr.foo = "" + # object.__delattr__(self, name) +class With_delattr: + + def __delattr__(self, name): + OK() + +def test_delattr(): + with_delattr = With_delattr() + del with_delattr.foo + # object.__dir__(self) +class With_dir: + + def __dir__(self): + OK() + return [] + +def test_dir(): + with_dir = With_dir() + dir(with_dir) + # 3.3.2.2. Implementing Descriptors +class Owner: + pass + # object.__get__(self, instance, owner=None) +class With_get: + + def __get__(self, instance, owner=None): + OK() + return "" + +def ftest_get(): + with_get = With_get() + Owner.foo = "" + Owner.attr = with_get + Owner.foo + # object.__set__(self, instance, value) +class With_set: + + def __set__(self, instance, value): + OK() + +def ftest_set(): + with_set = With_set() + with_set.foo = "" + # object.__delete__(self, instance) +class With_delete: + + def __delete__(self, instance): + OK() + +def ftest_delete(): + with_delete = With_delete() + with_delete.foo = "" + del with_delete.foo + # object.__set_name__(self, owner, name) +class With_set_name: + + def __set_name__(self, instance): + OK() + +def ftest_set_name(): + with_set_name = With_set_name() + with_set_name.foo = "" + del with_set_name.foo # 3.3.2.4. __slots__ # object.__slots__ @@ -207,89 +482,785 @@ SINK(customized.b) # 3.3.6. Emulating callable objects # object.__call__(self[, args...]) +class With_call: + + def __call__(self): + OK() + +def test_call(): + with_call = With_call() + with_call() # 3.3.7. Emulating container types # object.__len__(self) -# object.__length_hint__(self) -# object.__getitem__(self, key) -# object.__setitem__(self, key, value) -# object.__delitem__(self, key) -# object.__missing__(self, key) -# object.__iter__(self) -# object.__reversed__(self) -# object.__contains__(self, item) +class With_len: -# 3.3.8. Emulating numeric types -# object.__add__(self, other) -# object.__sub__(self, other) -# object.__mul__(self, other) -# object.__matmul__(self, other) -# object.__truediv__(self, other) -# object.__floordiv__(self, other) -# object.__mod__(self, other) -# object.__divmod__(self, other) -# object.__pow__(self, other[, modulo]) -# object.__lshift__(self, other) -# object.__rshift__(self, other) -# object.__and__(self, other) -# object.__xor__(self, other) -# object.__or__(self, other) -# object.__radd__(self, other) -# object.__rsub__(self, other) -# object.__rmul__(self, other) -# object.__rmatmul__(self, other) -# object.__rtruediv__(self, other) -# object.__rfloordiv__(self, other) -# object.__rmod__(self, other) -# object.__rdivmod__(self, other) -# object.__rpow__(self, other[, modulo]) -# object.__rlshift__(self, other) -# object.__rrshift__(self, other) -# object.__rand__(self, other) -# object.__rxor__(self, other) -# object.__ror__(self, other) -# object.__iadd__(self, other) -# object.__isub__(self, other) -# object.__imul__(self, other) -# object.__imatmul__(self, other) -# object.__itruediv__(self, other) -# object.__ifloordiv__(self, other) -# object.__imod__(self, other) -# object.__ipow__(self, other[, modulo]) -# object.__ilshift__(self, other) -# object.__irshift__(self, other) -# object.__iand__(self, other) -# object.__ixor__(self, other) -# object.__ior__(self, other) -# object.__neg__(self) -# object.__pos__(self) -# object.__abs__(self) -# object.__invert__(self) -# object.__complex__(self) -# object.__int__(self) -# object.__float__(self) -# object.__index__(self) -# object.__round__(self[, ndigits]) -# object.__trunc__(self) -# object.__floor__(self) -# object.__ceil__(self) + def __len__(self): + OK() + return 0 -# 3.3.9. With Statement Context Managers -# object.__enter__(self) -# object.__exit__(self, exc_type, exc_value, traceback) +def test_len(): + with_len = With_len() + len(with_len) -# 3.4.1. Awaitable Objects -# object.__await__(self) +def test_len_bool(): + with_len = With_len() + bool(with_len) -# 3.4.2. Coroutine Objects -# coroutine.send(value) -# coroutine.throw(type[, value[, traceback]]) -# coroutine.close() +def test_len_if(): + with_len = With_len() + if with_len: + pass -# 3.4.3. Asynchronous Iterators -# object.__aiter__(self) -# object.__anext__(self) +# # object.__length_hint__(self) +# # object.__getitem__(self, key) +# class With_getitem: -# 3.4.4. Asynchronous Context Managers -# object.__aenter__(self) -# object.__aexit__(self, exc_type, exc_value, traceback) +# def __getitem__(self, key): +# OK() +# return "" # edit to match type + +# def test_getitem(): +# with_getitem = With_getitem() +# getitem(with_getitem) # edit to effect call + +# # object.__setitem__(self, key, value) +# class With_setitem: + +# def __setitem__(self, key, value): +# OK() +# return "" # edit to match type + +# def test_setitem(): +# with_setitem = With_setitem() +# setitem(with_setitem) # edit to effect call + +# # object.__delitem__(self, key) +# class With_delitem: + +# def __delitem__(self, key): +# OK() +# return "" # edit to match type + +# def test_delitem(): +# with_delitem = With_delitem() +# delitem(with_delitem) # edit to effect call + +# # object.__missing__(self, key) +# class With_missing: + +# def __missing__(self, key): +# OK() +# return "" # edit to match type + +# def test_missing(): +# with_missing = With_missing() +# missing(with_missing) # edit to effect call + +# # object.__iter__(self) +# class With_iter: + +# def __iter__(self): +# OK() +# return "" # edit to match type + +# def test_iter(): +# with_iter = With_iter() +# iter(with_iter) # edit to effect call + +# # object.__reversed__(self) +# class With_reversed: + +# def __reversed__(self): +# OK() +# return "" # edit to match type + +# def test_reversed(): +# with_reversed = With_reversed() +# reversed(with_reversed) # edit to effect call + +# # object.__contains__(self, item) +# class With_contains: + +# def __contains__(self, item): +# OK() +# return "" # edit to match type + +# def test_contains(): +# with_contains = With_contains() +# contains(with_contains) # edit to effect call + + +# # 3.3.8. Emulating numeric types +# # object.__add__(self, other) +# class With_add: + +# def __add__(self, other): +# OK() +# return self + +# def test_add(): +# with_add = With_add() +# with_add + with_add + +# # object.__sub__(self, other) +# class With_sub: + +# def __sub__(self, other): +# OK() +# return "" # edit to match type + +# def test_sub(): +# with_sub = With_sub() +# sub(with_sub) # edit to effect call + +# # object.__mul__(self, other) +# class With_mul: + +# def __mul__(self, other): +# OK() +# return "" # edit to match type + +# def test_mul(): +# with_mul = With_mul() +# mul(with_mul) # edit to effect call + +# # object.__matmul__(self, other) +# class With_matmul: + +# def __matmul__(self, other): +# OK() +# return "" # edit to match type + +# def test_matmul(): +# with_matmul = With_matmul() +# matmul(with_matmul) # edit to effect call + +# # object.__truediv__(self, other) +# class With_truediv: + +# def __truediv__(self, other): +# OK() +# return "" # edit to match type + +# def test_truediv(): +# with_truediv = With_truediv() +# truediv(with_truediv) # edit to effect call + +# # object.__floordiv__(self, other) +# class With_floordiv: + +# def __floordiv__(self, other): +# OK() +# return "" # edit to match type + +# def test_floordiv(): +# with_floordiv = With_floordiv() +# floordiv(with_floordiv) # edit to effect call + +# # object.__mod__(self, other) +# class With_mod: + +# def __mod__(self, other): +# OK() +# return "" # edit to match type + +# def test_mod(): +# with_mod = With_mod() +# mod(with_mod) # edit to effect call + +# # object.__divmod__(self, other) +# class With_divmod: + +# def __divmod__(self, other): +# OK() +# return "" # edit to match type + +# def test_divmod(): +# with_divmod = With_divmod() +# divmod(with_divmod) # edit to effect call + +# # object.__pow__(self, other[, modulo]) +# class With_pow: + +# def __pow__(self, other): +# OK() +# return "" # edit to match type + +# def test_pow(): +# with_pow = With_pow() +# pow(with_pow) # edit to effect call + +# # object.__lshift__(self, other) +# class With_lshift: + +# def __lshift__(self, other): +# OK() +# return "" # edit to match type + +# def test_lshift(): +# with_lshift = With_lshift() +# lshift(with_lshift) # edit to effect call + +# # object.__rshift__(self, other) +# class With_rshift: + +# def __rshift__(self, other): +# OK() +# return "" # edit to match type + +# def test_rshift(): +# with_rshift = With_rshift() +# rshift(with_rshift) # edit to effect call + +# # object.__and__(self, other) +# class With_and: + +# def __and__(self, other): +# OK() +# return "" # edit to match type + +# def test_and(): +# with_and = With_and() +# with_and & with_and + +# # object.__xor__(self, other) +# class With_xor: + +# def __xor__(self, other): +# OK() +# return "" # edit to match type + +# def test_xor(): +# with_xor = With_xor() +# xor(with_xor) # edit to effect call + +# # object.__or__(self, other) +# class With_or: + +# def __or__(self, other): +# OK() +# return "" # edit to match type + +# def test_or(): +# with_or = With_or() +# with_or | with_or + +# # object.__radd__(self, other) +# class With_radd: + +# def __radd__(self, other): +# OK() +# return "" # edit to match type + +# def test_radd(): +# with_radd = With_radd() +# radd(with_radd) # edit to effect call + +# # object.__rsub__(self, other) +# class With_rsub: + +# def __rsub__(self, other): +# OK() +# return "" # edit to match type + +# def test_rsub(): +# with_rsub = With_rsub() +# rsub(with_rsub) # edit to effect call + +# # object.__rmul__(self, other) +# class With_rmul: + +# def __rmul__(self, other): +# OK() +# return "" # edit to match type + +# def test_rmul(): +# with_rmul = With_rmul() +# rmul(with_rmul) # edit to effect call + +# # object.__rmatmul__(self, other) +# class With_rmatmul: + +# def __rmatmul__(self, other): +# OK() +# return "" # edit to match type + +# def test_rmatmul(): +# with_rmatmul = With_rmatmul() +# rmatmul(with_rmatmul) # edit to effect call + +# # object.__rtruediv__(self, other) +# class With_rtruediv: + +# def __rtruediv__(self, other): +# OK() +# return "" # edit to match type + +# def test_rtruediv(): +# with_rtruediv = With_rtruediv() +# rtruediv(with_rtruediv) # edit to effect call + +# # object.__rfloordiv__(self, other) +# class With_rfloordiv: + +# def __rfloordiv__(self, other): +# OK() +# return "" # edit to match type + +# def test_rfloordiv(): +# with_rfloordiv = With_rfloordiv() +# rfloordiv(with_rfloordiv) # edit to effect call + +# # object.__rmod__(self, other) +# class With_rmod: + +# def __rmod__(self, other): +# OK() +# return "" # edit to match type + +# def test_rmod(): +# with_rmod = With_rmod() +# rmod(with_rmod) # edit to effect call + +# # object.__rdivmod__(self, other) +# class With_rdivmod: + +# def __rdivmod__(self, other): +# OK() +# return "" # edit to match type + +# def test_rdivmod(): +# with_rdivmod = With_rdivmod() +# rdivmod(with_rdivmod) # edit to effect call + +# # object.__rpow__(self, other[, modulo]) +# class With_rpow: + +# def __rpow__(self, other): +# OK() +# return "" # edit to match type + +# def test_rpow(): +# with_rpow = With_rpow() +# rpow(with_rpow) # edit to effect call + +# # object.__rlshift__(self, other) +# class With_rlshift: + +# def __rlshift__(self, other): +# OK() +# return "" # edit to match type + +# def test_rlshift(): +# with_rlshift = With_rlshift() +# rlshift(with_rlshift) # edit to effect call + +# # object.__rrshift__(self, other) +# class With_rrshift: + +# def __rrshift__(self, other): +# OK() +# return "" # edit to match type + +# def test_rrshift(): +# with_rrshift = With_rrshift() +# rrshift(with_rrshift) # edit to effect call + +# # object.__rand__(self, other) +# class With_rand: + +# def __rand__(self, other): +# OK() +# return "" # edit to match type + +# def test_rand(): +# with_rand = With_rand() +# rand(with_rand) # edit to effect call + +# # object.__rxor__(self, other) +# class With_rxor: + +# def __rxor__(self, other): +# OK() +# return "" # edit to match type + +# def test_rxor(): +# with_rxor = With_rxor() +# rxor(with_rxor) # edit to effect call + +# # object.__ror__(self, other) +# class With_ror: + +# def __ror__(self, other): +# OK() +# return "" # edit to match type + +# def test_ror(): +# with_ror = With_ror() +# ror(with_ror) # edit to effect call + +# # object.__iadd__(self, other) +# class With_iadd: + +# def __iadd__(self, other): +# OK() +# return "" # edit to match type + +# def test_iadd(): +# with_iadd = With_iadd() +# iadd(with_iadd) # edit to effect call + +# # object.__isub__(self, other) +# class With_isub: + +# def __isub__(self, other): +# OK() +# return "" # edit to match type + +# def test_isub(): +# with_isub = With_isub() +# isub(with_isub) # edit to effect call + +# # object.__imul__(self, other) +# class With_imul: + +# def __imul__(self, other): +# OK() +# return "" # edit to match type + +# def test_imul(): +# with_imul = With_imul() +# imul(with_imul) # edit to effect call + +# # object.__imatmul__(self, other) +# class With_imatmul: + +# def __imatmul__(self, other): +# OK() +# return "" # edit to match type + +# def test_imatmul(): +# with_imatmul = With_imatmul() +# imatmul(with_imatmul) # edit to effect call + +# # object.__itruediv__(self, other) +# class With_itruediv: + +# def __itruediv__(self, other): +# OK() +# return "" # edit to match type + +# def test_itruediv(): +# with_itruediv = With_itruediv() +# itruediv(with_itruediv) # edit to effect call + +# # object.__ifloordiv__(self, other) +# class With_ifloordiv: + +# def __ifloordiv__(self, other): +# OK() +# return "" # edit to match type + +# def test_ifloordiv(): +# with_ifloordiv = With_ifloordiv() +# ifloordiv(with_ifloordiv) # edit to effect call + +# # object.__imod__(self, other) +# class With_imod: + +# def __imod__(self, other): +# OK() +# return "" # edit to match type + +# def test_imod(): +# with_imod = With_imod() +# imod(with_imod) # edit to effect call + +# # object.__ipow__(self, other[, modulo]) +# class With_ipow: + +# def __ipow__(self, other): +# OK() +# return "" # edit to match type + +# def test_ipow(): +# with_ipow = With_ipow() +# ipow(with_ipow) # edit to effect call + +# # object.__ilshift__(self, other) +# class With_ilshift: + +# def __ilshift__(self, other): +# OK() +# return "" # edit to match type + +# def test_ilshift(): +# with_ilshift = With_ilshift() +# ilshift(with_ilshift) # edit to effect call + +# # object.__irshift__(self, other) +# class With_irshift: + +# def __irshift__(self, other): +# OK() +# return "" # edit to match type + +# def test_irshift(): +# with_irshift = With_irshift() +# irshift(with_irshift) # edit to effect call + +# # object.__iand__(self, other) +# class With_iand: + +# def __iand__(self, other): +# OK() +# return "" # edit to match type + +# def test_iand(): +# with_iand = With_iand() +# iand(with_iand) # edit to effect call + +# # object.__ixor__(self, other) +# class With_ixor: + +# def __ixor__(self, other): +# OK() +# return "" # edit to match type + +# def test_ixor(): +# with_ixor = With_ixor() +# ixor(with_ixor) # edit to effect call + +# # object.__ior__(self, other) +# class With_ior: + +# def __ior__(self, other): +# OK() +# return "" # edit to match type + +# def test_ior(): +# with_ior = With_ior() +# ior(with_ior) # edit to effect call + +# # object.__neg__(self) +# class With_neg: + +# def __neg__(self): +# OK() +# return "" # edit to match type + +# def test_neg(): +# with_neg = With_neg() +# neg(with_neg) # edit to effect call + +# # object.__pos__(self) +# class With_pos: + +# def __pos__(self): +# OK() +# return "" # edit to match type + +# def test_pos(): +# with_pos = With_pos() +# pos(with_pos) # edit to effect call + +# # object.__abs__(self) +# class With_abs: + +# def __abs__(self): +# OK() +# return "" # edit to match type + +# def test_abs(): +# with_abs = With_abs() +# abs(with_abs) # edit to effect call + +# # object.__invert__(self) +# class With_invert: + +# def __invert__(self): +# OK() +# return "" # edit to match type + +# def test_invert(): +# with_invert = With_invert() +# invert(with_invert) # edit to effect call + +# # object.__complex__(self) +# class With_complex: + +# def __complex__(self): +# OK() +# return "" # edit to match type + +# def test_complex(): +# with_complex = With_complex() +# complex(with_complex) # edit to effect call + +# # object.__int__(self) +# class With_int: + +# def __int__(self): +# OK() +# return "" # edit to match type + +# def test_int(): +# with_int = With_int() +# int(with_int) # edit to effect call + +# # object.__float__(self) +# class With_float: + +# def __float__(self): +# OK() +# return "" # edit to match type + +# def test_float(): +# with_float = With_float() +# float(with_float) # edit to effect call + +# # object.__index__(self) +# class With_index: + +# def __index__(self): +# OK() +# return "" # edit to match type + +# def test_index(): +# with_index = With_index() +# index(with_index) # edit to effect call + +# # object.__round__(self[, ndigits]) +# class With_round: + +# def __round__(self): +# OK() +# return "" # edit to match type + +# def test_round(): +# with_round = With_round() +# round(with_round) # edit to effect call + +# # object.__trunc__(self) +# class With_trunc: + +# def __trunc__(self): +# OK() +# return "" # edit to match type + +# def test_trunc(): +# with_trunc = With_trunc() +# trunc(with_trunc) # edit to effect call + +# # object.__floor__(self) +# class With_floor: + +# def __floor__(self): +# OK() +# return "" # edit to match type + +# def test_floor(): +# with_floor = With_floor() +# floor(with_floor) # edit to effect call + +# # object.__ceil__(self) +# class With_ceil: + +# def __ceil__(self): +# OK() +# return "" # edit to match type + +# def test_ceil(): +# with_ceil = With_ceil() +# ceil(with_ceil) # edit to effect call + + +# # 3.3.9. With Statement Context Managers +# # object.__enter__(self) +# class With_enter: + +# def __enter__(self): +# OK() +# return "" # edit to match type + +# def test_enter(): +# with_enter = With_enter() +# enter(with_enter) # edit to effect call + +# # object.__exit__(self, exc_type, exc_value, traceback) +# class With_exit: + +# def __exit__(self, exc_type, exc_value, traceback): +# OK() +# return "" # edit to match type + +# def test_exit(): +# with_exit = With_exit() +# exit(with_exit) # edit to effect call + + +# # 3.4.1. Awaitable Objects +# # object.__await__(self) +# class With_await: + +# def __await__(self): +# OK() +# return "" # edit to match type + +# async def test_await(): +# with_await = With_await() +# await(with_await) # edit to effect call + + +# # 3.4.2. Coroutine Objects +# # coroutine.send(value) +# # coroutine.throw(type[, value[, traceback]]) +# # coroutine.close() + +# # 3.4.3. Asynchronous Iterators +# # object.__aiter__(self) +# class With_aiter: + +# def __aiter__(self): +# OK() +# return "" # edit to match type + +# def test_aiter(): +# with_aiter = With_aiter() +# aiter(with_aiter) # edit to effect call + +# # object.__anext__(self) +# class With_anext: + +# def __anext__(self): +# OK() +# return "" # edit to match type + +# def test_anext(): +# with_anext = With_anext() +# anext(with_anext) # edit to effect call + + +# # 3.4.4. Asynchronous Context Managers +# # object.__aenter__(self) +# class With_aenter: + +# def __aenter__(self): +# OK() +# return "" # edit to match type + +# def test_aenter(): +# with_aenter = With_aenter() +# aenter(with_aenter) # edit to effect call + +# # object.__aexit__(self, exc_type, exc_value, traceback) +# class With_aexit: + +# def __aexit__(self, exc_type, exc_value, traceback): +# OK() +# return "" # edit to match type + +# def test_aexit(): +# with_aexit = With_aexit() +# aexit(with_aexit) # edit to effect call diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index 0324fd6a7f9..c3c0702f1a8 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -6,19 +6,27 @@ # # Functions whose name ends with "_with_local_flow" will also be tested for local flow. # -# All functions starting with "test_" should run and either -# - print a source (sources are defined in testConfig.qll). -# - print "Unexpected flow: " and a non-source -# (The idea is to later write a script to autimatically confirm this.) +# All functions starting with "test_" should run and print `"OK"`. +# This can be checked by running validTest.py. # These are defined so that we can evaluate the test code. NONSOURCE = "not a source" SOURCE = "source" + +def is_source(x): + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + def SINK(x): - print(x) + if is_source(x): + print("OK") + else: + print("Unexpected flow", x) def SINK_F(x): - print("Unexpected flow: ", x) + if is_source(x): + print("Unexpected flow", x) + else: + print("OK") def test_tuple_with_local_flow(): x = (NONSOURCE, SOURCE) diff --git a/python/ql/test/experimental/dataflow/coverage/validTest.py b/python/ql/test/experimental/dataflow/coverage/validTest.py new file mode 100644 index 00000000000..97ba6be8cdb --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/validTest.py @@ -0,0 +1,33 @@ +def check_output(s, f): + if s == "OK\n": + pass + else: + raise RuntimeError("Function failed", s, f) + +def check_test_function(f): + from io import StringIO + import sys + + capturer = StringIO() + old_stdout = sys.stdout + sys.stdout = capturer + f() + sys.stdout = old_stdout + check_output(capturer.getvalue(), f) + +def check_classes_valid(testFile): + # import python.ql.test.experimental.dataflow.coverage.classes as tests + # import classes as tests + import importlib + tests = importlib.import_module(testFile) + for i in dir(tests): + # print("Considering", i) + if i.startswith("test_"): + item = getattr(tests,i) + if callable(item): + print("Checking", testFile, item) + check_test_function(item) + +if __name__ == '__main__': + check_classes_valid("classes") + check_classes_valid("test") From aff4535965d4cc70b79a45307ebbc7d1bb38122c Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 7 Aug 2020 23:07:58 +0200 Subject: [PATCH 11/27] Python: fix tests for descriptors --- .../experimental/dataflow/coverage/classes.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 3cbe6e97791..154e40ec365 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -418,11 +418,10 @@ class With_get: OK() return "" -def ftest_get(): +def test_get(): with_get = With_get() - Owner.foo = "" Owner.attr = with_get - Owner.foo + Owner.attr # object.__set__(self, instance, value) class With_set: @@ -430,9 +429,11 @@ class With_set: def __set__(self, instance, value): OK() -def ftest_set(): +def test_set(): with_set = With_set() - with_set.foo = "" + Owner.attr = with_set + owner = Owner() + owner.attr = "" # object.__delete__(self, instance) class With_delete: @@ -440,21 +441,21 @@ class With_delete: def __delete__(self, instance): OK() -def ftest_delete(): +def test_delete(): with_delete = With_delete() - with_delete.foo = "" - del with_delete.foo + Owner.attr = with_delete + owner = Owner() + del owner.attr # object.__set_name__(self, owner, name) class With_set_name: - def __set_name__(self, instance): + def __set_name__(self, owner, name): OK() -def ftest_set_name(): +def test_set_name(): with_set_name = With_set_name() - with_set_name.foo = "" - del with_set_name.foo + type("Owner", (object,), dict(attr=with_set_name)) # 3.3.2.4. __slots__ # object.__slots__ From f6d6f91a42429be198e7417153c1f3f59ada611a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 7 Aug 2020 23:39:42 +0200 Subject: [PATCH 12/27] Python: tests for containers --- .../experimental/dataflow/coverage/classes.py | 123 ++++++++++-------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 154e40ec365..b1dcecb1e63 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -513,83 +513,92 @@ def test_len_if(): if with_len: pass -# # object.__length_hint__(self) -# # object.__getitem__(self, key) -# class With_getitem: +# object.__length_hint__(self) +class With_length_hint: -# def __getitem__(self, key): -# OK() -# return "" # edit to match type + def __length_hint__(self): + OK() + return 0 -# def test_getitem(): -# with_getitem = With_getitem() -# getitem(with_getitem) # edit to effect call +def test_length_hint(): + import operator + with_length_hint = With_length_hint() + operator.length_hint(with_length_hint) -# # object.__setitem__(self, key, value) -# class With_setitem: +# object.__getitem__(self, key) +class With_getitem: -# def __setitem__(self, key, value): -# OK() -# return "" # edit to match type + def __getitem__(self, key): + OK() + return "" -# def test_setitem(): -# with_setitem = With_setitem() -# setitem(with_setitem) # edit to effect call +def test_getitem(): + with_getitem = With_getitem() + with_getitem[0] -# # object.__delitem__(self, key) -# class With_delitem: +# object.__setitem__(self, key, value) +class With_setitem: -# def __delitem__(self, key): -# OK() -# return "" # edit to match type + def __setitem__(self, key, value): + OK() -# def test_delitem(): -# with_delitem = With_delitem() -# delitem(with_delitem) # edit to effect call +def test_setitem(): + with_setitem = With_setitem() + with_setitem[0] = "" -# # object.__missing__(self, key) -# class With_missing: +# object.__delitem__(self, key) +class With_delitem: -# def __missing__(self, key): -# OK() -# return "" # edit to match type + def __delitem__(self, key): + OK() -# def test_missing(): -# with_missing = With_missing() -# missing(with_missing) # edit to effect call +def test_delitem(): + with_delitem = With_delitem() + del with_delitem[0] -# # object.__iter__(self) -# class With_iter: +# object.__missing__(self, key) +class With_missing(dict): -# def __iter__(self): -# OK() -# return "" # edit to match type + def __missing__(self, key): + OK() + return "" -# def test_iter(): -# with_iter = With_iter() -# iter(with_iter) # edit to effect call +def test_missing(): + with_missing = With_missing() + with_missing[""] -# # object.__reversed__(self) -# class With_reversed: +# object.__iter__(self) +class With_iter: -# def __reversed__(self): -# OK() -# return "" # edit to match type + def __iter__(self): + OK() + return [].__iter__() -# def test_reversed(): -# with_reversed = With_reversed() -# reversed(with_reversed) # edit to effect call +def test_iter(): + with_iter = With_iter() + [x for x in with_iter] -# # object.__contains__(self, item) -# class With_contains: +# object.__reversed__(self) +class With_reversed: -# def __contains__(self, item): -# OK() -# return "" # edit to match type + def __reversed__(self): + OK() + return [].__iter__ -# def test_contains(): -# with_contains = With_contains() -# contains(with_contains) # edit to effect call +def test_reversed(): + with_reversed = With_reversed() + reversed(with_reversed) + +# object.__contains__(self, item) +class With_contains: + + def __contains__(self, item): + OK() + return True + +def test_contains(): + with_contains = With_contains() + 0 in with_contains # # 3.3.8. Emulating numeric types From 5b7c7f933cc98a949b75e8840e9d6e6ec2930fe4 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Sat, 8 Aug 2020 00:31:29 +0200 Subject: [PATCH 13/27] Python: tests for numeric classes --- .../experimental/dataflow/coverage/classes.py | 888 +++++++++--------- 1 file changed, 463 insertions(+), 425 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index b1dcecb1e63..81ef52d093c 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -601,589 +601,627 @@ def test_contains(): 0 in with_contains -# # 3.3.8. Emulating numeric types -# # object.__add__(self, other) -# class With_add: +# 3.3.8. Emulating numeric types +# object.__add__(self, other) +class With_add: -# def __add__(self, other): -# OK() -# return self + def __add__(self, other): + OK() + return self -# def test_add(): -# with_add = With_add() -# with_add + with_add +def test_add(): + with_add = With_add() + with_add + with_add -# # object.__sub__(self, other) -# class With_sub: +# object.__sub__(self, other) +class With_sub: -# def __sub__(self, other): -# OK() -# return "" # edit to match type + def __sub__(self, other): + OK() + return self -# def test_sub(): -# with_sub = With_sub() -# sub(with_sub) # edit to effect call +def test_sub(): + with_sub = With_sub() + with_sub - with_sub -# # object.__mul__(self, other) -# class With_mul: +# object.__mul__(self, other) +class With_mul: -# def __mul__(self, other): -# OK() -# return "" # edit to match type + def __mul__(self, other): + OK() + return self -# def test_mul(): -# with_mul = With_mul() -# mul(with_mul) # edit to effect call +def test_mul(): + with_mul = With_mul() + with_mul * with_mul -# # object.__matmul__(self, other) -# class With_matmul: +# object.__matmul__(self, other) +class With_matmul: -# def __matmul__(self, other): -# OK() -# return "" # edit to match type + def __matmul__(self, other): + OK() + return self -# def test_matmul(): -# with_matmul = With_matmul() -# matmul(with_matmul) # edit to effect call +def test_matmul(): + with_matmul = With_matmul() + with_matmul @ with_matmul -# # object.__truediv__(self, other) -# class With_truediv: +# object.__truediv__(self, other) +class With_truediv: -# def __truediv__(self, other): -# OK() -# return "" # edit to match type + def __truediv__(self, other): + OK() + return self -# def test_truediv(): -# with_truediv = With_truediv() -# truediv(with_truediv) # edit to effect call +def test_truediv(): + with_truediv = With_truediv() + with_truediv / with_truediv -# # object.__floordiv__(self, other) -# class With_floordiv: +# object.__floordiv__(self, other) +class With_floordiv: -# def __floordiv__(self, other): -# OK() -# return "" # edit to match type + def __floordiv__(self, other): + OK() + return self -# def test_floordiv(): -# with_floordiv = With_floordiv() -# floordiv(with_floordiv) # edit to effect call +def test_floordiv(): + with_floordiv = With_floordiv() + with_floordiv // with_floordiv -# # object.__mod__(self, other) -# class With_mod: +# object.__mod__(self, other) +class With_mod: -# def __mod__(self, other): -# OK() -# return "" # edit to match type + def __mod__(self, other): + OK() + return self -# def test_mod(): -# with_mod = With_mod() -# mod(with_mod) # edit to effect call +def test_mod(): + with_mod = With_mod() + with_mod % with_mod -# # object.__divmod__(self, other) -# class With_divmod: +# object.__divmod__(self, other) +class With_divmod: -# def __divmod__(self, other): -# OK() -# return "" # edit to match type + def __divmod__(self, other): + OK() + return self -# def test_divmod(): -# with_divmod = With_divmod() -# divmod(with_divmod) # edit to effect call +def test_divmod(): + with_divmod = With_divmod() + divmod(with_divmod, with_divmod) -# # object.__pow__(self, other[, modulo]) -# class With_pow: +# object.__pow__(self, other[, modulo]) +class With_pow: -# def __pow__(self, other): -# OK() -# return "" # edit to match type + def __pow__(self, other): + OK() + return self -# def test_pow(): -# with_pow = With_pow() -# pow(with_pow) # edit to effect call +def test_pow(): + with_pow = With_pow() + pow(with_pow, with_pow) -# # object.__lshift__(self, other) -# class With_lshift: +def test_pow_op(): + with_pow = With_pow() + with_pow ** with_pow -# def __lshift__(self, other): -# OK() -# return "" # edit to match type +# object.__lshift__(self, other) +class With_lshift: -# def test_lshift(): -# with_lshift = With_lshift() -# lshift(with_lshift) # edit to effect call + def __lshift__(self, other): + OK() + return self -# # object.__rshift__(self, other) -# class With_rshift: +def test_lshift(): + with_lshift = With_lshift() + with_lshift << with_lshift -# def __rshift__(self, other): -# OK() -# return "" # edit to match type +# object.__rshift__(self, other) +class With_rshift: -# def test_rshift(): -# with_rshift = With_rshift() -# rshift(with_rshift) # edit to effect call + def __rshift__(self, other): + OK() + return self -# # object.__and__(self, other) -# class With_and: +def test_rshift(): + with_rshift = With_rshift() + with_rshift >> with_rshift -# def __and__(self, other): -# OK() -# return "" # edit to match type +# object.__and__(self, other) +class With_and: -# def test_and(): -# with_and = With_and() -# with_and & with_and + def __and__(self, other): + OK() + return self -# # object.__xor__(self, other) -# class With_xor: +def test_and(): + with_and = With_and() + with_and & with_and -# def __xor__(self, other): -# OK() -# return "" # edit to match type +# object.__xor__(self, other) +class With_xor: -# def test_xor(): -# with_xor = With_xor() -# xor(with_xor) # edit to effect call + def __xor__(self, other): + OK() + return self -# # object.__or__(self, other) -# class With_or: +def test_xor(): + with_xor = With_xor() + with_xor ^ with_xor -# def __or__(self, other): -# OK() -# return "" # edit to match type +# object.__or__(self, other) +class With_or: -# def test_or(): -# with_or = With_or() -# with_or | with_or + def __or__(self, other): + OK() + return self -# # object.__radd__(self, other) -# class With_radd: +def test_or(): + with_or = With_or() + with_or | with_or -# def __radd__(self, other): -# OK() -# return "" # edit to match type +# object.__radd__(self, other) +class With_radd: -# def test_radd(): -# with_radd = With_radd() -# radd(with_radd) # edit to effect call + def __radd__(self, other): + OK() + return self -# # object.__rsub__(self, other) -# class With_rsub: +def test_radd(): + with_radd = With_radd() + f"" + with_radd -# def __rsub__(self, other): -# OK() -# return "" # edit to match type +# object.__rsub__(self, other) +class With_rsub: -# def test_rsub(): -# with_rsub = With_rsub() -# rsub(with_rsub) # edit to effect call + def __rsub__(self, other): + OK() + return self -# # object.__rmul__(self, other) -# class With_rmul: +def test_rsub(): + with_rsub = With_rsub() + f"" - with_rsub -# def __rmul__(self, other): -# OK() -# return "" # edit to match type +# object.__rmul__(self, other) +class With_rmul: -# def test_rmul(): -# with_rmul = With_rmul() -# rmul(with_rmul) # edit to effect call + def __rmul__(self, other): + OK() + return self -# # object.__rmatmul__(self, other) -# class With_rmatmul: +def test_rmul(): + with_rmul = With_rmul() + f"" * with_rmul -# def __rmatmul__(self, other): -# OK() -# return "" # edit to match type +# object.__rmatmul__(self, other) +class With_rmatmul: -# def test_rmatmul(): -# with_rmatmul = With_rmatmul() -# rmatmul(with_rmatmul) # edit to effect call + def __rmatmul__(self, other): + OK() + return self -# # object.__rtruediv__(self, other) -# class With_rtruediv: +def test_rmatmul(): + with_rmatmul = With_rmatmul() + f"" @ with_rmatmul -# def __rtruediv__(self, other): -# OK() -# return "" # edit to match type +# object.__rtruediv__(self, other) +class With_rtruediv: -# def test_rtruediv(): -# with_rtruediv = With_rtruediv() -# rtruediv(with_rtruediv) # edit to effect call + def __rtruediv__(self, other): + OK() + return self -# # object.__rfloordiv__(self, other) -# class With_rfloordiv: +def test_rtruediv(): + with_rtruediv = With_rtruediv() + f"" / with_rtruediv -# def __rfloordiv__(self, other): -# OK() -# return "" # edit to match type +# object.__rfloordiv__(self, other) +class With_rfloordiv: -# def test_rfloordiv(): -# with_rfloordiv = With_rfloordiv() -# rfloordiv(with_rfloordiv) # edit to effect call + def __rfloordiv__(self, other): + OK() + return self -# # object.__rmod__(self, other) -# class With_rmod: +def test_rfloordiv(): + with_rfloordiv = With_rfloordiv() + f"" // with_rfloordiv -# def __rmod__(self, other): -# OK() -# return "" # edit to match type +# object.__rmod__(self, other) +class With_rmod: -# def test_rmod(): -# with_rmod = With_rmod() -# rmod(with_rmod) # edit to effect call + def __rmod__(self, other): + OK() + return self -# # object.__rdivmod__(self, other) -# class With_rdivmod: +def test_rmod(): + with_rmod = With_rmod() + {} % with_rmod -# def __rdivmod__(self, other): -# OK() -# return "" # edit to match type +# object.__rdivmod__(self, other) +class With_rdivmod: -# def test_rdivmod(): -# with_rdivmod = With_rdivmod() -# rdivmod(with_rdivmod) # edit to effect call + def __rdivmod__(self, other): + OK() + return self -# # object.__rpow__(self, other[, modulo]) -# class With_rpow: +def test_rdivmod(): + with_rdivmod = With_rdivmod() + divmod(f"", with_rdivmod) -# def __rpow__(self, other): -# OK() -# return "" # edit to match type +# object.__rpow__(self, other[, modulo]) +class With_rpow: -# def test_rpow(): -# with_rpow = With_rpow() -# rpow(with_rpow) # edit to effect call + def __rpow__(self, other): + OK() + return self -# # object.__rlshift__(self, other) -# class With_rlshift: +def test_rpow(): + with_rpow = With_rpow() + pow(f"", with_rpow) -# def __rlshift__(self, other): -# OK() -# return "" # edit to match type +def test_rpow_op(): + with_rpow = With_rpow() + f"" ** with_rpow -# def test_rlshift(): -# with_rlshift = With_rlshift() -# rlshift(with_rlshift) # edit to effect call +# object.__rlshift__(self, other) +class With_rlshift: -# # object.__rrshift__(self, other) -# class With_rrshift: + def __rlshift__(self, other): + OK() + return self -# def __rrshift__(self, other): -# OK() -# return "" # edit to match type +def ftest_rlshift(): # TypeError: unsupported operand type(s) for >>: 'str' and 'With_rlshift' + with_rlshift = With_rlshift() + f"" >> with_rlshift -# def test_rrshift(): -# with_rrshift = With_rrshift() -# rrshift(with_rrshift) # edit to effect call +# object.__rrshift__(self, other) +class With_rrshift: -# # object.__rand__(self, other) -# class With_rand: + def __rrshift__(self, other): + OK() + return self -# def __rand__(self, other): -# OK() -# return "" # edit to match type +def ftest_rrshift(): # TypeError: unsupported operand type(s) for <<: 'str' and 'With_rrshift' + with_rrshift = With_rrshift() + f"" << with_rrshift -# def test_rand(): -# with_rand = With_rand() -# rand(with_rand) # edit to effect call +# object.__rand__(self, other) +class With_rand: -# # object.__rxor__(self, other) -# class With_rxor: + def __rand__(self, other): + OK() + return self -# def __rxor__(self, other): -# OK() -# return "" # edit to match type +def test_rand(): + with_rand = With_rand() + f"" & with_rand -# def test_rxor(): -# with_rxor = With_rxor() -# rxor(with_rxor) # edit to effect call +# object.__rxor__(self, other) +class With_rxor: -# # object.__ror__(self, other) -# class With_ror: + def __rxor__(self, other): + OK() + return self -# def __ror__(self, other): -# OK() -# return "" # edit to match type +def test_rxor(): + with_rxor = With_rxor() + f"" ^ with_rxor -# def test_ror(): -# with_ror = With_ror() -# ror(with_ror) # edit to effect call +# object.__ror__(self, other) +class With_ror: -# # object.__iadd__(self, other) -# class With_iadd: + def __ror__(self, other): + OK() + return self -# def __iadd__(self, other): -# OK() -# return "" # edit to match type +def test_ror(): + with_ror = With_ror() + f"" | with_ror -# def test_iadd(): -# with_iadd = With_iadd() -# iadd(with_iadd) # edit to effect call +# object.__iadd__(self, other) +class With_iadd: -# # object.__isub__(self, other) -# class With_isub: + def __iadd__(self, other): + OK() + return self -# def __isub__(self, other): -# OK() -# return "" # edit to match type +def test_iadd(): + with_iadd = With_iadd() + with_iadd += with_iadd -# def test_isub(): -# with_isub = With_isub() -# isub(with_isub) # edit to effect call +# object.__isub__(self, other) +class With_isub: -# # object.__imul__(self, other) -# class With_imul: + def __isub__(self, other): + OK() + return self -# def __imul__(self, other): -# OK() -# return "" # edit to match type +def test_isub(): + with_isub = With_isub() + with_isub -= with_isub -# def test_imul(): -# with_imul = With_imul() -# imul(with_imul) # edit to effect call +# object.__imul__(self, other) +class With_imul: -# # object.__imatmul__(self, other) -# class With_imatmul: + def __imul__(self, other): + OK() + return self -# def __imatmul__(self, other): -# OK() -# return "" # edit to match type +def test_imul(): + with_imul = With_imul() + with_imul *= with_imul -# def test_imatmul(): -# with_imatmul = With_imatmul() -# imatmul(with_imatmul) # edit to effect call +# object.__imatmul__(self, other) +class With_imatmul: -# # object.__itruediv__(self, other) -# class With_itruediv: + def __imatmul__(self, other): + OK() + return self -# def __itruediv__(self, other): -# OK() -# return "" # edit to match type +def test_imatmul(): + with_imatmul = With_imatmul() + with_imatmul @= with_imatmul -# def test_itruediv(): -# with_itruediv = With_itruediv() -# itruediv(with_itruediv) # edit to effect call +# object.__itruediv__(self, other) +class With_itruediv: -# # object.__ifloordiv__(self, other) -# class With_ifloordiv: + def __itruediv__(self, other): + OK() + return self -# def __ifloordiv__(self, other): -# OK() -# return "" # edit to match type +def test_itruediv(): + with_itruediv = With_itruediv() + with_itruediv /= with_itruediv -# def test_ifloordiv(): -# with_ifloordiv = With_ifloordiv() -# ifloordiv(with_ifloordiv) # edit to effect call +# object.__ifloordiv__(self, other) +class With_ifloordiv: -# # object.__imod__(self, other) -# class With_imod: + def __ifloordiv__(self, other): + OK() + return self -# def __imod__(self, other): -# OK() -# return "" # edit to match type +def test_ifloordiv(): + with_ifloordiv = With_ifloordiv() + with_ifloordiv //= with_ifloordiv -# def test_imod(): -# with_imod = With_imod() -# imod(with_imod) # edit to effect call +# object.__imod__(self, other) +class With_imod: -# # object.__ipow__(self, other[, modulo]) -# class With_ipow: + def __imod__(self, other): + OK() + return self -# def __ipow__(self, other): -# OK() -# return "" # edit to match type +def test_imod(): + with_imod = With_imod() + with_imod %= with_imod -# def test_ipow(): -# with_ipow = With_ipow() -# ipow(with_ipow) # edit to effect call +# object.__ipow__(self, other[, modulo]) +class With_ipow: -# # object.__ilshift__(self, other) -# class With_ilshift: + def __ipow__(self, other): + OK() + return self -# def __ilshift__(self, other): -# OK() -# return "" # edit to match type +def test_ipow(): + with_ipow = With_ipow() + with_ipow **= with_ipow -# def test_ilshift(): -# with_ilshift = With_ilshift() -# ilshift(with_ilshift) # edit to effect call +# object.__ilshift__(self, other) +class With_ilshift: -# # object.__irshift__(self, other) -# class With_irshift: + def __ilshift__(self, other): + OK() + return self -# def __irshift__(self, other): -# OK() -# return "" # edit to match type +def test_ilshift(): + with_ilshift = With_ilshift() + with_ilshift <<= with_ilshift -# def test_irshift(): -# with_irshift = With_irshift() -# irshift(with_irshift) # edit to effect call +# object.__irshift__(self, other) +class With_irshift: -# # object.__iand__(self, other) -# class With_iand: + def __irshift__(self, other): + OK() + return self -# def __iand__(self, other): -# OK() -# return "" # edit to match type +def test_irshift(): + with_irshift = With_irshift() + with_irshift >>= with_irshift -# def test_iand(): -# with_iand = With_iand() -# iand(with_iand) # edit to effect call +# object.__iand__(self, other) +class With_iand: -# # object.__ixor__(self, other) -# class With_ixor: + def __iand__(self, other): + OK() + return self -# def __ixor__(self, other): -# OK() -# return "" # edit to match type +def test_iand(): + with_iand = With_iand() + with_iand &= with_iand -# def test_ixor(): -# with_ixor = With_ixor() -# ixor(with_ixor) # edit to effect call +# object.__ixor__(self, other) +class With_ixor: -# # object.__ior__(self, other) -# class With_ior: + def __ixor__(self, other): + OK() + return self -# def __ior__(self, other): -# OK() -# return "" # edit to match type +def test_ixor(): + with_ixor = With_ixor() + with_ixor ^= with_ixor -# def test_ior(): -# with_ior = With_ior() -# ior(with_ior) # edit to effect call +# object.__ior__(self, other) +class With_ior: -# # object.__neg__(self) -# class With_neg: + def __ior__(self, other): + OK() + return self -# def __neg__(self): -# OK() -# return "" # edit to match type +def test_ior(): + with_ior = With_ior() + with_ior |= with_ior -# def test_neg(): -# with_neg = With_neg() -# neg(with_neg) # edit to effect call +# object.__neg__(self) +class With_neg: -# # object.__pos__(self) -# class With_pos: + def __neg__(self): + OK() + return self -# def __pos__(self): -# OK() -# return "" # edit to match type +def test_neg(): + with_neg = With_neg() + -with_neg -# def test_pos(): -# with_pos = With_pos() -# pos(with_pos) # edit to effect call +# object.__pos__(self) +class With_pos: -# # object.__abs__(self) -# class With_abs: + def __pos__(self): + OK() + return self -# def __abs__(self): -# OK() -# return "" # edit to match type +def test_pos(): + with_pos = With_pos() + +with_pos -# def test_abs(): -# with_abs = With_abs() -# abs(with_abs) # edit to effect call +# object.__abs__(self) +class With_abs: -# # object.__invert__(self) -# class With_invert: + def __abs__(self): + OK() + return self -# def __invert__(self): -# OK() -# return "" # edit to match type +def test_abs(): + with_abs = With_abs() + abs(with_abs) -# def test_invert(): -# with_invert = With_invert() -# invert(with_invert) # edit to effect call +# object.__invert__(self) +class With_invert: -# # object.__complex__(self) -# class With_complex: + def __invert__(self): + OK() + return self -# def __complex__(self): -# OK() -# return "" # edit to match type +def test_invert(): + with_invert = With_invert() + ~with_invert -# def test_complex(): -# with_complex = With_complex() -# complex(with_complex) # edit to effect call +# object.__complex__(self) +class With_complex: -# # object.__int__(self) -# class With_int: + def __complex__(self): + OK() + return 0j -# def __int__(self): -# OK() -# return "" # edit to match type +def test_complex(): + with_complex = With_complex() + complex(with_complex) -# def test_int(): -# with_int = With_int() -# int(with_int) # edit to effect call +# object.__int__(self) +class With_int: -# # object.__float__(self) -# class With_float: + def __int__(self): + OK() + return 0 -# def __float__(self): -# OK() -# return "" # edit to match type +def test_int(): + with_int = With_int() + int(with_int) -# def test_float(): -# with_float = With_float() -# float(with_float) # edit to effect call +# object.__float__(self) +class With_float: -# # object.__index__(self) -# class With_index: + def __float__(self): + OK() + return 0.0 -# def __index__(self): -# OK() -# return "" # edit to match type +def test_float(): + with_float = With_float() + float(with_float) -# def test_index(): -# with_index = With_index() -# index(with_index) # edit to effect call +# object.__index__(self) +class With_index: -# # object.__round__(self[, ndigits]) -# class With_round: + def __index__(self): + OK() + return 0 -# def __round__(self): -# OK() -# return "" # edit to match type +def test_index(): + import operator + with_index = With_index() + operator.index(with_index) -# def test_round(): -# with_round = With_round() -# round(with_round) # edit to effect call +# make With_index subscriptable to test slicing -# # object.__trunc__(self) -# class With_trunc: +def test_index_bin(): + with_index = With_index() + bin(with_index) -# def __trunc__(self): -# OK() -# return "" # edit to match type +def test_index_hex(): + with_index = With_index() + hex(with_index) -# def test_trunc(): -# with_trunc = With_trunc() -# trunc(with_trunc) # edit to effect call +def test_index_oct(): + with_index = With_index() + oct(with_index) -# # object.__floor__(self) -# class With_floor: +def test_index_int(): + with_index = With_index() + int(with_index) -# def __floor__(self): -# OK() -# return "" # edit to match type +def test_index_float(): + with_index = With_index() + float(with_index) -# def test_floor(): -# with_floor = With_floor() -# floor(with_floor) # edit to effect call +def test_index_complex(): + with_index = With_index() + complex(with_index) -# # object.__ceil__(self) -# class With_ceil: +# object.__round__(self[, ndigits]) +class With_round: -# def __ceil__(self): -# OK() -# return "" # edit to match type + def __round__(self): + OK() + return 0 -# def test_ceil(): -# with_ceil = With_ceil() -# ceil(with_ceil) # edit to effect call +def test_round(): + with_round = With_round() + round(with_round) + +# object.__trunc__(self) +class With_trunc: + + def __trunc__(self): + OK() + return 0 + +def test_trunc(): + with_trunc = With_trunc() + import math + math.trunc(with_trunc) # edit to effect call + +# object.__floor__(self) +class With_floor: + + def __floor__(self): + OK() + return 0 + +def test_floor(): + with_floor = With_floor() + import math + math.floor(with_floor) # edit to effect call + +# object.__ceil__(self) +class With_ceil: + + def __ceil__(self): + OK() + return 0 + +def test_ceil(): + with_ceil = With_ceil() + import math + math.ceil(with_ceil) # edit to effect call # # 3.3.9. With Statement Context Managers From 02478774c3f829f4318b205003e5a18d7cf3fbea Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 10 Aug 2020 08:11:25 +0200 Subject: [PATCH 14/27] Python: tests for context managers --- .../experimental/dataflow/coverage/classes.py | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 81ef52d093c..6f9a53076ed 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1224,29 +1224,34 @@ def test_ceil(): math.ceil(with_ceil) # edit to effect call -# # 3.3.9. With Statement Context Managers -# # object.__enter__(self) -# class With_enter: +# 3.3.9. With Statement Context Managers +# object.__enter__(self) +class With_enter: -# def __enter__(self): -# OK() -# return "" # edit to match type + def __enter__(self): + OK() + return -# def test_enter(): -# with_enter = With_enter() -# enter(with_enter) # edit to effect call + def __exit__(self, exc_type, exc_value, traceback): + return -# # object.__exit__(self, exc_type, exc_value, traceback) -# class With_exit: +def test_enter(): + with With_enter(): + pass -# def __exit__(self, exc_type, exc_value, traceback): -# OK() -# return "" # edit to match type +# object.__exit__(self, exc_type, exc_value, traceback) +class With_exit: -# def test_exit(): -# with_exit = With_exit() -# exit(with_exit) # edit to effect call + def __enter__(self): + return + def __exit__(self, exc_type, exc_value, traceback): + OK() + return + +def test_exit(): + with With_exit(): + pass # # 3.4.1. Awaitable Objects # # object.__await__(self) From 639d914a47e014513b82e6d67bac673df518caec Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 10 Aug 2020 08:58:16 +0200 Subject: [PATCH 15/27] Python: test Awaitable, framework for async test --- .../experimental/dataflow/coverage/classes.py | 18 +++++++++--------- .../dataflow/coverage/validTest.py | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 6f9a53076ed..ef40e546f7e 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1253,17 +1253,17 @@ def test_exit(): with With_exit(): pass -# # 3.4.1. Awaitable Objects -# # object.__await__(self) -# class With_await: +# 3.4.1. Awaitable Objects +# object.__await__(self) +class With_await: -# def __await__(self): -# OK() -# return "" # edit to match type + def __await__(self): + OK() + return (yield from asyncio.coroutine(lambda: "")()) -# async def test_await(): -# with_await = With_await() -# await(with_await) # edit to effect call +async def atest_await(): + with_await = With_await() + await(with_await) # # 3.4.2. Coroutine Objects diff --git a/python/ql/test/experimental/dataflow/coverage/validTest.py b/python/ql/test/experimental/dataflow/coverage/validTest.py index 97ba6be8cdb..dbede541e82 100644 --- a/python/ql/test/experimental/dataflow/coverage/validTest.py +++ b/python/ql/test/experimental/dataflow/coverage/validTest.py @@ -15,6 +15,18 @@ def check_test_function(f): sys.stdout = old_stdout check_output(capturer.getvalue(), f) +def check_async_test_function(f): + from io import StringIO + import sys + import asyncio + + capturer = StringIO() + old_stdout = sys.stdout + sys.stdout = capturer + asyncio.run(f()) + sys.stdout = old_stdout + check_output(capturer.getvalue(), f) + def check_classes_valid(testFile): # import python.ql.test.experimental.dataflow.coverage.classes as tests # import classes as tests @@ -28,6 +40,12 @@ def check_classes_valid(testFile): print("Checking", testFile, item) check_test_function(item) + elif i.startswith("atest_"): + item = getattr(tests,i) + if callable(item): + print("Checking", testFile, item) + check_async_test_function(item) + if __name__ == '__main__': check_classes_valid("classes") check_classes_valid("test") From 959c6315c41ae2dfa9a3445d3ee6ca863ad8a1f3 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 10 Aug 2020 09:24:45 +0200 Subject: [PATCH 16/27] Python: update reference to fix tests --- python/ql/test/experimental/dataflow/basic/callGraphSinks.ql | 2 +- python/ql/test/experimental/dataflow/basic/callGraphSources.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql index 020ea245cfd..7d15b353274 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSinks.ql @@ -1,4 +1,4 @@ -import callGraphConfig +import experimental.dataflow.callGraphConfig from DataFlow::Node sink where exists(CallGraphConfig cfg | cfg.isSink(sink)) diff --git a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql index a6bd5538866..21c3a5a9ace 100644 --- a/python/ql/test/experimental/dataflow/basic/callGraphSources.ql +++ b/python/ql/test/experimental/dataflow/basic/callGraphSources.ql @@ -1,4 +1,4 @@ -import callGraphConfig +import experimental.dataflow.callGraphConfig from DataFlow::Node source where exists(CallGraphConfig cfg | cfg.isSource(source)) From a963f15100f2fdd5b5d0b217091fb29d2cf6bf0e Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 10 Aug 2020 11:54:24 +0200 Subject: [PATCH 17/27] Python: format strings are unnecessary and mess up For some reason, we got no results when format strings were present. --- .../experimental/dataflow/coverage/classes.py | 28 +-- .../coverage/classesCallGraph.expected | 193 ++++++++++-------- .../dataflow/coverage/localFlow.expected | 14 +- 3 files changed, 133 insertions(+), 102 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index ef40e546f7e..f3b3e013b22 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -769,7 +769,7 @@ class With_radd: def test_radd(): with_radd = With_radd() - f"" + with_radd + "" + with_radd # object.__rsub__(self, other) class With_rsub: @@ -780,7 +780,7 @@ class With_rsub: def test_rsub(): with_rsub = With_rsub() - f"" - with_rsub + "" - with_rsub # object.__rmul__(self, other) class With_rmul: @@ -791,7 +791,7 @@ class With_rmul: def test_rmul(): with_rmul = With_rmul() - f"" * with_rmul + "" * with_rmul # object.__rmatmul__(self, other) class With_rmatmul: @@ -802,7 +802,7 @@ class With_rmatmul: def test_rmatmul(): with_rmatmul = With_rmatmul() - f"" @ with_rmatmul + "" @ with_rmatmul # object.__rtruediv__(self, other) class With_rtruediv: @@ -813,7 +813,7 @@ class With_rtruediv: def test_rtruediv(): with_rtruediv = With_rtruediv() - f"" / with_rtruediv + "" / with_rtruediv # object.__rfloordiv__(self, other) class With_rfloordiv: @@ -824,7 +824,7 @@ class With_rfloordiv: def test_rfloordiv(): with_rfloordiv = With_rfloordiv() - f"" // with_rfloordiv + "" // with_rfloordiv # object.__rmod__(self, other) class With_rmod: @@ -846,7 +846,7 @@ class With_rdivmod: def test_rdivmod(): with_rdivmod = With_rdivmod() - divmod(f"", with_rdivmod) + divmod("", with_rdivmod) # object.__rpow__(self, other[, modulo]) class With_rpow: @@ -857,11 +857,11 @@ class With_rpow: def test_rpow(): with_rpow = With_rpow() - pow(f"", with_rpow) + pow("", with_rpow) def test_rpow_op(): with_rpow = With_rpow() - f"" ** with_rpow + "" ** with_rpow # object.__rlshift__(self, other) class With_rlshift: @@ -872,7 +872,7 @@ class With_rlshift: def ftest_rlshift(): # TypeError: unsupported operand type(s) for >>: 'str' and 'With_rlshift' with_rlshift = With_rlshift() - f"" >> with_rlshift + "" >> with_rlshift # object.__rrshift__(self, other) class With_rrshift: @@ -883,7 +883,7 @@ class With_rrshift: def ftest_rrshift(): # TypeError: unsupported operand type(s) for <<: 'str' and 'With_rrshift' with_rrshift = With_rrshift() - f"" << with_rrshift + "" << with_rrshift # object.__rand__(self, other) class With_rand: @@ -894,7 +894,7 @@ class With_rand: def test_rand(): with_rand = With_rand() - f"" & with_rand + "" & with_rand # object.__rxor__(self, other) class With_rxor: @@ -905,7 +905,7 @@ class With_rxor: def test_rxor(): with_rxor = With_rxor() - f"" ^ with_rxor + "" ^ with_rxor # object.__ror__(self, other) class With_ror: @@ -916,7 +916,7 @@ class With_ror: def test_ror(): with_ror = With_ror() - f"" | with_ror + "" | with_ror # object.__iadd__(self, other) class With_iadd: diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index 83773c878c7..23d7c73ac30 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -1,81 +1,112 @@ -| classes.py:29:10:29:10 | ControlFlowNode for a | classes.py:17:10:17:10 | SSA variable x | -| classes.py:29:10:29:10 | ControlFlowNode for a | classes.py:31:6:31:17 | ControlFlowNode for f() | -| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:31:6:31:17 | ControlFlowNode for f() | -| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | -| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:28:7:28:7 | SSA variable a | -| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | -| classes.py:31:16:31:16 | ControlFlowNode for IntegerLiteral | classes.py:28:10:28:10 | SSA variable b | -| classes.py:39:16:39:16 | ControlFlowNode for x | classes.py:17:10:17:10 | SSA variable x | -| classes.py:39:16:39:16 | ControlFlowNode for x | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | -| classes.py:39:16:39:16 | ControlFlowNode for x | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | -| classes.py:43:16:43:16 | ControlFlowNode for x | classes.py:17:10:17:10 | SSA variable x | -| classes.py:43:16:43:16 | ControlFlowNode for x | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | -| classes.py:43:16:43:16 | ControlFlowNode for x | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | -| classes.py:56:14:56:14 | ControlFlowNode for x | classes.py:100:18:100:31 | ControlFlowNode for Attribute() | -| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | -| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | -| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:37:16:37:19 | SSA variable self | -| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:37:22:37:22 | SSA variable x | -| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | -| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:17:10:17:10 | SSA variable x | -| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:37:22:37:22 | SSA variable x | -| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:37:25:37:27 | SSA variable cls | -| classes.py:64:23:64:23 | ControlFlowNode for C | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | -| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | -| classes.py:65:15:65:15 | ControlFlowNode for c | classes.py:37:16:37:19 | SSA variable self | -| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | -| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:37:22:37:22 | SSA variable x | -| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | -| classes.py:65:26:65:26 | ControlFlowNode for C | classes.py:37:25:37:27 | SSA variable cls | -| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | -| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | -| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | -| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:42:21:42:23 | SSA variable cls | -| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:42:26:42:26 | SSA variable x | -| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | -| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | -| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:17:10:17:10 | SSA variable x | -| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:42:21:42:23 | SSA variable cls | -| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:42:26:42:26 | SSA variable x | -| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | -| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | -| classes.py:85:12:85:17 | ControlFlowNode for SOURCE | classes.py:79:9:79:9 | SSA variable x | -| classes.py:85:20:85:20 | ControlFlowNode for IntegerLiteral | classes.py:79:12:79:16 | SSA variable count | -| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | -| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | classes.py:20:12:20:12 | SSA variable x | -| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | -| classes.py:89:15:89:20 | ControlFlowNode for SOURCE | classes.py:49:13:49:16 | SSA variable self | -| classes.py:89:15:89:20 | ControlFlowNode for SOURCE | classes.py:49:19:49:19 | SSA variable x | -| classes.py:89:23:89:23 | ControlFlowNode for IntegerLiteral | classes.py:49:19:49:19 | SSA variable x | -| classes.py:89:23:89:23 | ControlFlowNode for IntegerLiteral | classes.py:49:22:49:26 | SSA variable count | -| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | -| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | classes.py:20:12:20:12 | SSA variable x | -| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | -| classes.py:96:10:96:10 | ControlFlowNode for x | classes.py:99:18:99:29 | ControlFlowNode for coro() | -| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | -| classes.py:99:23:99:28 | ControlFlowNode for SOURCE | classes.py:95:16:95:16 | SSA variable x | -| classes.py:99:23:99:28 | ControlFlowNode for SOURCE | classes.py:99:18:99:29 | ControlFlowNode for coro() | -| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | -| classes.py:100:25:100:30 | ControlFlowNode for SOURCE | classes.py:55:20:55:23 | SSA variable self | -| classes.py:100:25:100:30 | ControlFlowNode for SOURCE | classes.py:55:26:55:26 | SSA variable x | -| classes.py:100:25:100:30 | ControlFlowNode for SOURCE | classes.py:100:18:100:31 | ControlFlowNode for Attribute() | -| classes.py:110:10:110:16 | ControlFlowNode for Await | classes.py:112:18:112:29 | ControlFlowNode for agen() | -| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | classes.py:17:10:17:10 | SSA variable x | -| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | -| classes.py:112:23:112:28 | ControlFlowNode for SOURCE | classes.py:108:16:108:16 | SSA variable x | -| classes.py:142:12:142:31 | ControlFlowNode for Attribute() | classes.py:142:12:142:31 | ControlFlowNode for Attribute() | -| classes.py:150:6:150:17 | ControlFlowNode for Attribute | classes.py:17:10:17:10 | SSA variable x | -| classes.py:151:8:151:19 | ControlFlowNode for Attribute | classes.py:20:12:20:12 | SSA variable x | -| classes.py:152:6:152:17 | ControlFlowNode for Attribute | classes.py:17:10:17:10 | SSA variable x | -| classes.py:153:6:153:17 | ControlFlowNode for Attribute | classes.py:17:10:17:10 | SSA variable x | +| classes.py:21:12:21:78 | ControlFlowNode for BoolExpr | classes.py:24:8:24:19 | ControlFlowNode for is_source() | +| classes.py:21:12:21:78 | ControlFlowNode for BoolExpr | classes.py:30:8:30:19 | ControlFlowNode for is_source() | +| classes.py:24:18:24:18 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | +| classes.py:30:18:30:18 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | +| classes.py:41:10:41:10 | ControlFlowNode for a | classes.py:20:15:20:15 | SSA variable x | +| classes.py:41:10:41:10 | ControlFlowNode for a | classes.py:23:10:23:10 | SSA variable x | +| classes.py:41:10:41:10 | ControlFlowNode for a | classes.py:43:6:43:17 | ControlFlowNode for f() | +| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:43:6:43:17 | ControlFlowNode for f() | +| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | +| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | +| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:40:7:40:7 | SSA variable a | +| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | +| classes.py:43:16:43:16 | ControlFlowNode for IntegerLiteral | classes.py:40:10:40:10 | SSA variable b | +| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | +| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:23:10:23:10 | SSA variable x | +| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | +| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | +| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | +| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:23:10:23:10 | SSA variable x | +| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | +| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | +| classes.py:68:14:68:14 | ControlFlowNode for x | classes.py:112:18:112:31 | ControlFlowNode for Attribute() | +| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | +| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | +| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | +| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:49:16:49:19 | SSA variable self | +| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:49:22:49:22 | SSA variable x | +| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | +| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:20:15:20:15 | SSA variable x | +| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:23:10:23:10 | SSA variable x | +| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:49:22:49:22 | SSA variable x | +| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:49:25:49:27 | SSA variable cls | +| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | +| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | +| classes.py:77:15:77:15 | ControlFlowNode for c | classes.py:49:16:49:19 | SSA variable self | +| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | +| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | +| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:49:22:49:22 | SSA variable x | +| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | +| classes.py:77:26:77:26 | ControlFlowNode for C | classes.py:49:25:49:27 | SSA variable cls | +| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | +| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | +| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | +| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | +| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:54:21:54:23 | SSA variable cls | +| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:54:26:54:26 | SSA variable x | +| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | +| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | +| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | +| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | +| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:54:21:54:23 | SSA variable cls | +| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:54:26:54:26 | SSA variable x | +| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | +| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | +| classes.py:97:12:97:17 | ControlFlowNode for SOURCE | classes.py:91:9:91:9 | SSA variable x | +| classes.py:97:20:97:20 | ControlFlowNode for IntegerLiteral | classes.py:91:12:91:16 | SSA variable count | +| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | +| classes.py:101:15:101:20 | ControlFlowNode for SOURCE | classes.py:61:13:61:16 | SSA variable self | +| classes.py:101:15:101:20 | ControlFlowNode for SOURCE | classes.py:61:19:61:19 | SSA variable x | +| classes.py:101:23:101:23 | ControlFlowNode for IntegerLiteral | classes.py:61:19:61:19 | SSA variable x | +| classes.py:101:23:101:23 | ControlFlowNode for IntegerLiteral | classes.py:61:22:61:26 | SSA variable count | +| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | +| classes.py:108:10:108:10 | ControlFlowNode for x | classes.py:111:18:111:29 | ControlFlowNode for coro() | +| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | +| classes.py:111:23:111:28 | ControlFlowNode for SOURCE | classes.py:107:16:107:16 | SSA variable x | +| classes.py:111:23:111:28 | ControlFlowNode for SOURCE | classes.py:111:18:111:29 | ControlFlowNode for coro() | +| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | +| classes.py:112:25:112:30 | ControlFlowNode for SOURCE | classes.py:67:20:67:23 | SSA variable self | +| classes.py:112:25:112:30 | ControlFlowNode for SOURCE | classes.py:67:26:67:26 | SSA variable x | +| classes.py:112:25:112:30 | ControlFlowNode for SOURCE | classes.py:112:18:112:31 | ControlFlowNode for Attribute() | +| classes.py:122:10:122:16 | ControlFlowNode for Await | classes.py:124:18:124:29 | ControlFlowNode for agen() | +| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | +| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | +| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | +| classes.py:124:23:124:28 | ControlFlowNode for SOURCE | classes.py:120:16:120:16 | SSA variable x | +| classes.py:154:12:154:31 | ControlFlowNode for Attribute() | classes.py:154:12:154:31 | ControlFlowNode for Attribute() | +| classes.py:161:6:161:17 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | +| classes.py:161:6:161:17 | ControlFlowNode for Attribute | classes.py:23:10:23:10 | SSA variable x | +| classes.py:162:8:162:19 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | +| classes.py:162:8:162:19 | ControlFlowNode for Attribute | classes.py:29:12:29:12 | SSA variable x | +| classes.py:163:6:163:17 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | +| classes.py:163:6:163:17 | ControlFlowNode for Attribute | classes.py:23:10:23:10 | SSA variable x | +| classes.py:164:6:164:17 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | +| classes.py:164:6:164:17 | ControlFlowNode for Attribute | classes.py:23:10:23:10 | SSA variable x | +| classes.py:174:12:174:31 | ControlFlowNode for Attribute() | classes.py:174:12:174:31 | ControlFlowNode for Attribute() | +| classes.py:329:7:329:22 | ControlFlowNode for set() | classes.py:329:7:329:22 | ControlFlowNode for set() | +| classes.py:333:7:333:28 | ControlFlowNode for frozenset() | classes.py:333:7:333:28 | ControlFlowNode for frozenset() | +| classes.py:337:7:337:26 | ControlFlowNode for dict() | classes.py:337:7:337:26 | ControlFlowNode for dict() | +| classes.py:458:28:458:51 | ControlFlowNode for dict() | classes.py:458:28:458:51 | ControlFlowNode for dict() | +| classes.py:575:12:575:24 | ControlFlowNode for Attribute() | classes.py:575:12:575:24 | ControlFlowNode for Attribute() | diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.expected b/python/ql/test/experimental/dataflow/coverage/localFlow.expected index eee5e093f5e..9e4dc40eafc 100644 --- a/python/ql/test/experimental/dataflow/coverage/localFlow.expected +++ b/python/ql/test/experimental/dataflow/coverage/localFlow.expected @@ -1,7 +1,7 @@ -| test.py:24:5:24:5 | SSA variable x | test.py:23:1:23:33 | Exit node for Function test_tuple_with_local_flow | -| test.py:24:5:24:5 | SSA variable x | test.py:25:9:25:9 | ControlFlowNode for x | -| test.py:24:10:24:26 | ControlFlowNode for Tuple | test.py:24:5:24:5 | SSA variable x | -| test.py:25:5:25:5 | SSA variable y | test.py:26:5:26:11 | SSA variable y | -| test.py:25:5:25:5 | SSA variable y | test.py:26:10:26:10 | ControlFlowNode for y | -| test.py:25:9:25:12 | ControlFlowNode for Subscript | test.py:25:5:25:5 | SSA variable y | -| test.py:26:5:26:11 | SSA variable y | test.py:23:1:23:33 | Exit node for Function test_tuple_with_local_flow | +| test.py:32:5:32:5 | SSA variable x | test.py:31:1:31:33 | Exit node for Function test_tuple_with_local_flow | +| test.py:32:5:32:5 | SSA variable x | test.py:33:9:33:9 | ControlFlowNode for x | +| test.py:32:10:32:26 | ControlFlowNode for Tuple | test.py:32:5:32:5 | SSA variable x | +| test.py:33:5:33:5 | SSA variable y | test.py:34:5:34:11 | SSA variable y | +| test.py:33:5:33:5 | SSA variable y | test.py:34:10:34:10 | ControlFlowNode for y | +| test.py:33:9:33:12 | ControlFlowNode for Subscript | test.py:33:5:33:5 | SSA variable y | +| test.py:34:5:34:11 | SSA variable y | test.py:31:1:31:33 | Exit node for Function test_tuple_with_local_flow | From 5da37f5cf4c0797feb8b1394d425c30e1309e4c7 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 10 Aug 2020 17:07:00 +0200 Subject: [PATCH 18/27] Python: Update test expectations --- .../experimental/dataflow/coverage/classes.py | 6 +- .../dataflow/coverage/dataflow.expected | 339 +++++++++--------- 2 files changed, 165 insertions(+), 180 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index f3b3e013b22..3f47b69c943 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1197,7 +1197,7 @@ class With_trunc: def test_trunc(): with_trunc = With_trunc() import math - math.trunc(with_trunc) # edit to effect call + math.trunc(with_trunc) # object.__floor__(self) class With_floor: @@ -1209,7 +1209,7 @@ class With_floor: def test_floor(): with_floor = With_floor() import math - math.floor(with_floor) # edit to effect call + math.floor(with_floor) # object.__ceil__(self) class With_ceil: @@ -1221,7 +1221,7 @@ class With_ceil: def test_ceil(): with_ceil = With_ceil() import math - math.ceil(with_ceil) # edit to effect call + math.ceil(with_ceil) # 3.3.9. With Statement Context Managers diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 6713969a2e4..700da23c5ef 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -1,182 +1,167 @@ edges -| classes.py:16:1:16:6 | GSSA Variable SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | -| classes.py:16:1:16:6 | GSSA Variable SOURCE | classes.py:31:6:31:17 | GSSA Variable SOURCE | -| classes.py:16:1:16:6 | GSSA Variable SOURCE | classes.py:31:8:31:13 | ControlFlowNode for SOURCE | -| classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:16:1:16:6 | GSSA Variable SOURCE | -| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:58:5:58:7 | ControlFlowNode for C() | -| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | -| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:64:6:64:24 | GSSA Variable SOURCE | -| classes.py:31:6:31:17 | GSSA Variable SOURCE | classes.py:64:15:64:20 | ControlFlowNode for SOURCE | -| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | -| classes.py:58:1:58:1 | GSSA Variable c | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | -| classes.py:58:1:58:1 | GSSA Variable c | classes.py:64:6:64:24 | GSSA Variable c | -| classes.py:58:5:58:7 | ControlFlowNode for C() | classes.py:58:1:58:1 | GSSA Variable c | -| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | -| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:65:18:65:23 | ControlFlowNode for SOURCE | -| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | -| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | -| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:73:6:73:26 | GSSA Variable SOURCE | -| classes.py:64:6:64:24 | GSSA Variable SOURCE | classes.py:73:20:73:25 | ControlFlowNode for SOURCE | -| classes.py:64:6:64:24 | GSSA Variable c | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | -| classes.py:64:6:64:24 | GSSA Variable c | classes.py:65:6:65:27 | GSSA Variable c | -| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | -| classes.py:65:6:65:27 | GSSA Variable c | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | -| classes.py:65:6:65:27 | GSSA Variable c | classes.py:66:6:66:27 | GSSA Variable c | -| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | -| classes.py:66:6:66:27 | GSSA Variable c | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | -| classes.py:66:6:66:27 | GSSA Variable c | classes.py:73:6:73:26 | GSSA Variable c | -| classes.py:73:6:73:26 | GSSA Variable SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | -| classes.py:73:6:73:26 | GSSA Variable SOURCE | classes.py:74:6:74:26 | GSSA Variable SOURCE | -| classes.py:73:6:73:26 | GSSA Variable SOURCE | classes.py:74:20:74:25 | ControlFlowNode for SOURCE | -| classes.py:73:6:73:26 | GSSA Variable c | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | -| classes.py:73:6:73:26 | GSSA Variable c | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | -| classes.py:73:6:73:26 | GSSA Variable c | classes.py:85:8:85:21 | ControlFlowNode for gen() | -| classes.py:73:6:73:26 | GSSA Variable c | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | -| classes.py:73:6:73:26 | GSSA Variable c | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | -| classes.py:73:6:73:26 | GSSA Variable c | classes.py:89:9:89:24 | ControlFlowNode for Attribute() | -| classes.py:73:6:73:26 | GSSA Variable c | classes.py:89:9:89:24 | GSSA Variable c | -| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | -| classes.py:74:6:74:26 | GSSA Variable SOURCE | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | -| classes.py:74:6:74:26 | GSSA Variable SOURCE | classes.py:85:8:85:21 | ControlFlowNode for gen() | -| classes.py:74:6:74:26 | GSSA Variable SOURCE | classes.py:85:8:85:21 | GSSA Variable SOURCE | -| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | -| classes.py:85:1:85:4 | GSSA Variable iter | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | -| classes.py:85:1:85:4 | GSSA Variable iter | classes.py:86:6:86:20 | GSSA Variable iter | -| classes.py:85:8:85:21 | ControlFlowNode for gen() | classes.py:85:1:85:4 | GSSA Variable iter | -| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | -| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | -| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:89:9:89:24 | ControlFlowNode for Attribute() | -| classes.py:85:8:85:21 | GSSA Variable SOURCE | classes.py:89:9:89:24 | GSSA Variable SOURCE | -| classes.py:86:6:86:20 | GSSA Variable iter | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | -| classes.py:86:6:86:20 | GSSA Variable iter | classes.py:87:8:87:22 | GSSA Variable iter | -| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:89:9:89:24 | ControlFlowNode for Attribute() | -| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | -| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | -| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | -| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | -| classes.py:87:8:87:22 | GSSA Variable iter | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | -| classes.py:89:1:89:5 | GSSA Variable oiter | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | -| classes.py:89:1:89:5 | GSSA Variable oiter | classes.py:90:6:90:21 | GSSA Variable oiter | -| classes.py:89:9:89:24 | ControlFlowNode for Attribute() | classes.py:89:1:89:5 | GSSA Variable oiter | -| classes.py:89:9:89:24 | GSSA Variable SOURCE | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | -| classes.py:89:9:89:24 | GSSA Variable SOURCE | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | -| classes.py:89:9:89:24 | GSSA Variable SOURCE | classes.py:99:18:99:29 | GSSA Variable SOURCE | -| classes.py:89:9:89:24 | GSSA Variable c | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | -| classes.py:89:9:89:24 | GSSA Variable c | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | -| classes.py:89:9:89:24 | GSSA Variable c | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | -| classes.py:89:9:89:24 | GSSA Variable c | classes.py:100:18:100:31 | GSSA Variable c | -| classes.py:90:6:90:21 | GSSA Variable oiter | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | -| classes.py:90:6:90:21 | GSSA Variable oiter | classes.py:91:8:91:23 | GSSA Variable oiter | -| classes.py:91:8:91:23 | GSSA Variable oiter | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | -| classes.py:91:8:91:23 | GSSA Variable oiter | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | -| classes.py:91:8:91:23 | GSSA Variable oiter | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | -| classes.py:99:18:99:29 | GSSA Variable SOURCE | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | -| classes.py:99:18:99:29 | GSSA Variable SOURCE | classes.py:100:18:100:31 | GSSA Variable SOURCE | -| classes.py:100:18:100:31 | GSSA Variable SOURCE | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | -| classes.py:100:18:100:31 | GSSA Variable SOURCE | classes.py:112:18:112:29 | GSSA Variable SOURCE | -| classes.py:100:18:100:31 | GSSA Variable c | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | -| classes.py:100:18:100:31 | GSSA Variable c | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | -| classes.py:112:18:112:29 | GSSA Variable SOURCE | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | -| test.py:35:9:35:14 | ControlFlowNode for SOURCE | test.py:36:10:36:10 | ControlFlowNode for x | -| test.py:40:9:40:16 | ControlFlowNode for Str | test.py:41:10:41:10 | ControlFlowNode for x | -| test.py:44:9:44:17 | ControlFlowNode for Str | test.py:45:10:45:10 | ControlFlowNode for x | -| test.py:48:9:48:10 | ControlFlowNode for IntegerLiteral | test.py:49:10:49:10 | ControlFlowNode for x | -| test.py:52:9:52:12 | ControlFlowNode for FloatLiteral | test.py:53:10:53:10 | ControlFlowNode for x | -| test.py:61:10:61:15 | ControlFlowNode for SOURCE | test.py:62:10:62:10 | ControlFlowNode for x | -| test.py:238:28:238:33 | ControlFlowNode for SOURCE | test.py:238:10:238:34 | ControlFlowNode for second() | -| test.py:297:12:297:17 | ControlFlowNode for SOURCE | test.py:297:10:297:18 | ControlFlowNode for f() | -| test.py:301:28:301:33 | ControlFlowNode for SOURCE | test.py:301:10:301:34 | ControlFlowNode for second() | +| classes.py:18:1:18:6 | GSSA Variable SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | +| classes.py:18:1:18:6 | GSSA Variable SOURCE | classes.py:43:6:43:17 | GSSA Variable SOURCE | +| classes.py:18:1:18:6 | GSSA Variable SOURCE | classes.py:43:8:43:13 | ControlFlowNode for SOURCE | +| classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:18:1:18:6 | GSSA Variable SOURCE | +| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:70:5:70:7 | ControlFlowNode for C() | +| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | +| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:76:6:76:24 | GSSA Variable SOURCE | +| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:76:15:76:20 | ControlFlowNode for SOURCE | +| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | +| classes.py:70:1:70:1 | GSSA Variable c | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | +| classes.py:70:1:70:1 | GSSA Variable c | classes.py:76:6:76:24 | GSSA Variable c | +| classes.py:70:5:70:7 | ControlFlowNode for C() | classes.py:70:1:70:1 | GSSA Variable c | +| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | +| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:77:18:77:23 | ControlFlowNode for SOURCE | +| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | +| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | +| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:85:6:85:26 | GSSA Variable SOURCE | +| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:85:20:85:25 | ControlFlowNode for SOURCE | +| classes.py:76:6:76:24 | GSSA Variable c | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | +| classes.py:76:6:76:24 | GSSA Variable c | classes.py:77:6:77:27 | GSSA Variable c | +| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | +| classes.py:77:6:77:27 | GSSA Variable c | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | +| classes.py:77:6:77:27 | GSSA Variable c | classes.py:78:6:78:27 | GSSA Variable c | +| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | +| classes.py:78:6:78:27 | GSSA Variable c | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | +| classes.py:78:6:78:27 | GSSA Variable c | classes.py:85:6:85:26 | GSSA Variable c | +| classes.py:85:6:85:26 | GSSA Variable SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | +| classes.py:85:6:85:26 | GSSA Variable SOURCE | classes.py:86:6:86:26 | GSSA Variable SOURCE | +| classes.py:85:6:85:26 | GSSA Variable SOURCE | classes.py:86:20:86:25 | ControlFlowNode for SOURCE | +| classes.py:85:6:85:26 | GSSA Variable c | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | +| classes.py:85:6:85:26 | GSSA Variable c | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | +| classes.py:85:6:85:26 | GSSA Variable c | classes.py:97:8:97:21 | ControlFlowNode for gen() | +| classes.py:85:6:85:26 | GSSA Variable c | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | +| classes.py:85:6:85:26 | GSSA Variable c | classes.py:101:9:101:24 | ControlFlowNode for Attribute() | +| classes.py:85:6:85:26 | GSSA Variable c | classes.py:101:9:101:24 | GSSA Variable c | +| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | +| classes.py:86:6:86:26 | GSSA Variable SOURCE | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | +| classes.py:86:6:86:26 | GSSA Variable SOURCE | classes.py:97:8:97:21 | ControlFlowNode for gen() | +| classes.py:86:6:86:26 | GSSA Variable SOURCE | classes.py:97:8:97:21 | GSSA Variable SOURCE | +| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | +| classes.py:97:1:97:4 | GSSA Variable iter | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | +| classes.py:97:1:97:4 | GSSA Variable iter | classes.py:98:6:98:20 | GSSA Variable iter | +| classes.py:97:8:97:21 | ControlFlowNode for gen() | classes.py:97:1:97:4 | GSSA Variable iter | +| classes.py:97:8:97:21 | GSSA Variable SOURCE | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | +| classes.py:97:8:97:21 | GSSA Variable SOURCE | classes.py:101:9:101:24 | ControlFlowNode for Attribute() | +| classes.py:97:8:97:21 | GSSA Variable SOURCE | classes.py:101:9:101:24 | GSSA Variable SOURCE | +| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:101:9:101:24 | ControlFlowNode for Attribute() | +| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | +| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | +| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | +| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | +| classes.py:101:1:101:5 | GSSA Variable oiter | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | +| classes.py:101:1:101:5 | GSSA Variable oiter | classes.py:102:6:102:21 | GSSA Variable oiter | +| classes.py:101:9:101:24 | ControlFlowNode for Attribute() | classes.py:101:1:101:5 | GSSA Variable oiter | +| classes.py:101:9:101:24 | GSSA Variable SOURCE | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | +| classes.py:101:9:101:24 | GSSA Variable SOURCE | classes.py:111:18:111:29 | GSSA Variable SOURCE | +| classes.py:101:9:101:24 | GSSA Variable c | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | +| classes.py:101:9:101:24 | GSSA Variable c | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | +| classes.py:101:9:101:24 | GSSA Variable c | classes.py:112:18:112:31 | GSSA Variable c | +| classes.py:102:6:102:21 | GSSA Variable oiter | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | +| classes.py:102:6:102:21 | GSSA Variable oiter | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | +| classes.py:102:6:102:21 | GSSA Variable oiter | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | +| classes.py:111:18:111:29 | GSSA Variable SOURCE | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | +| classes.py:111:18:111:29 | GSSA Variable SOURCE | classes.py:112:18:112:31 | GSSA Variable SOURCE | +| classes.py:112:18:112:31 | GSSA Variable SOURCE | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | +| classes.py:112:18:112:31 | GSSA Variable SOURCE | classes.py:124:18:124:29 | GSSA Variable SOURCE | +| classes.py:112:18:112:31 | GSSA Variable c | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | +| classes.py:112:18:112:31 | GSSA Variable c | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | +| classes.py:124:18:124:29 | GSSA Variable SOURCE | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | +| test.py:43:9:43:14 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for x | +| test.py:48:9:48:16 | ControlFlowNode for Str | test.py:49:10:49:10 | ControlFlowNode for x | +| test.py:52:9:52:17 | ControlFlowNode for Str | test.py:53:10:53:10 | ControlFlowNode for x | +| test.py:56:9:56:10 | ControlFlowNode for IntegerLiteral | test.py:57:10:57:10 | ControlFlowNode for x | +| test.py:60:9:60:12 | ControlFlowNode for FloatLiteral | test.py:61:10:61:10 | ControlFlowNode for x | +| test.py:69:10:69:15 | ControlFlowNode for SOURCE | test.py:70:10:70:10 | ControlFlowNode for x | +| test.py:246:28:246:33 | ControlFlowNode for SOURCE | test.py:246:10:246:34 | ControlFlowNode for second() | +| test.py:305:12:305:17 | ControlFlowNode for SOURCE | test.py:305:10:305:18 | ControlFlowNode for f() | +| test.py:309:28:309:33 | ControlFlowNode for SOURCE | test.py:309:10:309:34 | ControlFlowNode for second() | nodes -| classes.py:16:1:16:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:16:10:16:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| classes.py:31:6:31:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| classes.py:31:6:31:17 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:31:8:31:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:58:1:58:1 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:58:5:58:7 | ControlFlowNode for C() | semmle.label | ControlFlowNode for C() | -| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:64:6:64:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:64:6:64:24 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:64:15:64:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:65:6:65:27 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:65:18:65:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | semmle.label | ControlFlowNode for func_obj() | -| classes.py:66:6:66:27 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:73:6:73:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:73:6:73:26 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:73:20:73:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:74:6:74:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:74:20:74:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | semmle.label | ControlFlowNode for c_func_obj() | -| classes.py:85:1:85:4 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:85:8:85:21 | ControlFlowNode for gen() | semmle.label | ControlFlowNode for gen() | -| classes.py:85:8:85:21 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:86:6:86:20 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:87:8:87:22 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:89:1:89:5 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:89:9:89:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:89:9:89:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:89:9:89:24 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:90:6:90:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:91:8:91:23 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:99:18:99:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:100:18:100:31 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:100:18:100:31 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:112:18:112:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| test.py:35:9:35:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:36:10:36:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:40:9:40:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:41:10:41:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:44:9:44:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:45:10:45:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:48:9:48:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral | +| classes.py:18:1:18:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:18:10:18:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| classes.py:43:6:43:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | +| classes.py:43:6:43:17 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:70:1:70:1 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:70:5:70:7 | ControlFlowNode for C() | semmle.label | ControlFlowNode for C() | +| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:76:6:76:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:76:6:76:24 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:77:6:77:27 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | semmle.label | ControlFlowNode for func_obj() | +| classes.py:78:6:78:27 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:85:6:85:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:85:6:85:26 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:86:6:86:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | semmle.label | ControlFlowNode for c_func_obj() | +| classes.py:97:1:97:4 | GSSA Variable iter | semmle.label | GSSA Variable iter | +| classes.py:97:8:97:21 | ControlFlowNode for gen() | semmle.label | ControlFlowNode for gen() | +| classes.py:97:8:97:21 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:98:6:98:20 | GSSA Variable iter | semmle.label | GSSA Variable iter | +| classes.py:101:1:101:5 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | +| classes.py:101:9:101:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:101:9:101:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:101:9:101:24 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:102:6:102:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | +| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:111:18:111:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:112:18:112:31 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| classes.py:112:18:112:31 | GSSA Variable c | semmle.label | GSSA Variable c | +| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| classes.py:124:18:124:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| test.py:43:9:43:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:44:10:44:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:48:9:48:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | | test.py:49:10:49:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:52:9:52:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral | +| test.py:52:9:52:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | | test.py:53:10:53:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:61:10:61:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:62:10:62:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:238:10:238:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:238:28:238:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:297:10:297:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| test.py:297:12:297:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:301:10:301:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:301:28:301:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:56:9:56:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral | +| test.py:57:10:57:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:60:9:60:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral | +| test.py:61:10:61:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:69:10:69:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:70:10:70:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:246:10:246:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:246:28:246:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:305:10:305:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | +| test.py:305:12:305:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:309:10:309:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:309:28:309:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select -| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:31:6:31:17 | ControlFlowNode for f() | | -| classes.py:31:6:31:17 | ControlFlowNode for f() | classes.py:31:8:31:13 | ControlFlowNode for SOURCE | classes.py:31:6:31:17 | ControlFlowNode for f() | | -| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | | -| classes.py:64:6:64:24 | ControlFlowNode for Attribute() | classes.py:64:15:64:20 | ControlFlowNode for SOURCE | classes.py:64:6:64:24 | ControlFlowNode for Attribute() | | -| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | | -| classes.py:65:6:65:27 | ControlFlowNode for Attribute() | classes.py:65:18:65:23 | ControlFlowNode for SOURCE | classes.py:65:6:65:27 | ControlFlowNode for Attribute() | | -| classes.py:66:6:66:27 | ControlFlowNode for func_obj() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:66:6:66:27 | ControlFlowNode for func_obj() | | -| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | | -| classes.py:73:6:73:26 | ControlFlowNode for Attribute() | classes.py:73:20:73:25 | ControlFlowNode for SOURCE | classes.py:73:6:73:26 | ControlFlowNode for Attribute() | | -| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | | -| classes.py:74:6:74:26 | ControlFlowNode for Attribute() | classes.py:74:20:74:25 | ControlFlowNode for SOURCE | classes.py:74:6:74:26 | ControlFlowNode for Attribute() | | -| classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:75:6:75:26 | ControlFlowNode for c_func_obj() | | -| classes.py:86:6:86:20 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:86:6:86:20 | ControlFlowNode for Attribute() | | -| classes.py:87:8:87:22 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:87:8:87:22 | ControlFlowNode for Attribute() | | -| classes.py:90:6:90:21 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:90:6:90:21 | ControlFlowNode for Attribute() | | -| classes.py:91:8:91:23 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:91:8:91:23 | ControlFlowNode for Attribute() | | -| classes.py:99:6:99:30 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:99:6:99:30 | ControlFlowNode for Attribute() | | -| classes.py:100:6:100:32 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:100:6:100:32 | ControlFlowNode for Attribute() | | -| classes.py:112:6:112:30 | ControlFlowNode for Attribute() | classes.py:16:10:16:17 | ControlFlowNode for Str | classes.py:112:6:112:30 | ControlFlowNode for Attribute() | | -| test.py:36:10:36:10 | ControlFlowNode for x | test.py:35:9:35:14 | ControlFlowNode for SOURCE | test.py:36:10:36:10 | ControlFlowNode for x | | -| test.py:41:10:41:10 | ControlFlowNode for x | test.py:40:9:40:16 | ControlFlowNode for Str | test.py:41:10:41:10 | ControlFlowNode for x | | -| test.py:45:10:45:10 | ControlFlowNode for x | test.py:44:9:44:17 | ControlFlowNode for Str | test.py:45:10:45:10 | ControlFlowNode for x | | -| test.py:49:10:49:10 | ControlFlowNode for x | test.py:48:9:48:10 | ControlFlowNode for IntegerLiteral | test.py:49:10:49:10 | ControlFlowNode for x | | -| test.py:53:10:53:10 | ControlFlowNode for x | test.py:52:9:52:12 | ControlFlowNode for FloatLiteral | test.py:53:10:53:10 | ControlFlowNode for x | | -| test.py:62:10:62:10 | ControlFlowNode for x | test.py:61:10:61:15 | ControlFlowNode for SOURCE | test.py:62:10:62:10 | ControlFlowNode for x | | -| test.py:238:10:238:34 | ControlFlowNode for second() | test.py:238:28:238:33 | ControlFlowNode for SOURCE | test.py:238:10:238:34 | ControlFlowNode for second() | | -| test.py:297:10:297:18 | ControlFlowNode for f() | test.py:297:12:297:17 | ControlFlowNode for SOURCE | test.py:297:10:297:18 | ControlFlowNode for f() | | -| test.py:301:10:301:34 | ControlFlowNode for second() | test.py:301:28:301:33 | ControlFlowNode for SOURCE | test.py:301:10:301:34 | ControlFlowNode for second() | | +| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:43:6:43:17 | ControlFlowNode for f() | | +| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | | +| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | | +| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | | +| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | | +| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | | +| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | | +| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | | +| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | | +| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | | +| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | | +| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | | +| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | | +| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | | +| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | | +| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | | +| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | | +| test.py:44:10:44:10 | ControlFlowNode for x | test.py:43:9:43:14 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for x | | +| test.py:49:10:49:10 | ControlFlowNode for x | test.py:48:9:48:16 | ControlFlowNode for Str | test.py:49:10:49:10 | ControlFlowNode for x | | +| test.py:53:10:53:10 | ControlFlowNode for x | test.py:52:9:52:17 | ControlFlowNode for Str | test.py:53:10:53:10 | ControlFlowNode for x | | +| test.py:57:10:57:10 | ControlFlowNode for x | test.py:56:9:56:10 | ControlFlowNode for IntegerLiteral | test.py:57:10:57:10 | ControlFlowNode for x | | +| test.py:61:10:61:10 | ControlFlowNode for x | test.py:60:9:60:12 | ControlFlowNode for FloatLiteral | test.py:61:10:61:10 | ControlFlowNode for x | | +| test.py:70:10:70:10 | ControlFlowNode for x | test.py:69:10:69:15 | ControlFlowNode for SOURCE | test.py:70:10:70:10 | ControlFlowNode for x | | +| test.py:246:10:246:34 | ControlFlowNode for second() | test.py:246:28:246:33 | ControlFlowNode for SOURCE | test.py:246:10:246:34 | ControlFlowNode for second() | | +| test.py:305:10:305:18 | ControlFlowNode for f() | test.py:305:12:305:17 | ControlFlowNode for SOURCE | test.py:305:10:305:18 | ControlFlowNode for f() | | +| test.py:309:10:309:34 | ControlFlowNode for second() | test.py:309:28:309:33 | ControlFlowNode for SOURCE | test.py:309:10:309:34 | ControlFlowNode for second() | | From 3929e013505a0a58f3469cfe625990e3a68d1617 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 11 Aug 2020 08:10:46 +0200 Subject: [PATCH 19/27] Python: tests for async iterators/context managers --- .../experimental/dataflow/coverage/classes.py | 82 +++++++++++-------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 3f47b69c943..96c04c09072 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1271,49 +1271,63 @@ async def atest_await(): # # coroutine.throw(type[, value[, traceback]]) # # coroutine.close() -# # 3.4.3. Asynchronous Iterators -# # object.__aiter__(self) -# class With_aiter: +# 3.4.3. Asynchronous Iterators +# object.__aiter__(self) +class With_aiter: -# def __aiter__(self): -# OK() -# return "" # edit to match type + def __aiter__(self): + OK() + return self -# def test_aiter(): -# with_aiter = With_aiter() -# aiter(with_aiter) # edit to effect call + async def __anext__(self): + raise StopAsyncIteration -# # object.__anext__(self) -# class With_anext: +async def atest_aiter(): + with_aiter = With_aiter() + async for x in with_aiter: + pass -# def __anext__(self): -# OK() -# return "" # edit to match type +# object.__anext__(self) +class With_anext: -# def test_anext(): -# with_anext = With_anext() -# anext(with_anext) # edit to effect call + def __aiter__(self): + return self + + async def __anext__(self): + OK() + raise StopAsyncIteration + +async def atest_anext(): + with_anext = With_anext() + async for x in with_anext: + pass -# # 3.4.4. Asynchronous Context Managers -# # object.__aenter__(self) -# class With_aenter: +# 3.4.4. Asynchronous Context Managers +# object.__aenter__(self) +class With_aenter: -# def __aenter__(self): -# OK() -# return "" # edit to match type + async def __aenter__(self): + OK() -# def test_aenter(): -# with_aenter = With_aenter() -# aenter(with_aenter) # edit to effect call + async def __aexit__(self, exc_type, exc_value, traceback): + pass -# # object.__aexit__(self, exc_type, exc_value, traceback) -# class With_aexit: +async def atest_aenter(): + with_aenter = With_aenter() + async with with_aenter: + pass -# def __aexit__(self, exc_type, exc_value, traceback): -# OK() -# return "" # edit to match type +# object.__aexit__(self, exc_type, exc_value, traceback) +class With_aexit: -# def test_aexit(): -# with_aexit = With_aexit() -# aexit(with_aexit) # edit to effect call + async def __aenter__(self): + pass + + async def __aexit__(self, exc_type, exc_value, traceback): + OK() + +async def atest_aexit(): + with_aexit = With_aexit() + async with with_aexit: + pass From 12dfc4afd9673e619a7acc44205c5209f1c452bb Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 11 Aug 2020 08:16:49 +0200 Subject: [PATCH 20/27] Python: clean up validity check code --- .../ql/test/experimental/dataflow/coverage/validTest.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/validTest.py b/python/ql/test/experimental/dataflow/coverage/validTest.py index dbede541e82..cec82c47723 100644 --- a/python/ql/test/experimental/dataflow/coverage/validTest.py +++ b/python/ql/test/experimental/dataflow/coverage/validTest.py @@ -27,9 +27,7 @@ def check_async_test_function(f): sys.stdout = old_stdout check_output(capturer.getvalue(), f) -def check_classes_valid(testFile): - # import python.ql.test.experimental.dataflow.coverage.classes as tests - # import classes as tests +def check_tests_valid(testFile): import importlib tests = importlib.import_module(testFile) for i in dir(tests): @@ -47,5 +45,5 @@ def check_classes_valid(testFile): check_async_test_function(item) if __name__ == '__main__': - check_classes_valid("classes") - check_classes_valid("test") + check_tests_valid("classes") + check_tests_valid("test") From 2c5de7f50e3f5ee91d06945ffbfad74859e0e31a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 11 Aug 2020 10:48:23 +0200 Subject: [PATCH 21/27] Python: fix r/l confusion --- python/ql/test/experimental/dataflow/coverage/classes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 96c04c09072..d86e442bd92 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -870,9 +870,9 @@ class With_rlshift: OK() return self -def ftest_rlshift(): # TypeError: unsupported operand type(s) for >>: 'str' and 'With_rlshift' +def test_rlshift(): with_rlshift = With_rlshift() - "" >> with_rlshift + "" << with_rlshift # object.__rrshift__(self, other) class With_rrshift: @@ -881,9 +881,9 @@ class With_rrshift: OK() return self -def ftest_rrshift(): # TypeError: unsupported operand type(s) for <<: 'str' and 'With_rrshift' +def test_rrshift(): with_rrshift = With_rrshift() - "" << with_rrshift + "" >> with_rrshift # object.__rand__(self, other) class With_rand: From f834d71bab3c93d7a6f0cdb929fbffdb468e34a2 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 11 Aug 2020 11:22:11 +0200 Subject: [PATCH 22/27] Python: split out data model tests --- .../experimental/dataflow/coverage/classes.py | 157 +---------------- .../dataflow/coverage/datamodel.py | 159 ++++++++++++++++++ 2 files changed, 161 insertions(+), 155 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/coverage/datamodel.py diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index d86e442bd92..63147cfd6ae 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -5,164 +5,9 @@ # These tests should cover all the class calls that we hope to support. # It is based on https://docs.python.org/3/reference/datamodel.html, and headings refer there. # -# Intended sources should be the variable `SOURCE` and intended sinks should be -# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). -# -# Functions whose name ends with "_with_local_flow" will also be tested for local flow. -# # All functions starting with "test_" should run and print `"OK"`. # This can be checked by running validTest.py. -# These are defined so that we can evaluate the test code. -NONSOURCE = "not a source" -SOURCE = "source" - -def is_source(x): - return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j - -def SINK(x): - if is_source(x): - print("OK") - else: - print("Unexpected flow", x) - -def SINK_F(x): - if is_source(x): - print("Unexpected flow", x) - else: - print("OK") - -# Callable types -# These are the types to which the function call operation (see section Calls) can be applied: - -# User-defined functions -# A user-defined function object is created by a function definition (see section Function definitions). It should be called with an argument list containing the same number of items as the function's formal parameter list. -def f(a, b): - return a - -SINK(f(SOURCE, 3)) - -# Instance methods -# An instance method object combines a class, a class instance and any callable object (normally a user-defined function). -class C(object): - - def method(self, x, cls): - assert cls is self.__class__ - return x - - @classmethod - def classmethod(cls, x): - return x - - @staticmethod - def staticmethod(x): - return x - - def gen(self, x, count): - n = count - while n > 0: - yield x - n -= 1 - - async def coro(self, x): - return x - -c = C() - -# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. -func_obj = c.method.__func__ - -# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). -SINK(c.method(SOURCE, C)) -SINK(C.method(c, SOURCE, C)) -SINK(func_obj(c, SOURCE, C)) - - -# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. -c_func_obj = C.classmethod.__func__ - -# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. -SINK(c.classmethod(SOURCE)) -SINK(C.classmethod(SOURCE)) -SINK(c_func_obj(C, SOURCE)) - -# Generator functions -# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. -def gen(x, count): - n = count - while n > 0: - yield x - n -= 1 - -iter = gen(SOURCE, 1) -SINK(iter.__next__()) -# SINK_F(iter.__next__()) # throws StopIteration, FP - -oiter = c.gen(SOURCE, 1) -SINK(oiter.__next__()) -# SINK_F(oiter.__next__()) # throws StopIteration, FP - -# Coroutine functions -# A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section. -async def coro(x): - return x - -import asyncio -SINK(asyncio.run(coro(SOURCE))) -SINK(asyncio.run(c.coro(SOURCE))) - -class A: - - def __await__(self): - # yield SOURCE -- see https://groups.google.com/g/dev-python/c/_lrrc-vp9TI?pli=1 - return (yield from asyncio.coroutine(lambda: SOURCE)()) - -async def agen(x): - a = A() - return await a - -SINK(asyncio.run(agen(SOURCE))) - -# Asynchronous generator functions -# A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function. - -# Calling the asynchronous iterator’s aiterator.__anext__() method will return an awaitable which when awaited will execute until it provides a value using the yield expression. When the function executes an empty return statement or falls off the end, a StopAsyncIteration exception is raised and the asynchronous iterator will have reached the end of the set of values to be yielded. - -# Built-in functions -# A built-in function object is a wrapper around a C function. Examples of built-in functions are len() and math.sin() (math is a standard built-in module). The number and type of the arguments are determined by the C function. Special read-only attributes: __doc__ is the function’s documentation string, or None if unavailable; __name__ is the function’s name; __self__ is set to None (but see the next item); __module__ is the name of the module the function was defined in or None if unavailable. - -# Built-in methods -# This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is alist.append(), assuming alist is a list object. In this case, the special read-only attribute __self__ is set to the object denoted by alist. - -# Classes -# Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override __new__(). The arguments of the call are passed to __new__() and, in the typical case, to __init__() to initialize the new instance. - -# Class Instances -# Instances of arbitrary classes can be made callable by defining a __call__() method in their class. - -# If a class sets __iter__() to None, calling iter() on its instances will raise a TypeError (without falling back to __getitem__()). - -# 3.3.1. Basic customization - -class Customized: - - a = NONSOURCE - b = NONSOURCE - - def __new__(cls): - cls.a = SOURCE - return super().__new__(cls) - - def __init__(self): - self.b = SOURCE - -# testing __new__ and __init__ -customized = Customized() -SINK(Customized.a) -SINK_F(Customized.b) -SINK(customized.a) -SINK(customized.b) - def OK(): print("OK") @@ -1254,6 +1099,8 @@ def test_exit(): pass # 3.4.1. Awaitable Objects +import asyncio + # object.__await__(self) class With_await: diff --git a/python/ql/test/experimental/dataflow/coverage/datamodel.py b/python/ql/test/experimental/dataflow/coverage/datamodel.py new file mode 100644 index 00000000000..82f10f4d53f --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/datamodel.py @@ -0,0 +1,159 @@ +# User-defined methods, both instance methods and class methods, can be called in many non-standard ways +# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` function on a +# class `C` will be called by the syntactic construct `await c` when `c` is an instance of `C`. +# +# These tests are based on the first part of https://docs.python.org/3/reference/datamodel.html. +# A thorough covering of methods in that document is found in classes.py. +# +# Intended sources should be the variable `SOURCE` and intended sinks should be +# arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). + +# These are defined so that we can evaluate the test code. +NONSOURCE = "not a source" +SOURCE = "source" + +def is_source(x): + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + +def SINK(x): + if is_source(x): + print("OK") + else: + print("Unexpected flow", x) + +def SINK_F(x): + if is_source(x): + print("Unexpected flow", x) + else: + print("OK") + +# Callable types +# These are the types to which the function call operation (see section Calls) can be applied: + +# User-defined functions +# A user-defined function object is created by a function definition (see section Function definitions). It should be called with an argument list containing the same number of items as the function's formal parameter list. +def f(a, b): + return a + +SINK(f(SOURCE, 3)) + +# Instance methods +# An instance method object combines a class, a class instance and any callable object (normally a user-defined function). +class C(object): + + def method(self, x, cls): + assert cls is self.__class__ + return x + + @classmethod + def classmethod(cls, x): + return x + + @staticmethod + def staticmethod(x): + return x + + def gen(self, x, count): + n = count + while n > 0: + yield x + n -= 1 + + async def coro(self, x): + return x + +c = C() + +# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. +func_obj = c.method.__func__ + +# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). +SINK(c.method(SOURCE, C)) +SINK(C.method(c, SOURCE, C)) +SINK(func_obj(c, SOURCE, C)) + + +# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. +c_func_obj = C.classmethod.__func__ + +# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. +SINK(c.classmethod(SOURCE)) +SINK(C.classmethod(SOURCE)) +SINK(c_func_obj(C, SOURCE)) + +# Generator functions +# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. +def gen(x, count): + n = count + while n > 0: + yield x + n -= 1 + +iter = gen(SOURCE, 1) +SINK(iter.__next__()) +# SINK_F(iter.__next__()) # throws StopIteration, FP + +oiter = c.gen(SOURCE, 1) +SINK(oiter.__next__()) +# SINK_F(oiter.__next__()) # throws StopIteration, FP + +# Coroutine functions +# A function or method which is defined using async def is called a coroutine function. Such a function, when called, returns a coroutine object. It may contain await expressions, as well as async with and async for statements. See also the Coroutine Objects section. +async def coro(x): + return x + +import asyncio +SINK(asyncio.run(coro(SOURCE))) +SINK(asyncio.run(c.coro(SOURCE))) + +class A: + + def __await__(self): + # yield SOURCE -- see https://groups.google.com/g/dev-python/c/_lrrc-vp9TI?pli=1 + return (yield from asyncio.coroutine(lambda: SOURCE)()) + +async def agen(x): + a = A() + return await a + +SINK(asyncio.run(agen(SOURCE))) + +# Asynchronous generator functions +# A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function. + +# Calling the asynchronous iterator’s aiterator.__anext__() method will return an awaitable which when awaited will execute until it provides a value using the yield expression. When the function executes an empty return statement or falls off the end, a StopAsyncIteration exception is raised and the asynchronous iterator will have reached the end of the set of values to be yielded. + +# Built-in functions +# A built-in function object is a wrapper around a C function. Examples of built-in functions are len() and math.sin() (math is a standard built-in module). The number and type of the arguments are determined by the C function. Special read-only attributes: __doc__ is the function’s documentation string, or None if unavailable; __name__ is the function’s name; __self__ is set to None (but see the next item); __module__ is the name of the module the function was defined in or None if unavailable. + +# Built-in methods +# This is really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument. An example of a built-in method is alist.append(), assuming alist is a list object. In this case, the special read-only attribute __self__ is set to the object denoted by alist. + +# Classes +# Classes are callable. These objects normally act as factories for new instances of themselves, but variations are possible for class types that override __new__(). The arguments of the call are passed to __new__() and, in the typical case, to __init__() to initialize the new instance. + +# Class Instances +# Instances of arbitrary classes can be made callable by defining a __call__() method in their class. + +# If a class sets __iter__() to None, calling iter() on its instances will raise a TypeError (without falling back to __getitem__()). + +# 3.3.1. Basic customization + +class Customized: + + a = NONSOURCE + b = NONSOURCE + + def __new__(cls): + cls.a = SOURCE + return super().__new__(cls) + + def __init__(self): + self.b = SOURCE + +# testing __new__ and __init__ +customized = Customized() +SINK(Customized.a) +SINK_F(Customized.b) +SINK(customized.a) +SINK(customized.b) From 394991164fecbe11586432d3d92db1b16c4a7d36 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 11 Aug 2020 13:05:35 +0200 Subject: [PATCH 23/27] Python: Update test expectations --- .../coverage/classesCallGraph.expected | 118 +------- .../dataflow/coverage/dataflow.expected | 256 +++++++++--------- 2 files changed, 134 insertions(+), 240 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index 23d7c73ac30..fde0cce16d6 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -1,112 +1,6 @@ -| classes.py:21:12:21:78 | ControlFlowNode for BoolExpr | classes.py:24:8:24:19 | ControlFlowNode for is_source() | -| classes.py:21:12:21:78 | ControlFlowNode for BoolExpr | classes.py:30:8:30:19 | ControlFlowNode for is_source() | -| classes.py:24:18:24:18 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | -| classes.py:30:18:30:18 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | -| classes.py:41:10:41:10 | ControlFlowNode for a | classes.py:20:15:20:15 | SSA variable x | -| classes.py:41:10:41:10 | ControlFlowNode for a | classes.py:23:10:23:10 | SSA variable x | -| classes.py:41:10:41:10 | ControlFlowNode for a | classes.py:43:6:43:17 | ControlFlowNode for f() | -| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:43:6:43:17 | ControlFlowNode for f() | -| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | -| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | -| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:40:7:40:7 | SSA variable a | -| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | -| classes.py:43:16:43:16 | ControlFlowNode for IntegerLiteral | classes.py:40:10:40:10 | SSA variable b | -| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | -| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:23:10:23:10 | SSA variable x | -| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | -| classes.py:51:16:51:16 | ControlFlowNode for x | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | -| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:20:15:20:15 | SSA variable x | -| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:23:10:23:10 | SSA variable x | -| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | -| classes.py:55:16:55:16 | ControlFlowNode for x | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | -| classes.py:68:14:68:14 | ControlFlowNode for x | classes.py:112:18:112:31 | ControlFlowNode for Attribute() | -| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | -| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | -| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | -| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:49:16:49:19 | SSA variable self | -| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:49:22:49:22 | SSA variable x | -| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | -| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:20:15:20:15 | SSA variable x | -| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:23:10:23:10 | SSA variable x | -| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:49:22:49:22 | SSA variable x | -| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:49:25:49:27 | SSA variable cls | -| classes.py:76:23:76:23 | ControlFlowNode for C | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | -| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | -| classes.py:77:15:77:15 | ControlFlowNode for c | classes.py:49:16:49:19 | SSA variable self | -| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | -| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | -| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:49:22:49:22 | SSA variable x | -| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | -| classes.py:77:26:77:26 | ControlFlowNode for C | classes.py:49:25:49:27 | SSA variable cls | -| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | -| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | -| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | -| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | -| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:54:21:54:23 | SSA variable cls | -| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:54:26:54:26 | SSA variable x | -| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | -| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | -| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:20:15:20:15 | SSA variable x | -| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:23:10:23:10 | SSA variable x | -| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:54:21:54:23 | SSA variable cls | -| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:54:26:54:26 | SSA variable x | -| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | -| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | -| classes.py:97:12:97:17 | ControlFlowNode for SOURCE | classes.py:91:9:91:9 | SSA variable x | -| classes.py:97:20:97:20 | ControlFlowNode for IntegerLiteral | classes.py:91:12:91:16 | SSA variable count | -| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | -| classes.py:101:15:101:20 | ControlFlowNode for SOURCE | classes.py:61:13:61:16 | SSA variable self | -| classes.py:101:15:101:20 | ControlFlowNode for SOURCE | classes.py:61:19:61:19 | SSA variable x | -| classes.py:101:23:101:23 | ControlFlowNode for IntegerLiteral | classes.py:61:19:61:19 | SSA variable x | -| classes.py:101:23:101:23 | ControlFlowNode for IntegerLiteral | classes.py:61:22:61:26 | SSA variable count | -| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | -| classes.py:108:10:108:10 | ControlFlowNode for x | classes.py:111:18:111:29 | ControlFlowNode for coro() | -| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | -| classes.py:111:23:111:28 | ControlFlowNode for SOURCE | classes.py:107:16:107:16 | SSA variable x | -| classes.py:111:23:111:28 | ControlFlowNode for SOURCE | classes.py:111:18:111:29 | ControlFlowNode for coro() | -| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | -| classes.py:112:25:112:30 | ControlFlowNode for SOURCE | classes.py:67:20:67:23 | SSA variable self | -| classes.py:112:25:112:30 | ControlFlowNode for SOURCE | classes.py:67:26:67:26 | SSA variable x | -| classes.py:112:25:112:30 | ControlFlowNode for SOURCE | classes.py:112:18:112:31 | ControlFlowNode for Attribute() | -| classes.py:122:10:122:16 | ControlFlowNode for Await | classes.py:124:18:124:29 | ControlFlowNode for agen() | -| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:20:15:20:15 | SSA variable x | -| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:23:10:23:10 | SSA variable x | -| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | -| classes.py:124:23:124:28 | ControlFlowNode for SOURCE | classes.py:120:16:120:16 | SSA variable x | -| classes.py:154:12:154:31 | ControlFlowNode for Attribute() | classes.py:154:12:154:31 | ControlFlowNode for Attribute() | -| classes.py:161:6:161:17 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | -| classes.py:161:6:161:17 | ControlFlowNode for Attribute | classes.py:23:10:23:10 | SSA variable x | -| classes.py:162:8:162:19 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | -| classes.py:162:8:162:19 | ControlFlowNode for Attribute | classes.py:29:12:29:12 | SSA variable x | -| classes.py:163:6:163:17 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | -| classes.py:163:6:163:17 | ControlFlowNode for Attribute | classes.py:23:10:23:10 | SSA variable x | -| classes.py:164:6:164:17 | ControlFlowNode for Attribute | classes.py:20:15:20:15 | SSA variable x | -| classes.py:164:6:164:17 | ControlFlowNode for Attribute | classes.py:23:10:23:10 | SSA variable x | -| classes.py:174:12:174:31 | ControlFlowNode for Attribute() | classes.py:174:12:174:31 | ControlFlowNode for Attribute() | -| classes.py:329:7:329:22 | ControlFlowNode for set() | classes.py:329:7:329:22 | ControlFlowNode for set() | -| classes.py:333:7:333:28 | ControlFlowNode for frozenset() | classes.py:333:7:333:28 | ControlFlowNode for frozenset() | -| classes.py:337:7:337:26 | ControlFlowNode for dict() | classes.py:337:7:337:26 | ControlFlowNode for dict() | -| classes.py:458:28:458:51 | ControlFlowNode for dict() | classes.py:458:28:458:51 | ControlFlowNode for dict() | -| classes.py:575:12:575:24 | ControlFlowNode for Attribute() | classes.py:575:12:575:24 | ControlFlowNode for Attribute() | +| classes.py:19:12:19:31 | ControlFlowNode for Attribute() | classes.py:19:12:19:31 | ControlFlowNode for Attribute() | +| classes.py:174:7:174:22 | ControlFlowNode for set() | classes.py:174:7:174:22 | ControlFlowNode for set() | +| classes.py:178:7:178:28 | ControlFlowNode for frozenset() | classes.py:178:7:178:28 | ControlFlowNode for frozenset() | +| classes.py:182:7:182:26 | ControlFlowNode for dict() | classes.py:182:7:182:26 | ControlFlowNode for dict() | +| classes.py:303:28:303:51 | ControlFlowNode for dict() | classes.py:303:28:303:51 | ControlFlowNode for dict() | +| classes.py:420:12:420:24 | ControlFlowNode for Attribute() | classes.py:420:12:420:24 | ControlFlowNode for Attribute() | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 700da23c5ef..2b446581634 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -1,73 +1,73 @@ edges -| classes.py:18:1:18:6 | GSSA Variable SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | -| classes.py:18:1:18:6 | GSSA Variable SOURCE | classes.py:43:6:43:17 | GSSA Variable SOURCE | -| classes.py:18:1:18:6 | GSSA Variable SOURCE | classes.py:43:8:43:13 | ControlFlowNode for SOURCE | -| classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:18:1:18:6 | GSSA Variable SOURCE | -| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:70:5:70:7 | ControlFlowNode for C() | -| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | -| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:76:6:76:24 | GSSA Variable SOURCE | -| classes.py:43:6:43:17 | GSSA Variable SOURCE | classes.py:76:15:76:20 | ControlFlowNode for SOURCE | -| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | -| classes.py:70:1:70:1 | GSSA Variable c | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | -| classes.py:70:1:70:1 | GSSA Variable c | classes.py:76:6:76:24 | GSSA Variable c | -| classes.py:70:5:70:7 | ControlFlowNode for C() | classes.py:70:1:70:1 | GSSA Variable c | -| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | -| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:77:18:77:23 | ControlFlowNode for SOURCE | -| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | -| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | -| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:85:6:85:26 | GSSA Variable SOURCE | -| classes.py:76:6:76:24 | GSSA Variable SOURCE | classes.py:85:20:85:25 | ControlFlowNode for SOURCE | -| classes.py:76:6:76:24 | GSSA Variable c | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | -| classes.py:76:6:76:24 | GSSA Variable c | classes.py:77:6:77:27 | GSSA Variable c | -| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | -| classes.py:77:6:77:27 | GSSA Variable c | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | -| classes.py:77:6:77:27 | GSSA Variable c | classes.py:78:6:78:27 | GSSA Variable c | -| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | -| classes.py:78:6:78:27 | GSSA Variable c | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | -| classes.py:78:6:78:27 | GSSA Variable c | classes.py:85:6:85:26 | GSSA Variable c | -| classes.py:85:6:85:26 | GSSA Variable SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | -| classes.py:85:6:85:26 | GSSA Variable SOURCE | classes.py:86:6:86:26 | GSSA Variable SOURCE | -| classes.py:85:6:85:26 | GSSA Variable SOURCE | classes.py:86:20:86:25 | ControlFlowNode for SOURCE | -| classes.py:85:6:85:26 | GSSA Variable c | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | -| classes.py:85:6:85:26 | GSSA Variable c | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | -| classes.py:85:6:85:26 | GSSA Variable c | classes.py:97:8:97:21 | ControlFlowNode for gen() | -| classes.py:85:6:85:26 | GSSA Variable c | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | -| classes.py:85:6:85:26 | GSSA Variable c | classes.py:101:9:101:24 | ControlFlowNode for Attribute() | -| classes.py:85:6:85:26 | GSSA Variable c | classes.py:101:9:101:24 | GSSA Variable c | -| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | -| classes.py:86:6:86:26 | GSSA Variable SOURCE | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | -| classes.py:86:6:86:26 | GSSA Variable SOURCE | classes.py:97:8:97:21 | ControlFlowNode for gen() | -| classes.py:86:6:86:26 | GSSA Variable SOURCE | classes.py:97:8:97:21 | GSSA Variable SOURCE | -| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | -| classes.py:97:1:97:4 | GSSA Variable iter | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | -| classes.py:97:1:97:4 | GSSA Variable iter | classes.py:98:6:98:20 | GSSA Variable iter | -| classes.py:97:8:97:21 | ControlFlowNode for gen() | classes.py:97:1:97:4 | GSSA Variable iter | -| classes.py:97:8:97:21 | GSSA Variable SOURCE | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | -| classes.py:97:8:97:21 | GSSA Variable SOURCE | classes.py:101:9:101:24 | ControlFlowNode for Attribute() | -| classes.py:97:8:97:21 | GSSA Variable SOURCE | classes.py:101:9:101:24 | GSSA Variable SOURCE | -| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:101:9:101:24 | ControlFlowNode for Attribute() | -| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | -| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | -| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | -| classes.py:98:6:98:20 | GSSA Variable iter | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | -| classes.py:101:1:101:5 | GSSA Variable oiter | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | -| classes.py:101:1:101:5 | GSSA Variable oiter | classes.py:102:6:102:21 | GSSA Variable oiter | -| classes.py:101:9:101:24 | ControlFlowNode for Attribute() | classes.py:101:1:101:5 | GSSA Variable oiter | -| classes.py:101:9:101:24 | GSSA Variable SOURCE | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | -| classes.py:101:9:101:24 | GSSA Variable SOURCE | classes.py:111:18:111:29 | GSSA Variable SOURCE | -| classes.py:101:9:101:24 | GSSA Variable c | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | -| classes.py:101:9:101:24 | GSSA Variable c | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | -| classes.py:101:9:101:24 | GSSA Variable c | classes.py:112:18:112:31 | GSSA Variable c | -| classes.py:102:6:102:21 | GSSA Variable oiter | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | -| classes.py:102:6:102:21 | GSSA Variable oiter | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | -| classes.py:102:6:102:21 | GSSA Variable oiter | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | -| classes.py:111:18:111:29 | GSSA Variable SOURCE | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | -| classes.py:111:18:111:29 | GSSA Variable SOURCE | classes.py:112:18:112:31 | GSSA Variable SOURCE | -| classes.py:112:18:112:31 | GSSA Variable SOURCE | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | -| classes.py:112:18:112:31 | GSSA Variable SOURCE | classes.py:124:18:124:29 | GSSA Variable SOURCE | -| classes.py:112:18:112:31 | GSSA Variable c | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | -| classes.py:112:18:112:31 | GSSA Variable c | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | -| classes.py:124:18:124:29 | GSSA Variable SOURCE | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | +| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | +| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | datamodel.py:38:6:38:17 | GSSA Variable SOURCE | +| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | +| datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:13:1:13:6 | GSSA Variable SOURCE | +| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:65:5:65:7 | ControlFlowNode for C() | +| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | +| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:71:6:71:24 | GSSA Variable SOURCE | +| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | +| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | +| datamodel.py:65:1:65:1 | GSSA Variable c | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | +| datamodel.py:65:1:65:1 | GSSA Variable c | datamodel.py:71:6:71:24 | GSSA Variable c | +| datamodel.py:65:5:65:7 | ControlFlowNode for C() | datamodel.py:65:1:65:1 | GSSA Variable c | +| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | +| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | +| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | +| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | +| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:80:6:80:26 | GSSA Variable SOURCE | +| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | +| datamodel.py:71:6:71:24 | GSSA Variable c | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | +| datamodel.py:71:6:71:24 | GSSA Variable c | datamodel.py:72:6:72:27 | GSSA Variable c | +| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | +| datamodel.py:72:6:72:27 | GSSA Variable c | datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | +| datamodel.py:72:6:72:27 | GSSA Variable c | datamodel.py:73:6:73:27 | GSSA Variable c | +| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | +| datamodel.py:73:6:73:27 | GSSA Variable c | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | +| datamodel.py:73:6:73:27 | GSSA Variable c | datamodel.py:80:6:80:26 | GSSA Variable c | +| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | +| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:6:81:26 | GSSA Variable SOURCE | +| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | +| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | +| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | +| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:92:8:92:21 | ControlFlowNode for gen() | +| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | +| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() | +| datamodel.py:80:6:80:26 | GSSA Variable c | datamodel.py:96:9:96:24 | GSSA Variable c | +| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | +| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | +| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | datamodel.py:92:8:92:21 | ControlFlowNode for gen() | +| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | datamodel.py:92:8:92:21 | GSSA Variable SOURCE | +| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | +| datamodel.py:92:1:92:4 | GSSA Variable iter | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | +| datamodel.py:92:1:92:4 | GSSA Variable iter | datamodel.py:93:6:93:20 | GSSA Variable iter | +| datamodel.py:92:8:92:21 | ControlFlowNode for gen() | datamodel.py:92:1:92:4 | GSSA Variable iter | +| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | +| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() | +| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | datamodel.py:96:9:96:24 | GSSA Variable SOURCE | +| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() | +| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | +| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | +| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | +| datamodel.py:93:6:93:20 | GSSA Variable iter | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | +| datamodel.py:96:1:96:5 | GSSA Variable oiter | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | +| datamodel.py:96:1:96:5 | GSSA Variable oiter | datamodel.py:97:6:97:21 | GSSA Variable oiter | +| datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() | datamodel.py:96:1:96:5 | GSSA Variable oiter | +| datamodel.py:96:9:96:24 | GSSA Variable SOURCE | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | +| datamodel.py:96:9:96:24 | GSSA Variable SOURCE | datamodel.py:106:18:106:29 | GSSA Variable SOURCE | +| datamodel.py:96:9:96:24 | GSSA Variable c | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | +| datamodel.py:96:9:96:24 | GSSA Variable c | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | +| datamodel.py:96:9:96:24 | GSSA Variable c | datamodel.py:107:18:107:31 | GSSA Variable c | +| datamodel.py:97:6:97:21 | GSSA Variable oiter | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | +| datamodel.py:97:6:97:21 | GSSA Variable oiter | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | +| datamodel.py:97:6:97:21 | GSSA Variable oiter | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | +| datamodel.py:106:18:106:29 | GSSA Variable SOURCE | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | +| datamodel.py:106:18:106:29 | GSSA Variable SOURCE | datamodel.py:107:18:107:31 | GSSA Variable SOURCE | +| datamodel.py:107:18:107:31 | GSSA Variable SOURCE | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | +| datamodel.py:107:18:107:31 | GSSA Variable SOURCE | datamodel.py:119:18:119:29 | GSSA Variable SOURCE | +| datamodel.py:107:18:107:31 | GSSA Variable c | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | +| datamodel.py:107:18:107:31 | GSSA Variable c | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | +| datamodel.py:119:18:119:29 | GSSA Variable SOURCE | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | | test.py:43:9:43:14 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for x | | test.py:48:9:48:16 | ControlFlowNode for Str | test.py:49:10:49:10 | ControlFlowNode for x | | test.py:52:9:52:17 | ControlFlowNode for Str | test.py:53:10:53:10 | ControlFlowNode for x | @@ -78,48 +78,48 @@ edges | test.py:305:12:305:17 | ControlFlowNode for SOURCE | test.py:305:10:305:18 | ControlFlowNode for f() | | test.py:309:28:309:33 | ControlFlowNode for SOURCE | test.py:309:10:309:34 | ControlFlowNode for second() | nodes -| classes.py:18:1:18:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:18:10:18:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| classes.py:43:6:43:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| classes.py:43:6:43:17 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:43:8:43:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:70:1:70:1 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:70:5:70:7 | ControlFlowNode for C() | semmle.label | ControlFlowNode for C() | -| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:76:6:76:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:76:6:76:24 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:76:15:76:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:77:6:77:27 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:77:18:77:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | semmle.label | ControlFlowNode for func_obj() | -| classes.py:78:6:78:27 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:85:6:85:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:85:6:85:26 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:85:20:85:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:86:6:86:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:86:20:86:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | semmle.label | ControlFlowNode for c_func_obj() | -| classes.py:97:1:97:4 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:97:8:97:21 | ControlFlowNode for gen() | semmle.label | ControlFlowNode for gen() | -| classes.py:97:8:97:21 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:98:6:98:20 | GSSA Variable iter | semmle.label | GSSA Variable iter | -| classes.py:101:1:101:5 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:101:9:101:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:101:9:101:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:101:9:101:24 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:102:6:102:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | -| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:111:18:111:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:112:18:112:31 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| classes.py:112:18:112:31 | GSSA Variable c | semmle.label | GSSA Variable c | -| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| classes.py:124:18:124:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:13:1:13:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:13:10:13:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| datamodel.py:38:6:38:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | +| datamodel.py:38:6:38:17 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:65:1:65:1 | GSSA Variable c | semmle.label | GSSA Variable c | +| datamodel.py:65:5:65:7 | ControlFlowNode for C() | semmle.label | ControlFlowNode for C() | +| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:71:6:71:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:71:6:71:24 | GSSA Variable c | semmle.label | GSSA Variable c | +| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:72:6:72:27 | GSSA Variable c | semmle.label | GSSA Variable c | +| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | semmle.label | ControlFlowNode for func_obj() | +| datamodel.py:73:6:73:27 | GSSA Variable c | semmle.label | GSSA Variable c | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:80:6:80:26 | GSSA Variable c | semmle.label | GSSA Variable c | +| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:81:6:81:26 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | semmle.label | ControlFlowNode for c_func_obj() | +| datamodel.py:92:1:92:4 | GSSA Variable iter | semmle.label | GSSA Variable iter | +| datamodel.py:92:8:92:21 | ControlFlowNode for gen() | semmle.label | ControlFlowNode for gen() | +| datamodel.py:92:8:92:21 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:93:6:93:20 | GSSA Variable iter | semmle.label | GSSA Variable iter | +| datamodel.py:96:1:96:5 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | +| datamodel.py:96:9:96:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:96:9:96:24 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:96:9:96:24 | GSSA Variable c | semmle.label | GSSA Variable c | +| datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:97:6:97:21 | GSSA Variable oiter | semmle.label | GSSA Variable oiter | +| datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:106:18:106:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:107:18:107:31 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| datamodel.py:107:18:107:31 | GSSA Variable c | semmle.label | GSSA Variable c | +| datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| datamodel.py:119:18:119:29 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | | test.py:43:9:43:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:44:10:44:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | | test.py:48:9:48:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | @@ -139,23 +139,23 @@ nodes | test.py:309:10:309:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:309:28:309:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select -| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:43:6:43:17 | ControlFlowNode for f() | | -| classes.py:43:6:43:17 | ControlFlowNode for f() | classes.py:43:8:43:13 | ControlFlowNode for SOURCE | classes.py:43:6:43:17 | ControlFlowNode for f() | | -| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | | -| classes.py:76:6:76:24 | ControlFlowNode for Attribute() | classes.py:76:15:76:20 | ControlFlowNode for SOURCE | classes.py:76:6:76:24 | ControlFlowNode for Attribute() | | -| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | | -| classes.py:77:6:77:27 | ControlFlowNode for Attribute() | classes.py:77:18:77:23 | ControlFlowNode for SOURCE | classes.py:77:6:77:27 | ControlFlowNode for Attribute() | | -| classes.py:78:6:78:27 | ControlFlowNode for func_obj() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:78:6:78:27 | ControlFlowNode for func_obj() | | -| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | | -| classes.py:85:6:85:26 | ControlFlowNode for Attribute() | classes.py:85:20:85:25 | ControlFlowNode for SOURCE | classes.py:85:6:85:26 | ControlFlowNode for Attribute() | | -| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | | -| classes.py:86:6:86:26 | ControlFlowNode for Attribute() | classes.py:86:20:86:25 | ControlFlowNode for SOURCE | classes.py:86:6:86:26 | ControlFlowNode for Attribute() | | -| classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:87:6:87:26 | ControlFlowNode for c_func_obj() | | -| classes.py:98:6:98:20 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:98:6:98:20 | ControlFlowNode for Attribute() | | -| classes.py:102:6:102:21 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:102:6:102:21 | ControlFlowNode for Attribute() | | -| classes.py:111:6:111:30 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:111:6:111:30 | ControlFlowNode for Attribute() | | -| classes.py:112:6:112:32 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:112:6:112:32 | ControlFlowNode for Attribute() | | -| classes.py:124:6:124:30 | ControlFlowNode for Attribute() | classes.py:18:10:18:17 | ControlFlowNode for Str | classes.py:124:6:124:30 | ControlFlowNode for Attribute() | | +| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:38:6:38:17 | ControlFlowNode for f() | | +| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | | +| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | | +| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | | +| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | | +| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | | +| datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:73:6:73:27 | ControlFlowNode for func_obj() | | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | +| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | +| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | | +| datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:82:6:82:26 | ControlFlowNode for c_func_obj() | | +| datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:93:6:93:20 | ControlFlowNode for Attribute() | | +| datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:97:6:97:21 | ControlFlowNode for Attribute() | | +| datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:106:6:106:30 | ControlFlowNode for Attribute() | | +| datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:107:6:107:32 | ControlFlowNode for Attribute() | | +| datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:119:6:119:30 | ControlFlowNode for Attribute() | | | test.py:44:10:44:10 | ControlFlowNode for x | test.py:43:9:43:14 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for x | | | test.py:49:10:49:10 | ControlFlowNode for x | test.py:48:9:48:16 | ControlFlowNode for Str | test.py:49:10:49:10 | ControlFlowNode for x | | | test.py:53:10:53:10 | ControlFlowNode for x | test.py:52:9:52:17 | ControlFlowNode for Str | test.py:53:10:53:10 | ControlFlowNode for x | | From dd4d00293d07f140f53ef524930b4edc62e1728c Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 11 Aug 2020 14:16:02 +0200 Subject: [PATCH 24/27] Python: remaining class tests --- .../experimental/dataflow/coverage/classes.py | 50 ++++++++++++++++++- .../coverage/classesCallGraph.expected | 2 +- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 63147cfd6ae..927fbc50abe 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -302,13 +302,20 @@ def test_set_name(): with_set_name = With_set_name() type("Owner", (object,), dict(attr=with_set_name)) -# 3.3.2.4. __slots__ +# 3.3.2.4. __slots__ // We are not testing the suppression of -weakref_ and _dict_ here # object.__slots__ # __weakref__ # __dict__ # 3.3.3. Customizing class creation # classmethod object.__init_subclass__(cls) +class With_init_subclass: + + def __init_subclass__(cls): + OK() + +def test_init_subclass(): + type("Subclass", (With_init_subclass,), {}) # 3.3.3.1. Metaclasses # By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace). @@ -318,13 +325,52 @@ def test_set_name(): # 3.3.3.4. Preparing the class namespace # metaclass.__prepare__(name, bases, **kwds) +class With_prepare(type): + + def __prepare__(name, bases, **kwds): + OK() + return kwds + + +def test_prepare(): + class With_meta(metaclass=With_prepare): + pass # 3.3.4. Customizing instance and subclass checks # class.__instancecheck__(self, instance) +class With_instancecheck: + + def __instancecheck__(self, instance): + OK() + return True + +def test_instancecheck(): + with_instancecheck = With_instancecheck() + isinstance("", with_instancecheck) + # class.__subclasscheck__(self, subclass) +class With_subclasscheck: + + def __subclasscheck__(self, subclass): + OK() + return True + +def test_subclasscheck(): + with_subclasscheck = With_subclasscheck() + issubclass(object, with_subclasscheck) + # 3.3.5. Emulating generic types # classmethod object.__class_getitem__(cls, key) +class With_class_getitem: + + def __class_getitem__(cls, key): + OK() + return object + +def test_class_getitem(): + with_class_getitem = With_class_getitem[int]() + # 3.3.6. Emulating callable objects # object.__call__(self[, args...]) @@ -1113,7 +1159,7 @@ async def atest_await(): await(with_await) -# # 3.4.2. Coroutine Objects +# # 3.4.2. Coroutine Objects // These should be handled as normal function calls # # coroutine.send(value) # # coroutine.throw(type[, value[, traceback]]) # # coroutine.close() diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index fde0cce16d6..4baf0b077ab 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -3,4 +3,4 @@ | classes.py:178:7:178:28 | ControlFlowNode for frozenset() | classes.py:178:7:178:28 | ControlFlowNode for frozenset() | | classes.py:182:7:182:26 | ControlFlowNode for dict() | classes.py:182:7:182:26 | ControlFlowNode for dict() | | classes.py:303:28:303:51 | ControlFlowNode for dict() | classes.py:303:28:303:51 | ControlFlowNode for dict() | -| classes.py:420:12:420:24 | ControlFlowNode for Attribute() | classes.py:420:12:420:24 | ControlFlowNode for Attribute() | +| classes.py:466:12:466:24 | ControlFlowNode for Attribute() | classes.py:466:12:466:24 | ControlFlowNode for Attribute() | From 4b336e9b01f589b3d18db8d15cc40ec764795fd0 Mon Sep 17 00:00:00 2001 From: yoff Date: Fri, 14 Aug 2020 10:53:10 +0200 Subject: [PATCH 25/27] Update python/ql/test/experimental/dataflow/coverage/classes.py Co-authored-by: Taus --- python/ql/test/experimental/dataflow/coverage/classes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 927fbc50abe..f6b39527443 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1,5 +1,5 @@ # User-defined methods, both instance methods and class methods, can be called in many non-standard ways -# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` function on a +# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` method on a # class `C` will be called by the syntactic construct `await c` when `c` is an instance of `C`. # # These tests should cover all the class calls that we hope to support. From 8d49ad73252a133262499544a66581b16d2f548e Mon Sep 17 00:00:00 2001 From: yoff Date: Fri, 14 Aug 2020 10:53:37 +0200 Subject: [PATCH 26/27] Update python/ql/test/experimental/dataflow/coverage/datamodel.py Co-authored-by: Taus --- python/ql/test/experimental/dataflow/coverage/datamodel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/datamodel.py b/python/ql/test/experimental/dataflow/coverage/datamodel.py index 82f10f4d53f..a3fc54d11a0 100644 --- a/python/ql/test/experimental/dataflow/coverage/datamodel.py +++ b/python/ql/test/experimental/dataflow/coverage/datamodel.py @@ -1,5 +1,5 @@ # User-defined methods, both instance methods and class methods, can be called in many non-standard ways -# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` function on a +# i.e. differently from simply `c.f()` or `C.f()`. For example, a user-defined `__await__` method on a # class `C` will be called by the syntactic construct `await c` when `c` is an instance of `C`. # # These tests are based on the first part of https://docs.python.org/3/reference/datamodel.html. From 9556937840999239728c141e6549655b5f599795 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 14 Aug 2020 11:29:58 +0200 Subject: [PATCH 27/27] Python: address review comments --- python/ql/test/experimental/dataflow/coverage/classes.py | 4 +++- .../test/experimental/dataflow/coverage/classesCallGraph.ql | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index f6b39527443..de06d284361 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -1041,7 +1041,9 @@ def test_index(): with_index = With_index() operator.index(with_index) -# make With_index subscriptable to test slicing +def test_index_slicing(): + with_index = With_index() + [0][with_index:1] def test_index_bin(): with_index = With_index() diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql index 0416cc7f6ea..1445e314c98 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql @@ -6,4 +6,5 @@ where sink.getLocation().getFile().getBaseName() = "classes.py" and exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) select source, sink -// Rewrite this to just have 1-step paths? +// Ideally, we would just have 1-step paths either from argument to parameter +// or from return to call. This gives a bit more, so should be rewritten.