Skip to content

Commit 4df946b

Browse files
RasmusWLtausbn
andcommitted
Python: call-graph: Don't design for special method calls yet
The `call` arguments were not `CallNode`s before, to allow for easier support of special method calls, such as `a + b` going to `__add__`. However, this is not implemented yet, so for now we can keep things simple. Co-authored-by: Taus <[email protected]>
1 parent b83fc3b commit 4df946b

File tree

1 file changed

+65
-71
lines changed

1 file changed

+65
-71
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll

Lines changed: 65 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ private module MethodCalls {
950950
)
951951
}
952952

953-
predicate resolveMethodCall(ControlFlowNode call, Function target, CallType type, Node self) {
953+
predicate resolveMethodCall(CallNode call, Function target, CallType type, Node self) {
954954
(
955955
directCall(call, target, _, _, _, self)
956956
or
@@ -1046,11 +1046,11 @@ predicate resolveClassInstanceCall(CallNode call, Function target, Node self) {
10461046
* Holds if `call` is a call to the `target`, with call-type `type`.
10471047
*/
10481048
cached
1049-
predicate resolveCall(ControlFlowNode call, Function target, CallType type) {
1049+
predicate resolveCall(CallNode call, Function target, CallType type) {
10501050
Stages::DataFlow::ref() and
10511051
(
10521052
type instanceof CallTypePlainFunction and
1053-
call.(CallNode).getFunction() = functionTracker(target).asCfgNode() and
1053+
call.getFunction() = functionTracker(target).asCfgNode() and
10541054
not exists(Class cls | cls.getAMethod() = target)
10551055
or
10561056
resolveMethodCall(call, target, type, _)
@@ -1128,83 +1128,77 @@ predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
11281128
* sending both `self` arguments to that function, which is by definition the right thing to do.
11291129
*/
11301130
cached
1131-
predicate getCallArg(
1132-
ControlFlowNode call, Function target, CallType type, Node arg, ArgumentPosition apos
1133-
) {
1131+
predicate getCallArg(CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos) {
11341132
Stages::DataFlow::ref() and
1133+
resolveCall(call, target, type) and
11351134
(
1136-
// normal calls with a real call node
1137-
resolveCall(call, target, type) and
1138-
call instanceof CallNode and
1135+
type instanceof CallTypePlainFunction and
1136+
normalCallArg(call, arg, apos)
1137+
or
1138+
// self argument for normal method calls
1139+
type instanceof CallTypeNormalMethod and
1140+
apos.isSelf() and
1141+
resolveMethodCall(call, target, type, arg) and
1142+
// dataflow lib has requirement that arguments and calls are in same enclosing callable.
1143+
exists(CfgNode cfgNode | cfgNode.getNode() = call |
1144+
cfgNode.getEnclosingCallable() = arg.getEnclosingCallable()
1145+
)
1146+
or
1147+
// cls argument for classmethod calls
1148+
type instanceof CallTypeClassMethod and
1149+
apos.isSelf() and
1150+
resolveMethodCall(call, target, type, arg) and
1151+
(arg = classTracker(_) or arg = clsArgumentTracker(_)) and
1152+
// dataflow lib has requirement that arguments and calls are in same enclosing callable.
1153+
exists(CfgNode cfgNode | cfgNode.getNode() = call |
1154+
cfgNode.getEnclosingCallable() = arg.getEnclosingCallable()
1155+
)
1156+
or
1157+
// normal arguments for method calls
11391158
(
1140-
type instanceof CallTypePlainFunction and
1141-
normalCallArg(call, arg, apos)
1159+
type instanceof CallTypeNormalMethod or
1160+
type instanceof CallTypeStaticMethod or
1161+
type instanceof CallTypeClassMethod
1162+
) and
1163+
normalCallArg(call, arg, apos)
1164+
or
1165+
// method as plain function call.
1166+
//
1167+
// argument index 0 of call has position self (and MUST be given as positional
1168+
// argument in call). This also means that call-arguments are shifted by 1, such
1169+
// that argument index 1 of call has argument position 0
1170+
type instanceof CallTypeMethodAsPlainFunction and
1171+
(
1172+
apos.isSelf() and arg.asCfgNode() = call.(CallNode).getArg(0)
11421173
or
1143-
// self argument for normal method calls
1144-
type instanceof CallTypeNormalMethod and
1145-
apos.isSelf() and
1146-
resolveMethodCall(call, target, type, arg) and
1147-
// dataflow lib has requirement that arguments and calls are in same enclosing callable.
1148-
exists(CfgNode cfgNode | cfgNode.getNode() = call |
1149-
cfgNode.getEnclosingCallable() = arg.getEnclosingCallable()
1150-
)
1174+
not apos.isPositional(_) and normalCallArg(call, arg, apos)
11511175
or
1152-
// cls argument for classmethod calls
1153-
type instanceof CallTypeClassMethod and
1154-
apos.isSelf() and
1155-
resolveMethodCall(call, target, type, arg) and
1156-
(arg = classTracker(_) or arg = clsArgumentTracker(_)) and
1157-
// dataflow lib has requirement that arguments and calls are in same enclosing callable.
1158-
exists(CfgNode cfgNode | cfgNode.getNode() = call |
1159-
cfgNode.getEnclosingCallable() = arg.getEnclosingCallable()
1176+
exists(ArgumentPosition normalPos, int index |
1177+
apos.isPositional(index - 1) and
1178+
normalPos.isPositional(index) and
1179+
normalCallArg(call, arg, normalPos)
11601180
)
1181+
)
1182+
or
1183+
// class call
1184+
type instanceof CallTypeClass and
1185+
(
1186+
// only pass synthetic node for created object to __init__, and not __new__ since
1187+
// __new__ is a classmethod.
1188+
target = invokedFunctionFromClassConstruction(_, "__init__") and
1189+
apos.isSelf() and
1190+
arg = TSyntheticPreUpdateNode(call)
11611191
or
1162-
// normal arguments for method calls
1163-
(
1164-
type instanceof CallTypeNormalMethod or
1165-
type instanceof CallTypeStaticMethod or
1166-
type instanceof CallTypeClassMethod
1167-
) and
11681192
normalCallArg(call, arg, apos)
1193+
)
1194+
or
1195+
// call on class instance, which goes to `__call__` method
1196+
type instanceof CallTypeClassInstanceCall and
1197+
(
1198+
apos.isSelf() and
1199+
resolveClassInstanceCall(call, target, arg)
11691200
or
1170-
// method as plain function call.
1171-
//
1172-
// argument index 0 of call has position self (and MUST be given as positional
1173-
// argument in call). This also means that call-arguments are shifted by 1, such
1174-
// that argument index 1 of call has argument position 0
1175-
type instanceof CallTypeMethodAsPlainFunction and
1176-
(
1177-
apos.isSelf() and arg.asCfgNode() = call.(CallNode).getArg(0)
1178-
or
1179-
not apos.isPositional(_) and normalCallArg(call, arg, apos)
1180-
or
1181-
exists(ArgumentPosition normalPos, int index |
1182-
apos.isPositional(index - 1) and
1183-
normalPos.isPositional(index) and
1184-
normalCallArg(call, arg, normalPos)
1185-
)
1186-
)
1187-
or
1188-
// class call
1189-
type instanceof CallTypeClass and
1190-
(
1191-
// only pass synthetic node for created object to __init__, and not __new__ since
1192-
// __new__ is a classmethod.
1193-
target = invokedFunctionFromClassConstruction(_, "__init__") and
1194-
apos.isSelf() and
1195-
arg = TSyntheticPreUpdateNode(call)
1196-
or
1197-
normalCallArg(call, arg, apos)
1198-
)
1199-
or
1200-
// call on class instance, which goes to `__call__` method
1201-
type instanceof CallTypeClassInstanceCall and
1202-
(
1203-
apos.isSelf() and
1204-
resolveClassInstanceCall(call, target, arg)
1205-
or
1206-
normalCallArg(call, arg, apos)
1207-
)
1201+
normalCallArg(call, arg, apos)
12081202
)
12091203
)
12101204
}

0 commit comments

Comments
 (0)