Skip to content

Commit cf8c8bc

Browse files
committed
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.
1 parent 5378f25 commit cf8c8bc

File tree

4 files changed

+42
-2
lines changed

4 files changed

+42
-2
lines changed

python/ql/lib/semmle/python/Exprs.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -769,8 +769,8 @@ class Annotation extends Expr {
769769
or
770770
result = any(Parameter p | p.getAnnotation() = this)
771771
or
772-
exists(FunctionExpr f |
773-
this = f.getReturns() and result = f.getInnerScope().getReturnNode().getNode()
772+
exists(FunctionExpr f, Return r |
773+
this = f.getReturns() and r.getScope() = f.getInnerScope() and result = r.getValue()
774774
)
775775
}
776776
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
testFailures
2+
debug_callableNotUnique
3+
pointsTo_found_typeTracker_notFound
4+
typeTracker_found_pointsTo_notFound
5+
| type_annotations.py:6:5:6:14 | ControlFlowNode for Attribute() | Foo.method |
6+
| type_annotations.py:16:5:16:14 | ControlFlowNode for Attribute() | Foo.method |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../CallGraph/InlineCallGraphTest.ql
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
class Foo:
2+
def method(self):
3+
pass
4+
5+
def test_parameter_annotation(x: Foo):
6+
x.method() #$ tt=Foo.method
7+
8+
def test_no_parameter_annotation(x):
9+
x.method()
10+
11+
def function_with_return_annotation() -> Foo:
12+
return eval("Foo()")
13+
14+
def test_return_annotation():
15+
x = function_with_return_annotation() #$ pt,tt=function_with_return_annotation
16+
x.method() #$ tt=Foo.method
17+
18+
def function_without_return_annotation():
19+
return eval("Foo()")
20+
21+
def test_no_return_annotation():
22+
x = function_without_return_annotation() #$ pt,tt=function_without_return_annotation
23+
x.method()
24+
25+
def test_variable_annotation():
26+
x = eval("Foo()")
27+
x : Foo
28+
# Currently fails because there is no flow from the class definition to the type annotation.
29+
x.method() #$ MISSING: tt=Foo.method
30+
31+
def test_no_variable_annotation():
32+
x = eval("Foo()")
33+
x.method()

0 commit comments

Comments
 (0)