mirror of
https://github.com/github/codeql.git
synced 2026-03-30 20:28:15 +02:00
Python: Model @typing.overload in method resolution
Adds `hasOverloadDecorator` as a predicate on functions. It looks for decorators called `overload` or `something.overload` (usually `typing.overload` or `t.overload`). These are then filtered out in the predicates that (approximate) resolving methods according to the MRO. As the test introduced in the previous commit shows, this removes the spurious resolutions we had before.
This commit is contained in:
@@ -304,6 +304,22 @@ predicate hasContextmanagerDecorator(Function func) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `func` has a `typing.overload` decorator.
|
||||
* Such functions are type stubs that declare an overload signature but are
|
||||
* not the actual implementation.
|
||||
*/
|
||||
overlay[local]
|
||||
predicate hasOverloadDecorator(Function func) {
|
||||
exists(ControlFlowNode overload |
|
||||
overload.(NameNode).getId() = "overload" and overload.(NameNode).isGlobal()
|
||||
or
|
||||
overload.(AttrNode).getObject("overload").(NameNode).isGlobal()
|
||||
|
|
||||
func.getADecorator() = overload.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Callables
|
||||
// =============================================================================
|
||||
@@ -849,7 +865,8 @@ private Class getNextClassInMro(Class cls) {
|
||||
*/
|
||||
Function findFunctionAccordingToMro(Class cls, string name) {
|
||||
result = cls.getAMethod() and
|
||||
result.getName() = name
|
||||
result.getName() = name and
|
||||
not hasOverloadDecorator(result)
|
||||
or
|
||||
not class_has_method(cls, name) and
|
||||
result = findFunctionAccordingToMro(getNextClassInMro(cls), name)
|
||||
@@ -891,6 +908,7 @@ Class getNextClassInMroKnownStartingClass(Class cls, Class startingClass) {
|
||||
Function findFunctionAccordingToMroKnownStartingClass(Class cls, Class startingClass, string name) {
|
||||
result = cls.getAMethod() and
|
||||
result.getName() = name and
|
||||
not hasOverloadDecorator(result) and
|
||||
cls = getADirectSuperclass*(startingClass)
|
||||
or
|
||||
not class_has_method(cls, name) and
|
||||
|
||||
@@ -11,8 +11,8 @@ class OverloadedInit:
|
||||
def __init__(self, x, y=None):
|
||||
pass
|
||||
|
||||
OverloadedInit(1) # $ init=OverloadedInit.__init__:11 SPURIOUS: init=OverloadedInit.__init__:6 init=OverloadedInit.__init__:9
|
||||
OverloadedInit("a", "b") # $ init=OverloadedInit.__init__:11 SPURIOUS: init=OverloadedInit.__init__:6 init=OverloadedInit.__init__:9
|
||||
OverloadedInit(1) # $ init=OverloadedInit.__init__:11
|
||||
OverloadedInit("a", "b") # $ init=OverloadedInit.__init__:11
|
||||
|
||||
|
||||
from typing import overload
|
||||
@@ -28,8 +28,8 @@ class OverloadedInitFromImport:
|
||||
def __init__(self, x, y=None):
|
||||
pass
|
||||
|
||||
OverloadedInitFromImport(1) # $ init=OverloadedInitFromImport.__init__:28 SPURIOUS: init=OverloadedInitFromImport.__init__:23 init=OverloadedInitFromImport.__init__:26
|
||||
OverloadedInitFromImport("a", "b") # $ init=OverloadedInitFromImport.__init__:28 SPURIOUS: init=OverloadedInitFromImport.__init__:23 init=OverloadedInitFromImport.__init__:26
|
||||
OverloadedInitFromImport(1) # $ init=OverloadedInitFromImport.__init__:28
|
||||
OverloadedInitFromImport("a", "b") # $ init=OverloadedInitFromImport.__init__:28
|
||||
|
||||
|
||||
class NoOverloads:
|
||||
|
||||
Reference in New Issue
Block a user