Python: Add tests

Also fixes an issue with the return type annotations that caused these
to not work properly.

Currently, annotated assignments don't work properly, due to the fact
that our flow relation doesn't consider flow going to the "type" part of
an annotated assignment. This means that in `x : Foo`, we do correctly
note that `x` is annotated with `Foo`, but we have no idea what `Foo`
is, since it has no incoming flow.

To fix this we should probably just extend the flow relation, but this
may need to be done with some care, so I have left it as future work.
This commit is contained in:
Taus
2025-07-11 12:03:00 +00:00
parent 2c45550a9f
commit c6c6a857df
4 changed files with 42 additions and 2 deletions

View File

@@ -769,8 +769,8 @@ class Annotation extends Expr {
or
result = any(Parameter p | p.getAnnotation() = this)
or
exists(FunctionExpr f |
this = f.getReturns() and result = f.getInnerScope().getReturnNode().getNode()
exists(FunctionExpr f, Return r |
this = f.getReturns() and r.getScope() = f.getInnerScope() and result = r.getValue()
)
}
}

View File

@@ -0,0 +1,6 @@
testFailures
debug_callableNotUnique
pointsTo_found_typeTracker_notFound
typeTracker_found_pointsTo_notFound
| type_annotations.py:6:5:6:14 | ControlFlowNode for Attribute() | Foo.method |
| type_annotations.py:16:5:16:14 | ControlFlowNode for Attribute() | Foo.method |

View File

@@ -0,0 +1 @@
../CallGraph/InlineCallGraphTest.ql

View File

@@ -0,0 +1,33 @@
class Foo:
def method(self):
pass
def test_parameter_annotation(x: Foo):
x.method() #$ tt=Foo.method
def test_no_parameter_annotation(x):
x.method()
def function_with_return_annotation() -> Foo:
return eval("Foo()")
def test_return_annotation():
x = function_with_return_annotation() #$ pt,tt=function_with_return_annotation
x.method() #$ tt=Foo.method
def function_without_return_annotation():
return eval("Foo()")
def test_no_return_annotation():
x = function_without_return_annotation() #$ pt,tt=function_without_return_annotation
x.method()
def test_variable_annotation():
x = eval("Foo()")
x : Foo
# Currently fails because there is no flow from the class definition to the type annotation.
x.method() #$ MISSING: tt=Foo.method
def test_no_variable_annotation():
x = eval("Foo()")
x.method()