Skip to content

Commit 8a56b48

Browse files
committed
Python: Support super().__new__(cls)
1 parent 2b76964 commit 8a56b48

File tree

3 files changed

+23
-3
lines changed

3 files changed

+23
-3
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,13 @@ private TypeTrackingNode classInstanceTracker(TypeTracker t, Class cls) {
468468
t.start() and
469469
resolveClassCall(result.(CallCfgNode).asCfgNode(), cls)
470470
or
471+
// result of `super().__new__` as used in a `__new__` method implementation
472+
t.start() and
473+
exists(Class classUsedInSuper |
474+
fromSuperNewCall(result.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
475+
classUsedInSuper = getADirectSuperclass*(cls)
476+
)
477+
or
471478
exists(TypeTracker t2 | result = classInstanceTracker(t2, cls).track(t2, t)) and
472479
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
473480
}
@@ -856,6 +863,16 @@ private module MethodCalls {
856863
attr.accesses(self, functionName)
857864
}
858865

866+
/**
867+
* Like `fromSuper`, but only for `__new__`, and without requirement for being able to
868+
* resolve the call to a known target (since the only super class might be the
869+
* builtin `object`, so we never have the implementation of `__new__` in the DB).
870+
*/
871+
predicate fromSuperNewCall(CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
872+
fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and
873+
self in [classTracker(_), clsTracker(_)]
874+
}
875+
859876
/**
860877
* Holds if `call` is a call to a method `target`, derived from a use of `super`, either
861878
* as:

python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ pointsTo_found_typeTracker_notFound
2121
| code/type_tracking_limitation.py:8:1:8:3 | ControlFlowNode for x() | my_func |
2222
typeTracker_found_pointsTo_notFound
2323
| code/callable_as_argument.py:29:5:29:12 | ControlFlowNode for Attribute() | test_class.InsideTestFunc.sm |
24+
| code/class_construction.py:44:9:44:26 | ControlFlowNode for Attribute() | WithNew.some_method |
25+
| code/class_construction.py:61:9:61:26 | ControlFlowNode for Attribute() | WithNew.some_method |
26+
| code/class_construction.py:75:9:75:27 | ControlFlowNode for Attribute() | ExtraCallToInit.__init__ |
2427
| code/class_special_methods.py:22:9:22:16 | ControlFlowNode for self() | Base.__call__ |
2528
| code/class_special_methods.py:22:9:22:16 | ControlFlowNode for self() | Sub.__call__ |
2629
| code/class_special_methods.py:33:1:33:5 | ControlFlowNode for b() | Base.__call__ |

python/ql/test/experimental/library-tests/CallGraph/code/class_construction.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __new__(cls, arg):
4141
print("WithNew.__new__", arg)
4242
inst = super().__new__(cls)
4343
assert isinstance(inst, cls)
44-
inst.some_method() # $ MISSING: pt,tt=WithNew.some_method
44+
inst.some_method() # $ tt=WithNew.some_method
4545
return inst
4646

4747
def __init__(self, arg=None):
@@ -58,7 +58,7 @@ def __new__(cls):
5858
print("WithNewSub.__new__")
5959
inst = super().__new__(cls, 44.1) # $ pt,tt=WithNew.__new__
6060
assert isinstance(inst, cls)
61-
inst.some_method() # $ MISSING: pt,tt=WithNew.some_method
61+
inst.some_method() # $ tt=WithNew.some_method
6262
return inst
6363

6464
WithNewSub() # $ tt=WithNewSub.__new__ tt=WithNew.__init__
@@ -72,7 +72,7 @@ def __new__(cls, arg):
7272
inst = super().__new__(cls)
7373
assert isinstance(inst, cls)
7474
# you're not supposed to do this, since it will cause the __init__ method will be run twice.
75-
inst.__init__(1001) # $ MISSING: pt,tt=ExtraCallToInit.__init__
75+
inst.__init__(1001) # $ tt=ExtraCallToInit.__init__
7676
return inst
7777

7878
def __init__(self, arg):

0 commit comments

Comments
 (0)