mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
Python: Move dataflow tests out of experimental
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,37 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
module DataFlowCallTest implements TestSig {
|
||||
string getARelevantTag() {
|
||||
result in ["call", "callType"]
|
||||
or
|
||||
result = "arg[" + any(DataFlowDispatch::ArgumentPosition pos).toString() + "]"
|
||||
}
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(DataFlowDispatch::DataFlowCall call |
|
||||
location = call.getLocation() and
|
||||
element = call.toString() and
|
||||
exists(call.getCallable())
|
||||
|
|
||||
value = prettyExpr(call.getNode().getNode()) and
|
||||
tag = "call"
|
||||
or
|
||||
value = call.(DataFlowDispatch::NormalCall).getCallType().toString() and
|
||||
tag = "callType"
|
||||
or
|
||||
exists(DataFlowDispatch::ArgumentPosition pos, DataFlow::Node arg |
|
||||
arg = call.getArgument(pos)
|
||||
|
|
||||
value = prettyNodeForInlineTest(arg) and
|
||||
tag = "arg[" + pos + "]"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<DataFlowCallTest>
|
||||
16
python/ql/test/library-tests/dataflow/calls/new_cls_param.py
Normal file
16
python/ql/test/library-tests/dataflow/calls/new_cls_param.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# We want to ensure that the __new__ method is considered a classmethod even though it
|
||||
# doesn't have a decorator. This means that the `cls` parameter should be considered a
|
||||
# reference to the class (or subclass), and not an instance of the class. We can detect
|
||||
# this from looking at the arguments passed in the `cls.foo` call. if we see a `self`
|
||||
# argument, this means it has correct behavior (because we're targeting a classmethod),
|
||||
# if there is no `self` argument, this means we've only considered `cls` to be a class
|
||||
# instance, since we don't want to pass that to the `cls` parameter of the classmethod `WithNewImpl.foo`.
|
||||
|
||||
class WithNewImpl(object):
|
||||
def __new__(cls):
|
||||
print("WithNewImpl.foo")
|
||||
cls.foo() # $ call=cls.foo() callType=CallTypeClassMethod arg[self]=cls
|
||||
|
||||
@classmethod
|
||||
def foo(cls):
|
||||
print("WithNewImpl.foo")
|
||||
82
python/ql/test/library-tests/dataflow/calls/test.py
Normal file
82
python/ql/test/library-tests/dataflow/calls/test.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# A very basic test of DataFlowCall
|
||||
#
|
||||
# see `coverage/argumentRoutingTest.ql` for a more in depth test of argument routing
|
||||
# handling.
|
||||
|
||||
def func(arg):
|
||||
pass
|
||||
|
||||
|
||||
class MyClass(object):
|
||||
def __init__(self, arg):
|
||||
pass
|
||||
|
||||
def my_method(self, arg):
|
||||
pass
|
||||
|
||||
def other_method(self):
|
||||
self.my_method(42) # $ arg[self]=self call=self.my_method(..) callType=CallTypeNormalMethod arg[position 0]=42
|
||||
self.sm(42) # $ call=self.sm(..) callType=CallTypeStaticMethod arg[position 0]=42
|
||||
|
||||
@staticmethod
|
||||
def sm(arg):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def cm(cls, arg):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def other_classmethod(cls):
|
||||
cls.cm(42) # $ call=cls.cm(..) callType=CallTypeClassMethod arg[position 0]=42 arg[self]=cls
|
||||
cls.sm(42) # $ call=cls.sm(..) callType=CallTypeStaticMethod arg[position 0]=42
|
||||
|
||||
def __getitem__(self, key):
|
||||
pass
|
||||
|
||||
func(0) # $ call=func(..) arg[position 0]=0 callType=CallTypePlainFunction
|
||||
|
||||
x = MyClass(1) # $ call=MyClass(..) arg[self]=[pre]MyClass(..) arg[position 0]=1 callType=CallTypeClass
|
||||
|
||||
x.my_method(2) # $ call=x.my_method(..) arg[self]=x arg[position 0]=2 callType=CallTypeNormalMethod
|
||||
mm = x.my_method
|
||||
mm(2) # $ call=mm(..) arg[self]=x arg[position 0]=2 callType=CallTypeNormalMethod
|
||||
MyClass.my_method(x, 2) # $ call=MyClass.my_method(..) arg[position 0]=2 arg[self]=x callType=CallTypeMethodAsPlainFunction
|
||||
|
||||
x.sm(3) # $ call=x.sm(..) arg[position 0]=3 callType=CallTypeStaticMethod
|
||||
MyClass.sm(3) # $ call=MyClass.sm(..) arg[position 0]=3 callType=CallTypeStaticMethod
|
||||
|
||||
x.cm(4) # $ call=x.cm(..) arg[position 0]=4 callType=CallTypeClassMethod
|
||||
MyClass.cm(4) # $ call=MyClass.cm(..) arg[position 0]=4 arg[self]=MyClass callType=CallTypeClassMethod
|
||||
|
||||
x[5] # $ MISSING: call=x[5] arg[self]=x arg[position 0]=5
|
||||
|
||||
|
||||
class Subclass(MyClass):
|
||||
pass
|
||||
|
||||
y = Subclass(1) # $ call=Subclass(..) arg[self]=[pre]Subclass(..) arg[position 0]=1 callType=CallTypeClass
|
||||
|
||||
y.my_method(2) # $ call=y.my_method(..) arg[self]=y arg[position 0]=2 callType=CallTypeNormalMethod
|
||||
mm = y.my_method
|
||||
mm(2) # $ call=mm(..) arg[self]=y arg[position 0]=2 callType=CallTypeNormalMethod
|
||||
Subclass.my_method(y, 2) # $ call=Subclass.my_method(..) arg[self]=y arg[position 0]=2 callType=CallTypeMethodAsPlainFunction
|
||||
|
||||
y.sm(3) # $ call=y.sm(..) arg[position 0]=3 callType=CallTypeStaticMethod
|
||||
Subclass.sm(3) # $ call=Subclass.sm(..) arg[position 0]=3 callType=CallTypeStaticMethod
|
||||
|
||||
y.cm(4) # $ call=y.cm(..) arg[position 0]=4 callType=CallTypeClassMethod
|
||||
Subclass.cm(4) # $ call=Subclass.cm(..) arg[self]=Subclass arg[position 0]=4 callType=CallTypeClassMethod
|
||||
|
||||
y[5] # $ MISSING: call=y[5] arg[self]=y arg[position 0]=5
|
||||
|
||||
|
||||
try:
|
||||
# These are included to show whether we have a DataFlowCall for things we can't
|
||||
# resolve. Both are interesting since with points-to we used to have a DataFlowCall
|
||||
# for _one_ but not the other
|
||||
import mypkg
|
||||
mypkg.foo(42)
|
||||
mypkg.subpkg.bar(43)
|
||||
except:
|
||||
pass
|
||||
Reference in New Issue
Block a user