Skip to content

Commit 0b473e3

Browse files
yoffCopilot
andcommitted
Python: deprecate Function.getAReturnValueFlowNode() and rewrite internal callers
Follow-up to the getAFlowNode deprecation in the same PR: same AST→legacy-CFG bridge pattern. The 11 internal call sites (across objects/, types/, frameworks/, and TypeTrackingImpl) are rewritten to bind a `Return ret` explicitly, then constrain via `ret.getScope() = f and n.getNode() = ret.getValue()`. The predicate itself is preserved with a deprecation note so external users do not experience churn. Semantic noop. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a13dfaa commit 0b473e3

12 files changed

Lines changed: 54 additions & 15 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: deprecated
3+
---
4+
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.

python/ql/lib/semmle/python/Function.qll

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,16 @@ class Function extends Function_, Scope, AstNode {
153153

154154
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
155155

156-
/** Gets a control flow node for a return value of this function */
157-
ControlFlowNode getAReturnValueFlowNode() {
156+
/**
157+
* DEPRECATED: bind a `Return` node explicitly instead, e.g.
158+
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
159+
* This API is being phased out together with `AstNode.getAFlowNode()` to
160+
* untangle the AST and CFG hierarchies in preparation for migrating the
161+
* dataflow library off the legacy CFG.
162+
*
163+
* Gets a control flow node for a return value of this function.
164+
*/
165+
deprecated ControlFlowNode getAReturnValueFlowNode() {
158166
exists(Return ret |
159167
ret.getScope() = this and
160168
ret.getValue() = result.getNode()

python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
9494
Node returnOf(Node callable, SummaryComponent return) {
9595
return = FlowSummaryImpl::Private::SummaryComponent::return() and
9696
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
97-
result.asCfgNode() =
98-
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
97+
exists(Return ret |
98+
ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
99+
result.asCfgNode().getNode() = ret.getValue()
100+
)
99101
}
100102

101103
// Relating callables to nodes

python/ql/lib/semmle/python/frameworks/Bottle.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ module Bottle {
7373
/** A response returned by a view callable. */
7474
class BottleReturnResponse extends Http::Server::HttpResponse::Range {
7575
BottleReturnResponse() {
76-
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode()
76+
exists(Return ret |
77+
ret.getScope() = any(View::ViewCallable vc) and
78+
this.asCfgNode().getNode() = ret.getValue()
79+
)
7780
}
7881

7982
override DataFlow::Node getBody() { result = this }

python/ql/lib/semmle/python/frameworks/Django.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2872,7 +2872,10 @@ module PrivateDjango {
28722872
DataFlow::CfgNode
28732873
{
28742874
DjangoRedirectViewGetRedirectUrlReturn() {
2875-
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
2875+
exists(Return ret |
2876+
ret.getScope() = any(GetRedirectUrlFunction f) and
2877+
node.getNode() = ret.getValue()
2878+
)
28762879
}
28772880

28782881
override DataFlow::Node getRedirectLocation() { result = this }

python/ql/lib/semmle/python/frameworks/FastApi.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,10 @@ module FastApi {
309309
FastApiRouteSetup routeSetup;
310310

311311
FastApiRequestHandlerReturn() {
312-
node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
312+
exists(Return ret |
313+
ret.getScope() = routeSetup.getARequestHandler() and
314+
node.getNode() = ret.getValue()
315+
)
313316
}
314317

315318
override DataFlow::Node getBody() { result = this }

python/ql/lib/semmle/python/frameworks/Pyramid.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ module Pyramid {
166166
/** A response returned by a view callable. */
167167
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
168168
PyramidReturnResponse() {
169-
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and
169+
exists(Return ret |
170+
ret.getScope() = any(View::ViewCallable vc) and
171+
this.asCfgNode().getNode() = ret.getValue()
172+
) and
170173
not this = instance()
171174
}
172175

python/ql/lib/semmle/python/frameworks/Stdlib.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2254,8 +2254,9 @@ module StdlibPrivate {
22542254
DataFlow::CfgNode
22552255
{
22562256
WsgirefSimpleServerApplicationReturn() {
2257-
exists(WsgirefSimpleServerApplication requestHandler |
2258-
node = requestHandler.getAReturnValueFlowNode()
2257+
exists(WsgirefSimpleServerApplication requestHandler, Return ret |
2258+
ret.getScope() = requestHandler and
2259+
node.getNode() = ret.getValue()
22592260
)
22602261
}
22612262

python/ql/lib/semmle/python/frameworks/Twisted.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,10 @@ private module Twisted {
182182
DataFlow::CfgNode
183183
{
184184
TwistedResourceRenderMethodReturn() {
185-
this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode()
185+
exists(Return ret |
186+
ret.getScope() = any(TwistedResourceRenderMethod meth) and
187+
this.asCfgNode().getNode() = ret.getValue()
188+
)
186189
}
187190

188191
override DataFlow::Node getBody() { result = this }

python/ql/lib/semmle/python/objects/Callables.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,12 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
8181

8282
pragma[nomagic]
8383
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
84-
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
84+
exists(Function func, Return ret, ControlFlowNode rval, ControlFlowNode forigin |
8585
func = this.getScope() and
8686
callee.appliesToScope(func)
8787
|
88-
rval = func.getAReturnValueFlowNode() and
88+
ret.getScope() = func and
89+
rval.getNode() = ret.getValue() and
8990
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
9091
origin = CfgOrigin::fromCfgNode(forigin)
9192
)

0 commit comments

Comments
 (0)