Skip to content

Commit 0a99461

Browse files
yoffCopilot
andcommitted
Python: migrate src queries to new shared CFG types + reformat
Migrate 27 queries under python/ql/src/ from legacy CFG types (CallNode/AttrNode/NameNode/etc.) to the shared-CFG-based 'Cfg::' namespace, matching the dataflow API surface introduced earlier on this branch. ModificationOfParameterWithDefaultCustomizations.qll is rewritten on top of BarrierGuard, removing the last legacy ESSA dependency in that file. UnguardedNextInGenerator.ql still uses ESSA and bridges to the new CFG via Cfg::CallNode.getNode(). Also reformat 14 library and query files that had drifted from the formatter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent cd703dc commit 0a99461

40 files changed

Lines changed: 172 additions & 138 deletions

python/ql/lib/semmle/python/Exprs.qll

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ class Expr extends Expr_, AstNode {
2828
/** Whether this expression may have a side effect (as determined purely from its syntax) */
2929
predicate hasSideEffects() {
3030
/* If an exception raised by this expression handled, count that as a side effect */
31-
exists(ControlFlowNode n | n.getNode() = this | n.getASuccessor().getNode() instanceof ExceptStmt)
31+
exists(ControlFlowNode n | n.getNode() = this |
32+
n.getASuccessor().getNode() instanceof ExceptStmt
33+
)
3234
or
3335
this.getASubExpression().hasSideEffects()
3436
}
@@ -94,7 +96,6 @@ class Subscript extends Subscript_ {
9496
}
9597

9698
Expr getObject() { result = Subscript_.super.getValue() }
97-
9899
}
99100

100101
/** A call expression, such as `func(...)` */
@@ -110,7 +111,6 @@ class Call extends Call_ {
110111

111112
override string toString() { result = this.getFunc().toString() + "()" }
112113

113-
114114
/** Gets a tuple (*) argument of this call. */
115115
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
116116

@@ -196,7 +196,6 @@ class IfExp extends IfExp_ {
196196
override Expr getASubExpression() {
197197
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
198198
}
199-
200199
}
201200

202201
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
@@ -405,7 +404,6 @@ class PlaceHolder extends PlaceHolder_ {
405404
override Expr getASubExpression() { none() }
406405

407406
override string toString() { result = "$" + this.getId() }
408-
409407
}
410408

411409
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
@@ -472,7 +470,6 @@ class Name extends Name_ {
472470

473471
override string toString() { result = this.getId() }
474472

475-
476473
override predicate isArtificial() {
477474
/* Artificial variable names in comprehensions all start with "." */
478475
this.getId().charAt(0) = "."
@@ -578,7 +575,6 @@ abstract class NameConstant extends Name, ImmutableLiteral {
578575

579576
override predicate isConstant() { any() }
580577

581-
582578
override predicate isArtificial() { none() }
583579
}
584580

python/ql/lib/semmle/python/Flow.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,9 @@ private Py::AstNode assigned_value(Py::Expr lhs) {
726726
exists(Py::Alias a | a.getAsname() = lhs and result = a.getValue())
727727
or
728728
/* lhs += x => result = (lhs + x) */
729-
exists(Py::AugAssign a, Py::BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
729+
exists(Py::AugAssign a, Py::BinaryExpr b |
730+
b = a.getOperation() and result = b and lhs = b.getLeft()
731+
)
730732
or
731733
/*
732734
* ..., lhs, ... = ..., result, ...
@@ -868,7 +870,9 @@ class NameNode extends ControlFlowNode {
868870
/** Whether this is a use of a global (including builtin) variable. */
869871
predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) }
870872

871-
predicate isSelf() { exists(Py::SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) }
873+
predicate isSelf() {
874+
exists(Py::SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this)
875+
}
872876
}
873877

874878
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */

python/ql/lib/semmle/python/Import.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ class ImportMember extends ImportMember_ {
162162
string getImportedModuleName() {
163163
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
164164
}
165-
166165
}
167166

168167
/** An import statement */

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -287,19 +287,19 @@ predicate isClassmethod(Function func) {
287287

288288
/** Holds if the function `func` has a `property` decorator. */
289289
overlay[local]
290-
predicate hasPropertyDecorator(Function func) {
291-
func.getADecorator().(Name).getId() = "property"
292-
}
290+
predicate hasPropertyDecorator(Function func) { func.getADecorator().(Name).getId() = "property" }
293291

294292
/**
295293
* Holds if the function `func` has a `contextlib.contextmanager`.
296294
*/
297295
overlay[local]
298296
predicate hasContextmanagerDecorator(Function func) {
299297
exists(Cfg::ControlFlowNode contextmanager |
300-
contextmanager.(Cfg::NameNode).getId() = "contextmanager" and contextmanager.(Cfg::NameNode).isGlobal()
298+
contextmanager.(Cfg::NameNode).getId() = "contextmanager" and
299+
contextmanager.(Cfg::NameNode).isGlobal()
301300
or
302-
contextmanager.(Cfg::AttrNode).getObject("contextmanager").(Cfg::NameNode).getId() = "contextlib"
301+
contextmanager.(Cfg::AttrNode).getObject("contextmanager").(Cfg::NameNode).getId() =
302+
"contextlib"
303303
|
304304
func.getADecorator() = contextmanager.getNode()
305305
)
@@ -1348,7 +1348,9 @@ predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
13481348
* translated into `l.clear()`, and we can still have use-use flow.
13491349
*/
13501350
cached
1351-
predicate getCallArg(Cfg::CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos) {
1351+
predicate getCallArg(
1352+
Cfg::CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos
1353+
) {
13521354
Stages::DataFlow::ref() and
13531355
resolveCall(call, target, type) and
13541356
(
@@ -1441,7 +1443,9 @@ private predicate sameEnclosingCallable(Node node1, Node node2) {
14411443
// DataFlowCall
14421444
// =============================================================================
14431445
newtype TDataFlowCall =
1444-
TNormalCall(Cfg::CallNode call, Function target, CallType type) { resolveCall(call, target, type) } or
1446+
TNormalCall(Cfg::CallNode call, Function target, CallType type) {
1447+
resolveCall(call, target, type)
1448+
} or
14451449
/** A call to the generated function inside a comprehension */
14461450
TComprehensionCall(Comp c) or
14471451
TPotentialLibraryCall(Cfg::CallNode call) or

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ newtype TNode =
3737
* A node corresponding to a scope entry definition. That is, the value of a variable
3838
* as it enters a scope.
3939
*/
40-
TScopeEntryDefinitionNode(SsaImpl::ScopeEntryDefinition def) { not def.getScope() instanceof Module } or
40+
TScopeEntryDefinitionNode(SsaImpl::ScopeEntryDefinition def) {
41+
not def.getScope() instanceof Module
42+
} or
4143
/**
4244
* A synthetic node representing the value of an object before a state change.
4345
*
@@ -656,11 +658,15 @@ private predicate outcomeOfGuard(
656658
)
657659
or
658660
// Recursive: comparisons against a boolean literal.
659-
exists(Cfg::CompareNode cmpNode, Cmpop op, Cfg::ControlFlowNode otherOperand,
660-
Cfg::ControlFlowNode guardOperand, boolean polarity, boolean cmpBranch
661+
exists(
662+
Cfg::CompareNode cmpNode, Cmpop op, Cfg::ControlFlowNode otherOperand,
663+
Cfg::ControlFlowNode guardOperand, boolean polarity, boolean cmpBranch
661664
|
662665
guardOperand.getNode() = guard.getNode() and
663-
(cmpNode.operands(guardOperand, op, otherOperand) or cmpNode.operands(otherOperand, op, guardOperand)) and
666+
(
667+
cmpNode.operands(guardOperand, op, otherOperand) or
668+
cmpNode.operands(otherOperand, op, guardOperand)
669+
) and
664670
not guard.getNode() instanceof BooleanLiteral and
665671
(
666672
(op instanceof Eq or op instanceof Is) and
@@ -692,7 +698,9 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
692698
result = ParameterizedBarrierGuard<Unit, extendedGuardChecks/4>::getABarrierNode(_)
693699
}
694700

695-
private predicate extendedGuardChecks(GuardNode g, Cfg::ControlFlowNode node, boolean branch, Unit u) {
701+
private predicate extendedGuardChecks(
702+
GuardNode g, Cfg::ControlFlowNode node, boolean branch, Unit u
703+
) {
696704
guardChecks(g, node, branch) and
697705
u = u
698706
}
@@ -790,7 +798,11 @@ newtype TContent =
790798
or
791799
// d["key"] = ...
792800
key =
793-
any(Cfg::SubscriptNode sub | sub.isStore() | sub.getIndex().getNode().(StringLiteral).getText())
801+
any(Cfg::SubscriptNode sub |
802+
sub.isStore()
803+
|
804+
sub.getIndex().getNode().(StringLiteral).getText()
805+
)
794806
or
795807
// d.setdefault("key", ...)
796808
exists(Cfg::CallNode call | call.getFunction().(Cfg::AttrNode).getName() = "setdefault" |

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ class UnpackingAssignmentTarget extends Cfg::ControlFlowNode {
233233
}
234234

235235
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
236-
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof Cfg::SequenceNode {
236+
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof Cfg::SequenceNode
237+
{
237238
Cfg::ControlFlowNode getElement(int i) { result = super.getElement(i) }
238239

239240
Cfg::ControlFlowNode getAnElement() { result = this.getElement(_) }

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
9797
return = FlowSummaryImpl::Private::SummaryComponent::return() and
9898
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
9999
exists(Return ret |
100-
ret.getScope() =
101-
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
100+
ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
102101
result.asCfgNode().getNode() = ret.getValue()
103102
)
104103
}
@@ -311,7 +310,9 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
311310
//
312311
// nodeFrom is `expr`
313312
// nodeTo is entry node for `f`
314-
exists(SsaImpl::ScopeEntryDefinition e, SsaImpl::SsaSourceVariable var, Cfg::DefinitionNode def |
313+
exists(
314+
SsaImpl::ScopeEntryDefinition e, SsaImpl::SsaSourceVariable var, Cfg::DefinitionNode def
315+
|
315316
e.getSourceVariable() = var and
316317
def.getNode() = var.getVariable().getAStore()
317318
|

python/ql/lib/semmle/python/dataflow/old/Implementation.qll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
448448
context = TNoParam() and
449449
src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and
450450
node.asCfgNode() = call and
451-
retval.asCfgNode().getNode() =
452-
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
451+
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
453452
) and
454453
edgeLabel = "return"
455454
}
@@ -471,8 +470,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
471470
this.callContexts(call, src, pyfunc, context, callee) and
472471
retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and
473472
node.asCfgNode() = call and
474-
retval.asCfgNode().getNode() =
475-
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
473+
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
476474
) and
477475
edgeLabel = "call"
478476
}

python/ql/src/Exceptions/UnguardedNextInGenerator.ql

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,26 @@
1212

1313
import python
1414
private import semmle.python.ApiGraphs
15+
private import semmle.python.controlflow.internal.Cfg as Cfg
16+
private import semmle.python.Flow as Flow
1517

1618
API::Node iter() { result = API::builtin("iter") }
1719

1820
API::Node next() { result = API::builtin("next") }
1921

2022
API::Node stopIteration() { result = API::builtin("StopIteration") }
2123

22-
predicate call_to_iter(CallNode call, EssaVariable sequence) {
23-
call = iter().getACall().asCfgNode() and
24+
predicate call_to_iter(Flow::CallNode call, EssaVariable sequence) {
25+
call.getNode() = iter().getACall().asCfgNode().(Cfg::CallNode).getNode() and
2426
call.getArg(0) = sequence.getAUse()
2527
}
2628

27-
predicate call_to_next(CallNode call, ControlFlowNode iter) {
28-
call = next().getACall().asCfgNode() and
29+
predicate call_to_next(Flow::CallNode call, Flow::ControlFlowNode iter) {
30+
call.getNode() = next().getACall().asCfgNode().(Cfg::CallNode).getNode() and
2931
call.getArg(0) = iter
3032
}
3133

32-
predicate call_to_next_has_default(CallNode call) {
34+
predicate call_to_next_has_default(Flow::CallNode call) {
3335
exists(call.getArg(1)) or exists(call.getArgByName("default"))
3436
}
3537

@@ -49,14 +51,14 @@ predicate iter_not_exhausted(EssaVariable iterator) {
4951
)
5052
}
5153

52-
predicate stop_iteration_handled(CallNode call) {
54+
predicate stop_iteration_handled(Flow::CallNode call) {
5355
exists(Try t |
5456
t.containsInScope(call.getNode()) and
5557
t.getAHandler().getType() = stopIteration().getAValueReachableFromSource().asExpr()
5658
)
5759
}
5860

59-
from CallNode call
61+
from Flow::CallNode call
6062
where
6163
call_to_next(call, _) and
6264
not call_to_next_has_default(call) and

python/ql/src/Expressions/CallArgs.qll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,7 @@ FunctionValue get_function_or_initializer(Value func_or_cls) {
116116
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
117117
not func.isC() and
118118
name = call.getANamedArgumentName() and
119-
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
120-
callCfg = get_a_call_objectapi(func)
121-
) and
119+
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call_objectapi(func)) and
122120
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
123121
}
124122

0 commit comments

Comments
 (0)