mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Python: CG trace: Better handling of builtins without __module__
Not 100% perfect, but better
This commit is contained in:
@@ -135,12 +135,8 @@ class XMLExternalCallee extends XMLCallee {
|
|||||||
|
|
||||||
Builtin getACallee() {
|
Builtin getACallee() {
|
||||||
exists(Builtin mod |
|
exists(Builtin mod |
|
||||||
not this.get_module_data() = "None" and
|
|
||||||
mod.isModule() and
|
mod.isModule() and
|
||||||
mod.getName() = this.get_module_data()
|
mod.getName() = this.get_module_data()
|
||||||
or
|
|
||||||
this.get_module_data() = "None" and
|
|
||||||
mod = Builtin::builtinModule()
|
|
||||||
|
|
|
|
||||||
result = traverse_qualname(mod, this.get_qualname_data())
|
result = traverse_qualname(mod, this.get_qualname_data())
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -95,6 +95,41 @@ class Callee:
|
|||||||
|
|
||||||
|
|
||||||
BUILTIN_FUNCTION_OR_METHOD = type(print)
|
BUILTIN_FUNCTION_OR_METHOD = type(print)
|
||||||
|
METHOD_DESCRIPTOR_TYPE = type(dict.get)
|
||||||
|
|
||||||
|
|
||||||
|
_unknown_module_fixup_cache = dict()
|
||||||
|
|
||||||
|
|
||||||
|
def _unkown_module_fixup(func):
|
||||||
|
# TODO: Doesn't work for everything (for example: `OrderedDict.fromkeys`, `object.__new__`)
|
||||||
|
|
||||||
|
module = func.__module__
|
||||||
|
qualname = func.__qualname__
|
||||||
|
cls_name, method_name = qualname.split(".")
|
||||||
|
|
||||||
|
key = (module, qualname)
|
||||||
|
if key in _unknown_module_fixup_cache:
|
||||||
|
return _unknown_module_fixup_cache[key]
|
||||||
|
|
||||||
|
matching_classes = list()
|
||||||
|
for klass in object.__subclasses__():
|
||||||
|
# type(dict.get) == METHOD_DESCRIPTOR_TYPE
|
||||||
|
# type(dict.__new__) == BUILTIN_FUNCTION_OR_METHOD
|
||||||
|
if klass.__qualname__ == cls_name and type(
|
||||||
|
getattr(klass, method_name, None)
|
||||||
|
) in [BUILTIN_FUNCTION_OR_METHOD, METHOD_DESCRIPTOR_TYPE]:
|
||||||
|
matching_classes.append(klass)
|
||||||
|
|
||||||
|
if len(matching_classes) == 1:
|
||||||
|
klass = matching_classes[0]
|
||||||
|
ret = klass.__module__
|
||||||
|
else:
|
||||||
|
if DEBUG:
|
||||||
|
LOGGER.debug(f"Found more than one matching class for {module} {qualname}")
|
||||||
|
ret = None
|
||||||
|
_unknown_module_fixup_cache[key] = ret
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@better_compare_for_dataclass
|
@better_compare_for_dataclass
|
||||||
@@ -109,9 +144,19 @@ class ExternalCallee(Callee):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_arg(cls, func):
|
def from_arg(cls, func):
|
||||||
|
# builtin bound methods seems to always return `None` for __module__, but we
|
||||||
|
# might be able to recover the lost information by looking through all classes.
|
||||||
|
# For example, `dict().get.__module__ is None` and `dict().get.__qualname__ ==
|
||||||
|
# "dict.get"`
|
||||||
|
|
||||||
|
module = func.__module__
|
||||||
|
qualname = func.__qualname__
|
||||||
|
if module is None and qualname.count(".") == 1:
|
||||||
|
module = _unkown_module_fixup(func)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
module=func.__module__,
|
module=module,
|
||||||
qualname=func.__qualname__,
|
qualname=qualname,
|
||||||
is_builtin=type(func) == BUILTIN_FUNCTION_OR_METHOD,
|
is_builtin=type(func) == BUILTIN_FUNCTION_OR_METHOD,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
d = dict()
|
||||||
|
|
||||||
|
d.get("foo") or d.get("bar")
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import socket
|
||||||
|
|
||||||
|
sock = socket.socket()
|
||||||
|
print(sock.getsockname())
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import io
|
||||||
|
|
||||||
|
# the `io.open` is just an alias for `_io.open`, but we record the external callee as `io.open` :|
|
||||||
|
io.open("foo")
|
||||||
Reference in New Issue
Block a user