Python: Support super().__new__(cls)

This commit is contained in:
Rasmus Wriedt Larsen
2022-10-24 15:35:53 +02:00
parent 2b76964f7f
commit 8a56b48357
3 changed files with 23 additions and 3 deletions

View File

@@ -468,6 +468,13 @@ private TypeTrackingNode classInstanceTracker(TypeTracker t, Class cls) {
t.start() and
resolveClassCall(result.(CallCfgNode).asCfgNode(), cls)
or
// result of `super().__new__` as used in a `__new__` method implementation
t.start() and
exists(Class classUsedInSuper |
fromSuperNewCall(result.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
classUsedInSuper = getADirectSuperclass*(cls)
)
or
exists(TypeTracker t2 | result = classInstanceTracker(t2, cls).track(t2, t)) and
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
}
@@ -856,6 +863,16 @@ private module MethodCalls {
attr.accesses(self, functionName)
}
/**
* Like `fromSuper`, but only for `__new__`, and without requirement for being able to
* resolve the call to a known target (since the only super class might be the
* builtin `object`, so we never have the implementation of `__new__` in the DB).
*/
predicate fromSuperNewCall(CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and
self in [classTracker(_), clsTracker(_)]
}
/**
* Holds if `call` is a call to a method `target`, derived from a use of `super`, either
* as:

View File

@@ -21,6 +21,9 @@ pointsTo_found_typeTracker_notFound
| code/type_tracking_limitation.py:8:1:8:3 | ControlFlowNode for x() | my_func |
typeTracker_found_pointsTo_notFound
| code/callable_as_argument.py:29:5:29:12 | ControlFlowNode for Attribute() | test_class.InsideTestFunc.sm |
| code/class_construction.py:44:9:44:26 | ControlFlowNode for Attribute() | WithNew.some_method |
| code/class_construction.py:61:9:61:26 | ControlFlowNode for Attribute() | WithNew.some_method |
| code/class_construction.py:75:9:75:27 | ControlFlowNode for Attribute() | ExtraCallToInit.__init__ |
| code/class_special_methods.py:22:9:22:16 | ControlFlowNode for self() | Base.__call__ |
| code/class_special_methods.py:22:9:22:16 | ControlFlowNode for self() | Sub.__call__ |
| code/class_special_methods.py:33:1:33:5 | ControlFlowNode for b() | Base.__call__ |

View File

@@ -41,7 +41,7 @@ class WithNew(object):
print("WithNew.__new__", arg)
inst = super().__new__(cls)
assert isinstance(inst, cls)
inst.some_method() # $ MISSING: pt,tt=WithNew.some_method
inst.some_method() # $ tt=WithNew.some_method
return inst
def __init__(self, arg=None):
@@ -58,7 +58,7 @@ class WithNewSub(WithNew):
print("WithNewSub.__new__")
inst = super().__new__(cls, 44.1) # $ pt,tt=WithNew.__new__
assert isinstance(inst, cls)
inst.some_method() # $ MISSING: pt,tt=WithNew.some_method
inst.some_method() # $ tt=WithNew.some_method
return inst
WithNewSub() # $ tt=WithNewSub.__new__ tt=WithNew.__init__
@@ -72,7 +72,7 @@ class ExtraCallToInit(object):
inst = super().__new__(cls)
assert isinstance(inst, cls)
# you're not supposed to do this, since it will cause the __init__ method will be run twice.
inst.__init__(1001) # $ MISSING: pt,tt=ExtraCallToInit.__init__
inst.__init__(1001) # $ tt=ExtraCallToInit.__init__
return inst
def __init__(self, arg):