Skip to content

Commit 9e537a7

Browse files
committed
Python points-to: Handle varargs in caller
1 parent 468975b commit 9e537a7

File tree

11 files changed

+168
-15
lines changed

11 files changed

+168
-15
lines changed

python/ql/src/semmle/python/Exprs.qll

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,12 @@ class Call extends Call_ {
168168

169169
override CallNode getAFlowNode() { result = super.getAFlowNode() }
170170

171-
/** Gets a tuple (*) argument of this class definition. */
171+
/** Gets a tuple (*) argument of this call. */
172172
Expr getStarargs() {
173173
result = this.getAPositionalArg().(Starred).getValue()
174174
}
175175

176-
/** Gets a dictionary (**) argument of this class definition. */
176+
/** Gets a dictionary (**) argument of this call. */
177177
Expr getKwargs() {
178178
result = this.getANamedArg().(DictUnpacking).getValue()
179179
}
@@ -227,10 +227,18 @@ class Call extends Call_ {
227227
result = this.getKwargs().(Dict).getAKey().(StrConst).getText()
228228
}
229229

230+
/** Gets the positional argument count of this call, provided there is no more than one tuple (*) argument. */
230231
int getPositionalArgumentCount() {
231232
count(this.getStarargs()) < 2 and
232-
result = count(this.getAPositionalArg())
233+
result = count(Expr arg | arg = this.getAPositionalArg() and not arg instanceof Starred)
233234
}
235+
236+
/** Gets the tuple (*) argument of this call, provided there is exactly one. */
237+
Expr getStarArg() {
238+
count(this.getStarargs()) < 2 and
239+
result = getStarargs()
240+
}
241+
234242
}
235243

236244
/** A conditional expression such as, `body if test else orelse` */

python/ql/src/semmle/python/Flow.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,11 @@ class CallNode extends ControlFlowNode {
481481
)
482482
}
483483

484+
ControlFlowNode getStarArg() {
485+
result.getNode() = this.getNode().getStarArg() and
486+
result.getBasicBlock().dominates(this.getBasicBlock())
487+
}
488+
484489
}
485490

486491
/** A control flow corresponding to an attribute expression, such as `value.attr` */

python/ql/src/semmle/python/objects/Sequences.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ abstract class TupleObjectInternal extends SequenceObjectInternal {
4141
private string contents(int n) {
4242
n < 4 and n = this.length() and result = ""
4343
or
44-
n = 4 and n < this.length() and result = "... " + (this.length()-4).toString() + " more"
44+
n = 3 and this.length() > 3 and result = (this.length()-3).toString() + " more..."
4545
or
4646
result = this.getItem(n).toString() + ", " + this.contents(n+1)
4747
}
@@ -165,7 +165,7 @@ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal {
165165
exists(CallNode call, PointsToContext context, int offset, int length |
166166
this = TVarargsTuple(call, context, offset, length) and
167167
n < length and
168-
PointsToInternal::pointsTo(call.getArg(offset+n), context, result, _)
168+
InterProceduralPointsTo::positional_argument_points_to(call, offset+n, context, result, _)
169169
)
170170
}
171171

python/ql/src/semmle/python/pointsto/PointsTo.qll

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -844,11 +844,13 @@ module InterProceduralPointsTo {
844844
private predicate normal_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
845845
exists(PointsToContext caller, ControlFlowNode arg |
846846
PointsToInternal::pointsTo(arg, caller, value, origin) and
847-
callsite_argument_transfer(arg, caller, def, context)
847+
named_argument_transfer(arg, caller, def, context)
848848
)
849849
or
850850
not def.isSelf() and not def.isVarargs() and not def.isKwargs() and
851851
context.isRuntime() and value = ObjectInternal::unknown() and origin = def.getDefiningNode()
852+
or
853+
positional_parameter_points_to(def, context, value, origin)
852854
}
853855

854856
pragma [noinline]
@@ -921,15 +923,15 @@ module InterProceduralPointsTo {
921923
exists(int parameter_offset |
922924
callsite_calls_function(call, caller, scope, callee, parameter_offset) and
923925
startOffset = scope.getPositionalParameterCount() - parameter_offset and
924-
length = call.getNode().getPositionalArgumentCount() - startOffset and
926+
length = positional_argument_count(call, caller) - startOffset and
925927
length > 0
926928
)
927929
}
928930

929931
predicate varargs_empty_tuple(Function scope, PointsToContext callee) {
930932
exists(CallNode call, PointsToContext caller, int parameter_offset |
931933
callsite_calls_function(call, caller, scope, callee, parameter_offset) and
932-
scope.getPositionalParameterCount() - parameter_offset >= call.getNode().getPositionalArgumentCount()
934+
scope.getPositionalParameterCount() - parameter_offset >= positional_argument_count(call, caller)
933935
)
934936
}
935937

@@ -940,16 +942,39 @@ module InterProceduralPointsTo {
940942
p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict"))
941943
}
942944

943-
/** Holds if the `(argument, caller)` pair matches up with `(param, callee)` pair across call. */
944-
cached predicate callsite_argument_transfer(ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, PointsToContext callee) {
945+
predicate positional_argument_points_to(CallNode call, int argument, PointsToContext caller, ObjectInternal value, ControlFlowNode origin) {
946+
PointsToInternal::pointsTo(call.getArg(argument), caller, value, origin)
947+
or
948+
exists(SequenceObjectInternal arg, int pos |
949+
pos = call.getNode().getPositionalArgumentCount() and
950+
PointsToInternal::pointsTo(origin, caller, arg, _) and
951+
value = arg.getItem(argument-pos) and
952+
origin = call.getStarArg()
953+
)
954+
}
955+
956+
private int positional_argument_count(CallNode call, PointsToContext caller) {
957+
result = call.getNode().getPositionalArgumentCount() and not exists(call.getStarArg()) and caller.appliesTo(call)
958+
or
959+
exists(SequenceObjectInternal arg, int pos |
960+
pos = call.getNode().getPositionalArgumentCount() and
961+
PointsToInternal::pointsTo(call.getStarArg(), caller, arg, _) and
962+
result = pos + arg.length()
963+
)
964+
}
965+
966+
predicate positional_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
967+
exists(CallNode call, int argument, PointsToContext caller, Function func, int offset |
968+
positional_argument_points_to(call, argument, caller, value, origin) and
969+
callsite_calls_function(call, caller, func, context, offset) and
970+
def.getParameter() = func.getArg(argument+offset)
971+
)
972+
}
973+
974+
cached predicate named_argument_transfer(ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, PointsToContext callee) {
945975
exists(CallNode call, Function func, int offset |
946976
callsite_calls_function(call, caller, func, callee, offset)
947977
|
948-
exists(int n |
949-
argument = call.getArg(n) and
950-
param.getParameter() = func.getArg(n+offset)
951-
)
952-
or
953978
exists(string name |
954979
argument = call.getArgByName(name) and
955980
param.getParameter() = func.getArgByName(name)

python/ql/test/library-tests/PointsTo/new/Call.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
| l_calls.py:37 | ControlFlowNode for f() | f |
2020
| l_calls.py:38 | ControlFlowNode for Attribute() | E.m |
2121
| l_calls.py:39 | ControlFlowNode for Attribute() | E.m |
22+
| l_calls.py:42 | ControlFlowNode for f() | f |
23+
| l_calls.py:51 | ControlFlowNode for g() | g |
24+
| l_calls.py:52 | ControlFlowNode for Attribute() | F.m |
25+
| l_calls.py:53 | ControlFlowNode for Attribute() | F.m |
2226
| q_super.py:4 | ControlFlowNode for Attribute() | object.__init__ |
2327
| q_super.py:12 | ControlFlowNode for Attribute() | Base2.__init__ |
2428
| q_super.py:22 | ControlFlowNode for Attribute() | Base1.meth |

python/ql/test/library-tests/PointsTo/new/NameSpace.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,18 @@
113113
| k_getsetattr.py:4 | Class C | meth1 | Function meth1 |
114114
| k_getsetattr.py:4 | Class C | meth2 | Function meth2 |
115115
| l_calls.py:0 | Module code.l_calls | E | class E |
116+
| l_calls.py:0 | Module code.l_calls | F | class F |
116117
| l_calls.py:0 | Module code.l_calls | Owner | class Owner |
117118
| l_calls.py:0 | Module code.l_calls | bar | Function bar |
118119
| l_calls.py:0 | Module code.l_calls | f | Function f |
119120
| l_calls.py:0 | Module code.l_calls | foo | Function foo |
121+
| l_calls.py:0 | Module code.l_calls | g | Function g |
122+
| l_calls.py:0 | Module code.l_calls | t | Tuple |
120123
| l_calls.py:12 | Class Owner | cm | classmethod() |
121124
| l_calls.py:12 | Class Owner | cm2 | classmethod() |
122125
| l_calls.py:12 | Class Owner | m | Function m |
123126
| l_calls.py:32 | Class E | m | Function m |
127+
| l_calls.py:47 | Class F | m | Function m |
124128
| o_no_returns.py:0 | Module code.o_no_returns | bar | Function bar |
125129
| o_no_returns.py:0 | Module code.o_no_returns | fail | Function fail |
126130
| o_no_returns.py:0 | Module code.o_no_returns | foo | Function foo |

python/ql/test/library-tests/PointsTo/new/PointsToNone.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
| k_getsetattr.py:15 | ControlFlowNode for Attribute() | 6 |
7676
| l_calls.py:4 | ControlFlowNode for Attribute() | 4 |
7777
| l_calls.py:9 | ControlFlowNode for foo() | 4 |
78+
| l_calls.py:48 | ControlFlowNode for None | 48 |
7879
| m_attributes.py:12 | ControlFlowNode for Attribute() | 8 |
7980
| m_attributes.py:13 | ControlFlowNode for Attribute() | 8 |
8081
| o_no_returns.py:7 | ControlFlowNode for fail() | 10 |

python/ql/test/library-tests/PointsTo/new/PointsToWithContext.expected

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ WARNING: Predicate points_to has been deprecated and may be removed in future (P
602602
| l_calls.py:29 | ControlFlowNode for args | args | builtin-class tuple | 29 | runtime |
603603
| l_calls.py:29 | ControlFlowNode for f | Function f | builtin-class function | 29 | import |
604604
| l_calls.py:30 | ControlFlowNode for args | args | builtin-class tuple | 29 | code/l_calls.py:37 from import |
605+
| l_calls.py:30 | ControlFlowNode for args | args | builtin-class tuple | 29 | code/l_calls.py:42 from import |
605606
| l_calls.py:30 | ControlFlowNode for args | args | builtin-class tuple | 29 | runtime |
606607
| l_calls.py:32 | ControlFlowNode for ClassExpr | class E | builtin-class type | 32 | import |
607608
| l_calls.py:32 | ControlFlowNode for E | class E | builtin-class type | 32 | import |
@@ -635,6 +636,37 @@ WARNING: Predicate points_to has been deprecated and may be removed in future (P
635636
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 39 | import |
636637
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 4 | builtin-class int | 39 | import |
637638
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 5 | builtin-class int | 39 | import |
639+
| l_calls.py:41 | ControlFlowNode for Str | 'a' | builtin-class str | 41 | import |
640+
| l_calls.py:41 | ControlFlowNode for Str | 'b' | builtin-class str | 41 | import |
641+
| l_calls.py:41 | ControlFlowNode for Str | 'c' | builtin-class str | 41 | import |
642+
| l_calls.py:41 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 41 | import |
643+
| l_calls.py:41 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 | import |
644+
| l_calls.py:42 | ControlFlowNode for f | Function f | builtin-class function | 29 | import |
645+
| l_calls.py:42 | ControlFlowNode for f() | args | builtin-class tuple | 29 | import |
646+
| l_calls.py:42 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 | import |
647+
| l_calls.py:44 | ControlFlowNode for FunctionExpr | Function g | builtin-class function | 44 | import |
648+
| l_calls.py:44 | ControlFlowNode for g | Function g | builtin-class function | 44 | import |
649+
| l_calls.py:45 | ControlFlowNode for a | 'a' | builtin-class str | 51 | code/l_calls.py:51 from import |
650+
| l_calls.py:47 | ControlFlowNode for ClassExpr | class F | builtin-class type | 47 | import |
651+
| l_calls.py:47 | ControlFlowNode for F | class F | builtin-class type | 47 | import |
652+
| l_calls.py:47 | ControlFlowNode for object | builtin-class object | builtin-class type | 47 | import |
653+
| l_calls.py:48 | ControlFlowNode for FunctionExpr | Function m | builtin-class function | 48 | import |
654+
| l_calls.py:48 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 48 | import |
655+
| l_calls.py:48 | ControlFlowNode for m | Function m | builtin-class function | 48 | import |
656+
| l_calls.py:49 | ControlFlowNode for x | 'a' | builtin-class str | 52 | code/l_calls.py:52 from import |
657+
| l_calls.py:49 | ControlFlowNode for x | 'b' | builtin-class str | 53 | code/l_calls.py:53 from import |
658+
| l_calls.py:51 | ControlFlowNode for g | Function g | builtin-class function | 44 | import |
659+
| l_calls.py:51 | ControlFlowNode for g() | 'a' | builtin-class str | 51 | import |
660+
| l_calls.py:51 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 | import |
661+
| l_calls.py:52 | ControlFlowNode for Attribute | Attribute | builtin-class method | 52 | import |
662+
| l_calls.py:52 | ControlFlowNode for Attribute() | 'a' | builtin-class str | 52 | import |
663+
| l_calls.py:52 | ControlFlowNode for F | class F | builtin-class type | 47 | import |
664+
| l_calls.py:52 | ControlFlowNode for F() | F() | class F | 52 | import |
665+
| l_calls.py:52 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 | import |
666+
| l_calls.py:53 | ControlFlowNode for Attribute | Function m | builtin-class function | 48 | import |
667+
| l_calls.py:53 | ControlFlowNode for Attribute() | 'b' | builtin-class str | 53 | import |
668+
| l_calls.py:53 | ControlFlowNode for F | class F | builtin-class type | 47 | import |
669+
| l_calls.py:53 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 | import |
638670
| m_attributes.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | import |
639671
| m_attributes.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | import |
640672
| m_attributes.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | import |

python/ql/test/library-tests/PointsTo/new/PointsToWithType.expected

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,37 @@ WARNING: Predicate points_to has been deprecated and may be removed in future (P
708708
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 39 |
709709
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 4 | builtin-class int | 39 |
710710
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 5 | builtin-class int | 39 |
711+
| l_calls.py:41 | ControlFlowNode for Str | 'a' | builtin-class str | 41 |
712+
| l_calls.py:41 | ControlFlowNode for Str | 'b' | builtin-class str | 41 |
713+
| l_calls.py:41 | ControlFlowNode for Str | 'c' | builtin-class str | 41 |
714+
| l_calls.py:41 | ControlFlowNode for Tuple | Tuple | builtin-class tuple | 41 |
715+
| l_calls.py:41 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 |
716+
| l_calls.py:42 | ControlFlowNode for f | Function f | builtin-class function | 29 |
717+
| l_calls.py:42 | ControlFlowNode for f() | args | builtin-class tuple | 29 |
718+
| l_calls.py:42 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 |
719+
| l_calls.py:44 | ControlFlowNode for FunctionExpr | Function g | builtin-class function | 44 |
720+
| l_calls.py:44 | ControlFlowNode for g | Function g | builtin-class function | 44 |
721+
| l_calls.py:45 | ControlFlowNode for a | 'a' | builtin-class str | 51 |
722+
| l_calls.py:47 | ControlFlowNode for ClassExpr | class F | builtin-class type | 47 |
723+
| l_calls.py:47 | ControlFlowNode for F | class F | builtin-class type | 47 |
724+
| l_calls.py:47 | ControlFlowNode for object | builtin-class object | builtin-class type | 47 |
725+
| l_calls.py:48 | ControlFlowNode for FunctionExpr | Function m | builtin-class function | 48 |
726+
| l_calls.py:48 | ControlFlowNode for None | NoneType None | builtin-class NoneType | 48 |
727+
| l_calls.py:48 | ControlFlowNode for m | Function m | builtin-class function | 48 |
728+
| l_calls.py:49 | ControlFlowNode for x | 'a' | builtin-class str | 52 |
729+
| l_calls.py:49 | ControlFlowNode for x | 'b' | builtin-class str | 53 |
730+
| l_calls.py:51 | ControlFlowNode for g | Function g | builtin-class function | 44 |
731+
| l_calls.py:51 | ControlFlowNode for g() | 'a' | builtin-class str | 51 |
732+
| l_calls.py:51 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 |
733+
| l_calls.py:52 | ControlFlowNode for Attribute | Attribute | builtin-class method | 52 |
734+
| l_calls.py:52 | ControlFlowNode for Attribute() | 'a' | builtin-class str | 52 |
735+
| l_calls.py:52 | ControlFlowNode for F | class F | builtin-class type | 47 |
736+
| l_calls.py:52 | ControlFlowNode for F() | F() | class F | 52 |
737+
| l_calls.py:52 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 |
738+
| l_calls.py:53 | ControlFlowNode for Attribute | Function m | builtin-class function | 48 |
739+
| l_calls.py:53 | ControlFlowNode for Attribute() | 'b' | builtin-class str | 53 |
740+
| l_calls.py:53 | ControlFlowNode for F | class F | builtin-class type | 47 |
741+
| l_calls.py:53 | ControlFlowNode for t | Tuple | builtin-class tuple | 41 |
711742
| s_scopes.py:4 | ControlFlowNode for True | bool True | builtin-class bool | 4 |
712743
| s_scopes.py:4 | ControlFlowNode for float | bool True | builtin-class bool | 4 |
713744
| s_scopes.py:7 | ControlFlowNode for C2 | class C2 | builtin-class type | 7 |

python/ql/test/library-tests/PointsTo/new/Values.expected

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@
468468
| l_calls.py:25 | ControlFlowNode for a | runtime | class Owner | builtin-class type |
469469
| l_calls.py:29 | ControlFlowNode for FunctionExpr | import | Function f | builtin-class function |
470470
| l_calls.py:30 | ControlFlowNode for args | code/l_calls.py:37 from import | (int 1, int 2, int 3, ) | builtin-class tuple |
471+
| l_calls.py:30 | ControlFlowNode for args | code/l_calls.py:42 from import | ('a', 'b', 'c', ) | builtin-class tuple |
471472
| l_calls.py:30 | ControlFlowNode for args | runtime | instance of tuple | builtin-class tuple |
472473
| l_calls.py:32 | ControlFlowNode for ClassExpr | import | class E | builtin-class type |
473474
| l_calls.py:32 | ControlFlowNode for object | import | builtin-class object | builtin-class type |
@@ -498,6 +499,33 @@
498499
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | import | int 3 | builtin-class int |
499500
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | import | int 4 | builtin-class int |
500501
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | import | int 5 | builtin-class int |
502+
| l_calls.py:41 | ControlFlowNode for Str | import | 'a' | builtin-class str |
503+
| l_calls.py:41 | ControlFlowNode for Str | import | 'b' | builtin-class str |
504+
| l_calls.py:41 | ControlFlowNode for Str | import | 'c' | builtin-class str |
505+
| l_calls.py:41 | ControlFlowNode for Tuple | import | ('a', 'b', 'c', ) | builtin-class tuple |
506+
| l_calls.py:42 | ControlFlowNode for f | import | Function f | builtin-class function |
507+
| l_calls.py:42 | ControlFlowNode for f() | import | ('a', 'b', 'c', ) | builtin-class tuple |
508+
| l_calls.py:42 | ControlFlowNode for t | import | ('a', 'b', 'c', ) | builtin-class tuple |
509+
| l_calls.py:44 | ControlFlowNode for FunctionExpr | import | Function g | builtin-class function |
510+
| l_calls.py:45 | ControlFlowNode for a | code/l_calls.py:51 from import | 'a' | builtin-class str |
511+
| l_calls.py:47 | ControlFlowNode for ClassExpr | import | class F | builtin-class type |
512+
| l_calls.py:47 | ControlFlowNode for object | import | builtin-class object | builtin-class type |
513+
| l_calls.py:48 | ControlFlowNode for FunctionExpr | import | Function F.m | builtin-class function |
514+
| l_calls.py:48 | ControlFlowNode for None | import | None | builtin-class NoneType |
515+
| l_calls.py:49 | ControlFlowNode for x | code/l_calls.py:52 from import | 'a' | builtin-class str |
516+
| l_calls.py:49 | ControlFlowNode for x | code/l_calls.py:53 from import | 'b' | builtin-class str |
517+
| l_calls.py:51 | ControlFlowNode for g | import | Function g | builtin-class function |
518+
| l_calls.py:51 | ControlFlowNode for g() | import | 'a' | builtin-class str |
519+
| l_calls.py:51 | ControlFlowNode for t | import | ('a', 'b', 'c', ) | builtin-class tuple |
520+
| l_calls.py:52 | ControlFlowNode for Attribute | import | Method(Function F.m, F()) | builtin-class method |
521+
| l_calls.py:52 | ControlFlowNode for Attribute() | import | 'a' | builtin-class str |
522+
| l_calls.py:52 | ControlFlowNode for F | import | class F | builtin-class type |
523+
| l_calls.py:52 | ControlFlowNode for F() | import | F() | class F |
524+
| l_calls.py:52 | ControlFlowNode for t | import | ('a', 'b', 'c', ) | builtin-class tuple |
525+
| l_calls.py:53 | ControlFlowNode for Attribute | import | Function F.m | builtin-class function |
526+
| l_calls.py:53 | ControlFlowNode for Attribute() | import | 'b' | builtin-class str |
527+
| l_calls.py:53 | ControlFlowNode for F | import | class F | builtin-class type |
528+
| l_calls.py:53 | ControlFlowNode for t | import | ('a', 'b', 'c', ) | builtin-class tuple |
501529
| m_attributes.py:3 | ControlFlowNode for ClassExpr | import | class C | builtin-class type |
502530
| m_attributes.py:3 | ControlFlowNode for object | import | builtin-class object | builtin-class type |
503531
| m_attributes.py:5 | ControlFlowNode for FunctionExpr | import | Function C.__init__ | builtin-class function |

0 commit comments

Comments
 (0)