Files
Taus c9e9deb41e Python: Adapt to a points-to-less world
Technically we still depend on points-to in that we still mention
`PythonFunctionValue` and `ClassValue` in the query. However, we
immediately move to working with the corresponding `Function` and
`Class` AST nodes, and so we're not really using points-to. (The reason
for doing things this way is that otherwise the `.toString()` for all of
the alerts would change, which would make the diff hard to interpret.
This way, it should be fairly simple to see which changes are actually
relevant.)

We do lose some precision when moving away from points-to, and this is
reflected in the changes in the `.expected` file. In particular we no
longer do complicated tracking of values, but rather look at the
syntactic structure of the classes in question. This causes us to lose
out on some results where a special method is defined elsewhere, and
causes a single FP where a special method initially has the wrong
signature, but is subsequently overwritten with a function with the
correct signature.

We also lose out on results having to do with default values, as these
are now disabled.

Finally, it was necessary to add special handling of methods marked with
the `staticmethod` decorator, as these expect to receive fewer
arguments. This was motivated by a MRVA run, where e.g. sympy showed a
lot of examples along the lines of
```
@staticmethod
def __abs__():
   return ...
```
2025-03-14 16:49:33 +00:00

112 lines
2.0 KiB
Python

#Signature overridden wrong
class Base(object):
def ok1(self, arg1, arg2 = 2):
return arg1, arg2
def ok2(self, arg1, arg2 = 2):
return arg1, arg2
def grossly_wrong1(self, arg1, arg2):
return arg1, arg2
def grossly_wrong2(self, arg1, arg2):
return arg1, arg2
def strictly_wrong1(self, arg1, arg2 = 2):
return arg1, arg2
def strictly_wrong2(self, arg1, arg2 = 2):
return arg1, arg2
class Derived(Base):
def ok1(self, arg1, arg2 = 2):
return arg1, arg2
def ok2(self, arg1, arg2 = 2, arg3 = 3):
return arg1, arg2, arg3
def grossly_wrong1(self, arg1):
return arg1
def grossly_wrong2(self, arg1, arg2, arg3):
return arg1, arg2, arg3
def strictly_wrong1(self, arg1):
return arg1
def strictly_wrong2(self, arg1, arg2, arg3 = 3):
return arg1, arg2, arg3
#Special method signatures
class Special(object):
def __add__(self, x):
return self, x
def __pos__(self):
return self
def __str__(self):
return repr(self)
class WrongSpecials(object):
def __div__(self, x, y):
return self, x, y
def __mul__(self):
return self
def __neg__(self, other):
return self, other
def __exit__(self, arg0, arg1):
return arg0 == arg1
def __repr__():
return ""
def __add__(self, other="Unused default"):
return 4
@staticmethod
def __abs__():
return 42
class OKSpecials(object):
def __del__():
state = some_state()
def __del__(self):
use_the_state(state)
return __del__
__del__ = __del__()
__add__ = lambda x, y : do_add(x, y)
class NotOKSpecials(object):
__sub__ = lambda x : do_neg(x)
#Correctly overridden builtin method
class LoggingDict(dict):
def pop(self):
print("pop")
return dict.pop(self)
class MoreSpecialMethods:
@staticmethod
def __abs__():
return 42