Skip to content

Python: Modernize 4 queries for missing/multiple calls to init/del methods #19932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
271f32e
Move missing/multiple calls to init/del queries to folder
joefarebrother Jun 30, 2025
a2fc14a
Update missing call to init
joefarebrother Jun 30, 2025
6f9983a
Add missing call to del
joefarebrother Jun 30, 2025
adcfdf1
Modernize multple calls to init/del
joefarebrother Jul 1, 2025
caddec4
Update alert messages
joefarebrother Jul 1, 2025
71d1179
Fix FPs and typo
joefarebrother Jul 1, 2025
085df26
Move tests and add inline expectation postprocessing
joefarebrother Jul 2, 2025
2faf67d
Update test outputs + fix semantics
joefarebrother Jul 2, 2025
1b4e2fe
Change implenetation of missing calls to use getASuperCallTarget, and…
joefarebrother Jul 3, 2025
16b90a1
Fixes
joefarebrother Jul 3, 2025
b3056fc
Update tests for calls to init + fixes
joefarebrother Jul 3, 2025
73057d3
Add additional test case + update missing del tests
joefarebrother Jul 3, 2025
2e6f35b
Remove case excluding classes with a __new__ method; as it doesn't ma…
joefarebrother Jul 3, 2025
c5b79fa
Update multiple calls queries to include call targets in alert message
joefarebrother Jul 4, 2025
804b9ef
Update tests and add an additional test
joefarebrother Jul 4, 2025
6ca4f32
qhelp: move examples to subfolder
joefarebrother Jul 4, 2025
2e5f470
Update qhelp + alert messages
joefarebrother Jul 4, 2025
d2c68de
Update integration test output
joefarebrother Jul 4, 2025
f1026e4
Add change note
joefarebrother Jul 4, 2025
c47e6e3
Add qldoc
joefarebrother Jul 4, 2025
4b49ac3
Fix changenote formatting
joefarebrother Jul 4, 2025
d163bdf
Fix typos
joefarebrother Jul 4, 2025
e8a65b8
Update integration test outout and fix qhelp
joefarebrother Jul 7, 2025
f5066c7
Remove tostring
joefarebrother Jul 7, 2025
2c93e2c
Inline locationBefore
joefarebrother Jul 14, 2025
7dad89f
Adress review suggestions - cleanups
joefarebrother Jul 14, 2025
d2a8e5d
Fix typo in example
joefarebrother Jul 18, 2025
b33a1c2
Fix doc typo
joefarebrother Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update multiple calls queries to include call targets in alert message
  • Loading branch information
joefarebrother committed Jul 4, 2025
commit c5b79fa622b7e6e690cdb11d0457f861104cd483
38 changes: 31 additions & 7 deletions python/ql/src/Classes/CallsToInitDel/MethodCallOrder.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import semmle.python.ApiGraphs
import semmle.python.dataflow.new.internal.DataFlowDispatch
import codeql.util.Option

predicate multipleCallsToSuperclassMethod(Function meth, Function calledMulti, string name) {
exists(DataFlow::MethodCallNode call1, DataFlow::MethodCallNode call2, Class cls |
predicate multipleCallsToSuperclassMethod(
Function meth, Function calledMulti, DataFlow::MethodCallNode call1,
DataFlow::MethodCallNode call2, string name
) {
exists(Class cls |
meth.getName() = name and
meth.getScope() = cls and
call1.asExpr() != call2.asExpr() and
call1.getLocation().toString() < call2.getLocation().toString() and
calledMulti = getASuperCallTargetFromCall(cls, meth, call1, name) and
calledMulti = getASuperCallTargetFromCall(cls, meth, call2, name) and
nonTrivial(calledMulti)
Expand All @@ -18,23 +21,44 @@ predicate multipleCallsToSuperclassMethod(Function meth, Function calledMulti, s

Function getASuperCallTargetFromCall(
Class mroBase, Function meth, DataFlow::MethodCallNode call, string name
) {
exists(Function target | target = getDirectSuperCallTargetFromCall(mroBase, meth, call, name) |
result = target
or
result = getASuperCallTargetFromCall(mroBase, target, _, name)
)
}

Function getDirectSuperCallTargetFromCall(
Class mroBase, Function meth, DataFlow::MethodCallNode call, string name
) {
meth = call.getScope() and
getADirectSuperclass*(mroBase) = meth.getScope() and
meth.getName() = name and
call.calls(_, name) and
exists(Class targetCls | result = getASuperCallTargetFromClass(mroBase, targetCls, name) |
mroBase = getADirectSubclass*(meth.getScope()) and
exists(Class targetCls |
// the differences between 0-arg and 2-arg super is not considered; we assume each super uses the mro of the instance `self`
superCall(call, _) and
targetCls = getNextClassInMroKnownStartingClass(meth.getScope(), mroBase)
targetCls = getNextClassInMroKnownStartingClass(meth.getScope(), mroBase) and
result = findFunctionAccordingToMroKnownStartingClass(targetCls, mroBase, name)
or
callsMethodOnClassWithSelf(meth, call, targetCls, _)
// targetCls is the mro base for this lookup.
// note however that if the call we find uses super(), that still uses the mro of the instance `self` will sill be used
// assuming it's 0-arg or is 2-arg with `self` as second arg.
callsMethodOnClassWithSelf(meth, call, targetCls, _) and
result = findFunctionAccordingToMroKnownStartingClass(targetCls, targetCls, name)
)
}

Function getASuperCallTargetFromClass(Class mroBase, Class cls, string name) {
exists(Function target |
target = findFunctionAccordingToMroKnownStartingClass(cls, mroBase, name) and
(result = target or result = getASuperCallTargetFromCall(mroBase, target, _, name))
(
result = target
or
result = getASuperCallTargetFromCall(mroBase, target, _, name)
)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,34 @@
import python
import MethodCallOrder

predicate multipleCallsToSuperclassDel(Function meth, Function calledMulti) {
multipleCallsToSuperclassMethod(meth, calledMulti, "__del__")
predicate multipleCallsToSuperclassDel(
Function meth, Function calledMulti, DataFlow::MethodCallNode call1,
DataFlow::MethodCallNode call2
) {
multipleCallsToSuperclassMethod(meth, calledMulti, call1, call2, "__del__")
}

from Function meth, Function calledMulti
from
Function meth, Function calledMulti, DataFlow::MethodCallNode call1,
DataFlow::MethodCallNode call2, Function target1, Function target2, string msg
where
multipleCallsToSuperclassDel(meth, calledMulti) and
// Don't alert for multiple calls to a superclass del when a subclass will do.
multipleCallsToSuperclassDel(meth, calledMulti, call1, call2) and
// Only alert for the lowest method in the hierarchy that both calls will call.
not exists(Function subMulti |
multipleCallsToSuperclassDel(meth, subMulti) and
multipleCallsToSuperclassDel(meth, subMulti, _, _) and
calledMulti.getScope() = getADirectSuperclass+(subMulti.getScope())
) and
target1 = getDirectSuperCallTargetFromCall(meth.getScope(), meth, call1, _) and
target2 = getDirectSuperCallTargetFromCall(meth.getScope(), meth, call2, _) and
(
target1 != target2 and
msg =
"This deletion method calls $@ multiple times, via $@ and $@, resolving to $@ and $@ respectively."
or
target1 = target2 and
// The targets themselves are called multiple times (either is calledMulti, or something earlier in the MRO)
// Mentioning them again would be redundant.
msg = "This deletion method calls $@ multiple times, via $@ and $@."
)
select meth, "This delete method calls $@ multiple times.", calledMulti,
calledMulti.getQualifiedName()
select meth, msg, calledMulti, calledMulti.getQualifiedName(), call1, "this call", call2,
"this call", target1, target1.getQualifiedName(), target2, target2.getQualifiedName()
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,34 @@
import python
import MethodCallOrder

predicate multipleCallsToSuperclassInit(Function meth, Function calledMulti) {
multipleCallsToSuperclassMethod(meth, calledMulti, "__init__")
predicate multipleCallsToSuperclassInit(
Function meth, Function calledMulti, DataFlow::MethodCallNode call1,
DataFlow::MethodCallNode call2
) {
multipleCallsToSuperclassMethod(meth, calledMulti, call1, call2, "__init__")
}

from Function meth, Function calledMulti
from
Function meth, Function calledMulti, DataFlow::MethodCallNode call1,
DataFlow::MethodCallNode call2, Function target1, Function target2, string msg
where
multipleCallsToSuperclassInit(meth, calledMulti) and
// Don't alert for multiple calls to a superclass init when a subclass will do.
multipleCallsToSuperclassInit(meth, calledMulti, call1, call2) and
// Only alert for the lowest method in the hierarchy that both calls will call.
not exists(Function subMulti |
multipleCallsToSuperclassInit(meth, subMulti) and
multipleCallsToSuperclassInit(meth, subMulti, _, _) and
calledMulti.getScope() = getADirectSuperclass+(subMulti.getScope())
) and
target1 = getDirectSuperCallTargetFromCall(meth.getScope(), meth, call1, _) and
target2 = getDirectSuperCallTargetFromCall(meth.getScope(), meth, call2, _) and
(
target1 != target2 and
msg =
"This initialization method calls $@ multiple times, via $@ and $@, resolving to $@ and $@ respectively."
or
target1 = target2 and
// The targets themselves are called multiple times (either is calledMulti, or something earlier in the MRO)
// Mentioning them again would be redundant.
msg = "This initialization method calls $@ multiple times, via $@ and $@."
)
select meth, "This initialization method calls $@ multiple times.", calledMulti,
calledMulti.getQualifiedName()
select meth, msg, calledMulti, calledMulti.getQualifiedName(), call1, "this call", call2,
"this call", target1, target1.getQualifiedName(), target2, target2.getQualifiedName()
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy