Skip to content

Commit 0c4f794

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 0c4f794

14 files changed

Lines changed: 70 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/Exprs.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class Attribute extends Attribute_ {
7070
/* syntax: Expr.name */
7171
override Expr getASubExpression() { result = this.getObject() }
7272

73+
deprecated override AttrNode getAFlowNode() { result = super.getAFlowNode() }
74+
7375
/** Gets the name of this attribute. That is the `name` in `obj.name` */
7476
string getName() { result = Attribute_.super.getAttr() }
7577

@@ -96,6 +98,8 @@ class Subscript extends Subscript_ {
9698
}
9799

98100
Expr getObject() { result = Subscript_.super.getValue() }
101+
102+
deprecated override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
99103
}
100104

101105
/** A call expression, such as `func(...)` */
@@ -111,6 +115,8 @@ class Call extends Call_ {
111115

112116
override string toString() { result = this.getFunc().toString() + "()" }
113117

118+
deprecated override CallNode getAFlowNode() { result = super.getAFlowNode() }
119+
114120
/** Gets a tuple (*) argument of this call. */
115121
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
116122

@@ -196,6 +202,8 @@ class IfExp extends IfExp_ {
196202
override Expr getASubExpression() {
197203
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
198204
}
205+
206+
deprecated override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
199207
}
200208

201209
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
@@ -404,6 +412,8 @@ class PlaceHolder extends PlaceHolder_ {
404412
override Expr getASubExpression() { none() }
405413

406414
override string toString() { result = "$" + this.getId() }
415+
416+
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
407417
}
408418

409419
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
@@ -470,6 +480,8 @@ class Name extends Name_ {
470480

471481
override string toString() { result = this.getId() }
472482

483+
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
484+
473485
override predicate isArtificial() {
474486
/* Artificial variable names in comprehensions all start with "." */
475487
this.getId().charAt(0) = "."
@@ -575,6 +587,8 @@ abstract class NameConstant extends Name, ImmutableLiteral {
575587

576588
override predicate isConstant() { any() }
577589

590+
deprecated override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
591+
578592
override predicate isArtificial() { none() }
579593
}
580594

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/Import.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ class ImportExpr extends ImportExpr_ {
149149
class ImportMember extends ImportMember_ {
150150
override Expr getASubExpression() { result = this.getModule() }
151151

152+
deprecated override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
153+
152154
override predicate hasSideEffects() {
153155
/* Strictly this only has side-effects if the module is a package */
154156
any()

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

0 commit comments

Comments
 (0)