Update tests for calls to init + fixes

This commit is contained in:
Joe Farebrother
2025-07-03 15:48:52 +01:00
parent 9ac95266c7
commit c9932e187a
3 changed files with 78 additions and 70 deletions

View File

@@ -95,13 +95,15 @@ predicate missingCallToSuperclassMethod(Class base, Function shouldCall, string
predicate missingCallToSuperclassMethodRestricted(Class base, Function shouldCall, string name) {
missingCallToSuperclassMethod(base, shouldCall, name) and
not exists(Class subBase |
subBase = getADirectSubclass+(base) and
missingCallToSuperclassMethod(subBase, shouldCall, name)
not exists(Class superBase |
// Alert only on the highest base class that has the issue
superBase = getADirectSuperclass+(base) and
missingCallToSuperclassMethod(superBase, shouldCall, name)
) and
not exists(Function superShouldCall |
superShouldCall.getScope() = getADirectSuperclass+(shouldCall.getScope()) and
missingCallToSuperclassMethod(base, superShouldCall, name)
not exists(Function subShouldCall |
// Mention in the alert only the lowest method we're missing the call to
subShouldCall.getScope() = getADirectSubclass+(shouldCall.getScope()) and
missingCallToSuperclassMethod(base, subShouldCall, name)
)
}

View File

@@ -1,3 +1,5 @@
| missing_init.py:12:1:12:13 | class B3 | Class B3 may not be initialized properly as $@ is not called from its $@. | missing_init.py:9:5:9:23 | Function __init__ | method B2.__init__ | missing_init.py:14:5:14:23 | Function __init__ | __init__ method |
| missing_init.py:39:1:39:21 | class IUVT | Class IUVT may not be initialized properly as $@ is not called from its $@. | missing_init.py:30:5:30:23 | Function __init__ | method UT.__init__ | missing_init.py:26:5:26:23 | Function __init__ | __init__ method |
| missing_init.py:72:1:72:13 | class AB | Class AB may not be initialized properly as $@ is not called from its $@. | missing_init.py:69:5:69:23 | Function __init__ | method AA.__init__ | missing_init.py:75:5:75:23 | Function __init__ | __init__ method |
| missing_init.py:14:5:14:23 | Function __init__ | This initialization method does not call $@, which may leave $@ partially initialized. | missing_init.py:9:5:9:23 | Function __init__ | B2.__init__ | missing_init.py:13:1:13:13 | Class B3 | B3 |
| missing_init.py:29:5:29:23 | Function __init__ | This initialization method does not call super().__init__, which may cause $@ to be missed during the initialization of $@. | missing_init.py:33:5:33:23 | Function __init__ | UT.__init__ | missing_init.py:42:1:42:21 | Class IUVT | IUVT |
| missing_init.py:70:5:70:23 | Function __init__ | This initialization method does not call $@, which may leave $@ partially initialized. | missing_init.py:64:5:64:23 | Function __init__ | AA.__init__ | missing_init.py:67:1:67:13 | Class AB | AB |
| missing_init.py:124:9:124:27 | Function __init__ | This initialization method does not call $@, which may leave $@ partially initialized. | missing_init.py:117:5:117:23 | Function __init__ | DA.__init__ | missing_init.py:122:5:122:17 | Class DC | DC |
| missing_init.py:134:5:134:23 | Function __init__ | This initialization method does not call $@, which may leave $@ partially initialized. | missing_init.py:117:5:117:23 | Function __init__ | DA.__init__ | missing_init.py:132:1:132:13 | Class DD | DD |

View File

@@ -2,18 +2,21 @@
class B1(object):
def __init__(self):
do_something()
print("B1 init")
class B2(B1):
def __init__(self):
print("B2 init")
B1.__init__(self)
class B3(B2):
def __init__(self):
class B3(B2): # $ Alert
def __init__(self):
print("B3 init")
B1.__init__(self)
B3()
#OK if superclass __init__ is builtin as
#builtin classes tend to rely on __new__
class MyException(Exception):
@@ -23,11 +26,11 @@ class MyException(Exception):
#ODASA-4107
class IUT(object):
def __init__(self):
def __init__(self):
print("IUT init")
class UT(object):
def __init__(self):
def __init__(self):
print("UT init")
class PU(object):
@@ -36,150 +39,151 @@ class PU(object):
class UVT(UT, PU):
pass
class IUVT(IUT, UVT):
class IUVT(IUT, UVT): # $ Alert
pass
#False positive
print("IUVT")
IUVT()
class M1(object):
def __init__(self):
print("A")
print("M1 init")
class M2(object):
pass
class Mult(M2, M1):
def __init__(self):
super(Mult, self).__init__() # Calls M1.__init__
print("Mult init")
super(Mult, self).__init__() # OK - Calls M1.__init__
class X:
def __init__(self):
do_something()
class Y(X):
@decorated
def __init__(self):
X.__init__(self)
class Z(Y):
def __init__(self):
Y.__init__(self)
Mult()
class AA(object):
def __init__(self):
do_something()
print("AA init")
class AB(AA):
class AB(AA): # $ Alert
#Don't call super class init
def __init__(self):
do_something()
# Doesn't call super class init
def __init__(self):
print("AB init")
class AC(AB):
def __init__(self):
#Missing call to AA.__init__ but not AC's fault.
# Doesn't call AA init, but we don't alert here as the issue is with AB.
print("AC init")
super(AC, self).__init__()
AC()
import six
import abc
class BA(object):
def __init__(self):
do_something()
print("BA init")
@six.add_metaclass(abc.ABCMeta)
class BB(BA):
def __init__(self):
print("BB init")
super(BB,self).__init__()
BB()
@six.add_metaclass(abc.ABCMeta)
class CA(object):
def __init__(self):
do_something()
print("CA init")
class CB(BA):
class CB(CA):
def __init__(self):
print("CB init")
super(CB,self).__init__()
CB()
#ODASA-5799
class DA(object):
def __init__(self):
do_something()
print("DA init")
class DB(DA):
class DC(DA):
class DC(DA): # $ SPURIOUS: Alert # We only consider direct super calls, so have an FP here
def __init__(self):
def __init__(self):
print("DC init")
sup = super(DB.DC, self)
sup.__init__()
#Simpler variants
class DD(DA):
DB.DC()
def __init__(self):
#Simpler variants
class DD(DA): # $ SPURIOUS: Alert # We only consider direct super calls, so have an FP here
def __init__(self):
print("DD init")
sup = super(DD, self)
sup.__init__()
DD()
class DE(DA):
class DF(DA):
class DF(DA): # No alert here
def __init__(self):
def __init__(self):
print("DF init")
sup = super(DE.DF, self).__init__()
DE.DF()
class FA(object):
def __init__(self):
pass
pass # does nothing, thus is considered a trivial method and ok to not call
class FB(object):
def __init__(self):
do_something()
print("FB init")
class FC(FA, FB):
def __init__(self):
#OK to skip call to FA.__init__ as that does nothing.
# No alert here - ok to skip call to trivial FA init
FB.__init__(self)
#Potential false positives.
class ConfusingInit(B1):
def __init__(self):
def __init__(self): # We track this correctly and don't alert.
super_call = super(ConfusingInit, self).__init__
super_call()
# Library class
import collections
class G1(collections.Counter):
class G1:
def __init__(self):
collections.Counter.__init__(self)
class G2(G1):
print("G1 init")
class G2:
def __init__(self):
super(G2, self).__init__()
print("G2 init")
class G3(collections.Counter):
def __init__(self):
super(G3, self).__init__()
class G4(G3):
def __init__(self):
G3.__init__(self)
class G3(G1, G2):
def __init__(self):
print("G3 init")
for cls in self.__class__.__bases__:
cls.__init__(self) # We dont track which classes this could refer to, but assume it calls all required init methods and don't alert.