@@ -5,6 +5,7 @@ import semmle.python.ApiGraphs
55import semmle.python.dataflow.new.internal.DataFlowDispatch
66import codeql.util.Option
77
8+ /** Holds if `meth` is a method named `name` that transitively calls `calledMulti` of the same name via the calls `call1` and `call2`. */
89predicate multipleCallsToSuperclassMethod (
910 Function meth , Function calledMulti , DataFlow:: MethodCallNode call1 ,
1011 DataFlow:: MethodCallNode call2 , string name
@@ -19,6 +20,7 @@ predicate multipleCallsToSuperclassMethod(
1920 )
2021}
2122
23+ /** Gets a method transitively called by `meth` named `name` with `call` that it overrides, with `mroBase` as the type determining the MRO to search. */
2224Function getASuperCallTargetFromCall (
2325 Class mroBase , Function meth , DataFlow:: MethodCallNode call , string name
2426) {
@@ -29,6 +31,7 @@ Function getASuperCallTargetFromCall(
2931 )
3032}
3133
34+ /** Gets the method called by `meth` named `name` with `call`, with `mroBase` as the type determining the MRO to search. */
3235Function getDirectSuperCallTargetFromCall (
3336 Class mroBase , Function meth , DataFlow:: MethodCallNode call , string name
3437) {
@@ -51,6 +54,7 @@ Function getDirectSuperCallTargetFromCall(
5154 )
5255}
5356
57+ /** Gets a method that is transitively called by a call to `cls.<name>`, with `mroBase` as the type determining the MRO to search. */
5458Function getASuperCallTargetFromClass ( Class mroBase , Class cls , string name ) {
5559 exists ( Function target |
5660 target = findFunctionAccordingToMroKnownStartingClass ( cls , mroBase , name ) and
@@ -62,6 +66,7 @@ Function getASuperCallTargetFromClass(Class mroBase, Class cls, string name) {
6266 )
6367}
6468
69+ /** Holds if `meth` does something besides calling a superclass method. */
6570predicate nonTrivial ( Function meth ) {
6671 exists ( Stmt s | s = meth .getAStmt ( ) |
6772 not s instanceof Pass and
@@ -74,20 +79,23 @@ predicate nonTrivial(Function meth) {
7479 exists ( meth .getANormalExit ( ) ) // doesn't always raise an exception
7580}
7681
82+ /** Holds if `call` is a call to `super().<name>`. No distinction is made btween 0- and 2- arg super calls. */
7783predicate superCall ( DataFlow:: MethodCallNode call , string name ) {
7884 exists ( DataFlow:: Node sup |
7985 call .calls ( sup , name ) and
8086 sup = API:: builtin ( "super" ) .getACall ( )
8187 )
8288}
8389
90+ /** Holds if `meth` calls `super().<name>` where `name` is the name of the method. */
8491predicate callsSuper ( Function meth ) {
8592 exists ( DataFlow:: MethodCallNode call |
8693 call .getScope ( ) = meth and
8794 superCall ( call , meth .getName ( ) )
8895 )
8996}
9097
98+ /** Holds if `meth` calls `target.<name>(self, ...)` with the call `call`. */
9199predicate callsMethodOnClassWithSelf (
92100 Function meth , DataFlow:: MethodCallNode call , Class target , string name
93101) {
@@ -99,6 +107,7 @@ predicate callsMethodOnClassWithSelf(
99107 )
100108}
101109
110+ /** Holds if `meth` calls a method named `name` passing its `self` argument as its first parameter, but the class it refers to is unknown. */
102111predicate callsMethodOnUnknownClassWithSelf ( Function meth , string name ) {
103112 exists ( DataFlow:: MethodCallNode call , DataFlow:: Node callTarget , DataFlow:: ParameterNode self |
104113 call .calls ( callTarget , name ) and
@@ -108,6 +117,7 @@ predicate callsMethodOnUnknownClassWithSelf(Function meth, string name) {
108117 )
109118}
110119
120+ /** Holds if `base` does not call a superclass method `shouldCall` named `name` when it appears it should. */
111121predicate missingCallToSuperclassMethod ( Class base , Function shouldCall , string name ) {
112122 shouldCall .getName ( ) = name and
113123 shouldCall .getScope ( ) = getADirectSuperclass + ( base ) and
@@ -117,6 +127,9 @@ predicate missingCallToSuperclassMethod(Class base, Function shouldCall, string
117127 not callsMethodOnUnknownClassWithSelf ( getASuperCallTargetFromClass ( base , base , name ) , name )
118128}
119129
130+ /** Holds if `base` does not call a superclass method `shouldCall` named `name` when it appears it should.
131+ * Results are restricted to hold only for the highest `base` class and the lowest `shouldCall` method in the heirarchy for which this applies.
132+ */
120133predicate missingCallToSuperclassMethodRestricted ( Class base , Function shouldCall , string name ) {
121134 missingCallToSuperclassMethod ( base , shouldCall , name ) and
122135 not exists ( Class superBase |
@@ -131,6 +144,11 @@ predicate missingCallToSuperclassMethodRestricted(Class base, Function shouldCal
131144 )
132145}
133146
147+ /**
148+ * If `base` contains a `super()` call, gets a method in the inheritence heirarchy of `name` in the MRO of `base`
149+ * that does not contain a `super()` call, but would call `shouldCall` if it did, which does not otherwise get called
150+ * during a call to `base.<name>`.
151+ * */
134152Function getPossibleMissingSuper ( Class base , Function shouldCall , string name ) {
135153 missingCallToSuperclassMethod ( base , shouldCall , name ) and
136154 exists ( Function baseMethod |
@@ -151,6 +169,7 @@ Function getPossibleMissingSuper(Class base, Function shouldCall, string name) {
151169
152170private module FunctionOption = Option< Function > ;
153171
172+ /** An optional `Function`. */
154173class FunctionOption extends FunctionOption:: Option {
155174 /**
156175 * Holds if this element is at the specified location.
@@ -174,6 +193,7 @@ class FunctionOption extends FunctionOption::Option {
174193 endcolumn = 0
175194 }
176195
196+ /** Gets the qualified name of this function. */
177197 string getQualifiedName ( ) {
178198 result = this .asSome ( ) .getQualifiedName ( )
179199 or
@@ -182,6 +202,7 @@ class FunctionOption extends FunctionOption::Option {
182202 }
183203}
184204
205+ /** Gets the result of `getPossibleMissingSuper`, or None if none exists. */
185206bindingset [ name]
186207FunctionOption getPossibleMissingSuperOption ( Class base , Function shouldCall , string name ) {
187208 result .asSome ( ) = getPossibleMissingSuper ( base , shouldCall , name )
0 commit comments