mirror of
https://github.com/github/codeql.git
synced 2025-12-20 18:56:32 +01:00
Python: Add test showing self type-tracking problems
This commit is contained in:
@@ -42,4 +42,8 @@ typeTracker_found_pointsTo_notFound
|
|||||||
| code/isinstance.py:14:13:14:22 | ControlFlowNode for Attribute() | B.foo |
|
| code/isinstance.py:14:13:14:22 | ControlFlowNode for Attribute() | B.foo |
|
||||||
| code/isinstance.py:17:13:17:22 | ControlFlowNode for Attribute() | A.foo |
|
| code/isinstance.py:17:13:17:22 | ControlFlowNode for Attribute() | A.foo |
|
||||||
| code/nested_class.py:83:9:83:16 | ControlFlowNode for Attribute() | X.class_def_in_func.Y.meth |
|
| code/nested_class.py:83:9:83:16 | ControlFlowNode for Attribute() | X.class_def_in_func.Y.meth |
|
||||||
|
| code/self_passing.py:16:9:16:18 | ControlFlowNode for Attribute() | A.foo |
|
||||||
|
| code/self_passing.py:16:9:16:18 | ControlFlowNode for Attribute() | B.foo |
|
||||||
|
| code/self_passing.py:67:9:67:16 | ControlFlowNode for Attribute() | Y.cm |
|
||||||
|
| code/self_passing.py:69:9:69:17 | ControlFlowNode for Attribute() | X.foo |
|
||||||
| code/underscore_prefix_func_name.py:14:5:14:19 | ControlFlowNode for some_function() | some_function |
|
| code/underscore_prefix_func_name.py:14:5:14:19 | ControlFlowNode for some_function() | some_function |
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# These test-cases illustrates what can happen if we allow the type trackers that are used
|
||||||
|
# for tracking class instances to flow into self parameters.
|
||||||
|
|
||||||
|
# This first case shows the problem of the call to `self.bar` inside A.foo, could be
|
||||||
|
# considered a call to B.bar, if we allow the flow from the `self` parameter of
|
||||||
|
# `Base.base_meth` to flow into A.foo (through the `self.foo` call). This is
|
||||||
|
# problematic, and causes us to have different results for the `self.bar()` calls in
|
||||||
|
# `A.foo` and `A.not_called`.
|
||||||
|
|
||||||
|
from inspect import isclass
|
||||||
|
|
||||||
|
|
||||||
|
class Base(object):
|
||||||
|
def base_meth(self):
|
||||||
|
print("Base.base_meth")
|
||||||
|
self.foo() # $ pt,tt=Base.foo tt=A.foo tt=B.foo
|
||||||
|
|
||||||
|
def foo(self):
|
||||||
|
print("Base.foo")
|
||||||
|
|
||||||
|
class A(Base):
|
||||||
|
def foo(self):
|
||||||
|
print("A.foo")
|
||||||
|
self.bar() # $ pt,tt=A.bar SPURIOUS: tt=B.bar
|
||||||
|
|
||||||
|
def not_called(self):
|
||||||
|
self.bar() #$ pt,tt=A.bar
|
||||||
|
|
||||||
|
def bar(self):
|
||||||
|
print("A.bar")
|
||||||
|
|
||||||
|
class B(Base):
|
||||||
|
def foo(self):
|
||||||
|
print("B.foo")
|
||||||
|
|
||||||
|
def bar(self):
|
||||||
|
print("B.bar")
|
||||||
|
|
||||||
|
a = A()
|
||||||
|
a.foo() # $ pt,tt=A.foo
|
||||||
|
|
||||||
|
# Another problem is mixing up class instances and class references. In the example
|
||||||
|
# below since `func` takes BOTH an instance of X, and the class Y, we used to end up
|
||||||
|
# tracking _both_ to the self argument of X.foo, which meant that the self.meth() call
|
||||||
|
# in X.foo was resolved to BOTH X.meth and Y.meth.
|
||||||
|
|
||||||
|
class X(object):
|
||||||
|
def meth(self):
|
||||||
|
print("X.meth")
|
||||||
|
|
||||||
|
def foo(self):
|
||||||
|
print("X.foo")
|
||||||
|
self.meth() # $ pt,tt=X.meth SPURIOUS: tt=Y.meth
|
||||||
|
|
||||||
|
|
||||||
|
class Y(object):
|
||||||
|
def meth(self):
|
||||||
|
print("Y.meth")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cm(cls):
|
||||||
|
print("Y.cm")
|
||||||
|
|
||||||
|
|
||||||
|
def func(obj):
|
||||||
|
if isclass(obj):
|
||||||
|
obj.cm() # $ tt=Y.cm
|
||||||
|
else:
|
||||||
|
obj.foo() # $ tt=X.foo
|
||||||
|
|
||||||
|
func(Y) # $ pt,tt=func
|
||||||
|
x = X()
|
||||||
|
func(x) # $ pt,tt=func
|
||||||
|
|
||||||
|
|
||||||
|
# While avoiding the two problems above is good, we have to be careful not to prune away
|
||||||
|
# _all_ type-tracking flow to the self parameter (since it's the local source node for
|
||||||
|
# all references to it within the function). So in the example below, we still want to
|
||||||
|
# be able to resolve that some_function is assigned to the attribute `func` on self.
|
||||||
|
|
||||||
|
|
||||||
|
class Example3(object):
|
||||||
|
def wat(self, f):
|
||||||
|
print("Example3.wat")
|
||||||
|
self.func = f
|
||||||
|
self.func() # $ pt,tt=some_function
|
||||||
|
|
||||||
|
|
||||||
|
def some_function():
|
||||||
|
print("some_function")
|
||||||
|
|
||||||
|
|
||||||
|
ex3 = Example3()
|
||||||
|
ex3.wat(some_function) # $ pt,tt=Example3.wat
|
||||||
Reference in New Issue
Block a user