Skip to content

Commit 169d7a3

Browse files
committed
Python: Add scope entry definition nodes
otherwise we confuse captured variables in the single scope entry cfg node. Now we have one for each defined variable.
1 parent 3b7e29b commit 169d7a3

File tree

9 files changed

+41
-16
lines changed

9 files changed

+41
-16
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,10 @@ module LocalFlow {
352352
// nodeFrom is `y` on first line
353353
// nodeTo is `y` on second line
354354
exists(EssaDefinition def |
355-
nodeFrom.(CfgNode).getNode() = def.(EssaNodeDefinition).getDefiningNode() and
355+
nodeFrom.(CfgNode).getNode() = def.(EssaNodeDefinition).getDefiningNode()
356+
or
357+
nodeFrom.(ScopeEntryDefinitionNode).getDefinition() = def
358+
|
356359
AdjacentUses::firstUse(def, nodeTo.(CfgNode).getNode())
357360
)
358361
or

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ newtype TNode =
2727
isExpressionNode(node)
2828
or
2929
node.getNode() instanceof Pattern
30-
or
31-
node = any(ScopeEntryDefinition def | not def.getScope() instanceof Module).getDefiningNode()
3230
} or
31+
/**
32+
* A node corresponding to a scope entry definition. That is, the value of a variable
33+
* as it enters a scope.
34+
*/
35+
TScopeEntryDefinitionNode(ScopeEntryDefinition def) { not def.getScope() instanceof Module } or
3336
/**
3437
* A synthetic node representing the value of an object before a state change.
3538
*
@@ -257,6 +260,28 @@ class ExprNode extends CfgNode {
257260
/** Gets a node corresponding to expression `e`. */
258261
ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
259262

263+
/**
264+
* A node corresponding to a scope entry definition. That is, the value of a variable
265+
* as it enters a scope.
266+
*/
267+
class ScopeEntryDefinitionNode extends Node, TScopeEntryDefinitionNode {
268+
ScopeEntryDefinition def;
269+
270+
ScopeEntryDefinitionNode() { this = TScopeEntryDefinitionNode(def) }
271+
272+
/** Gets the `ScopeEntryDefinition` associated with this node. */
273+
ScopeEntryDefinition getDefinition() { result = def }
274+
275+
/** Gets the source variable represented by this node. */
276+
SsaSourceVariable getVariable() { result = def.getSourceVariable() }
277+
278+
override Location getLocation() { result = def.getLocation() }
279+
280+
override Scope getScope() { result = def.getScope() }
281+
282+
override string toString() { result = "Entry definition for " + this.getVariable().toString() }
283+
}
284+
260285
/**
261286
* The value of a parameter at function entry, viewed as a node in a data
262287
* flow graph.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class LocalSourceNode extends Node {
7171
or
7272
// We include all scope entry definitions, as these act as the local source within the scope they
7373
// enter.
74-
this.asCfgNode() = any(ScopeEntryDefinition def).getDefiningNode()
74+
this instanceof ScopeEntryDefinitionNode
7575
or
7676
this instanceof ParameterNode
7777
}
@@ -167,7 +167,7 @@ class LocalSourceNodeNotModuleVariableNode extends LocalSourceNode {
167167
LocalSourceNodeNotModuleVariableNode() {
168168
this instanceof ExprNode
169169
or
170-
this.asCfgNode() = any(ScopeEntryDefinition def).getDefiningNode()
170+
this instanceof ScopeEntryDefinitionNode
171171
}
172172
}
173173

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput {
251251
e.getSourceVariable() = var and
252252
var.hasDefiningNode(def)
253253
|
254-
nodeTo.asCfgNode() = e.getDefiningNode() and
254+
nodeTo.(DataFlowPublic::ScopeEntryDefinitionNode).getDefinition() = e and
255255
nodeFrom.asCfgNode() = def.getValue() and
256256
var.getScope().getScope*() = nodeFrom.getScope()
257257
)

python/ql/test/experimental/dataflow/coverage/localFlow.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
| test.py:41:1:41:33 | Entry node for Function test_tuple_with_local_flow | test.py:42:10:42:18 | ControlFlowNode for NONSOURCE |
2-
| test.py:41:1:41:33 | Entry node for Function test_tuple_with_local_flow | test.py:42:21:42:26 | ControlFlowNode for SOURCE |
3-
| test.py:41:1:41:33 | Entry node for Function test_tuple_with_local_flow | test.py:44:5:44:8 | ControlFlowNode for SINK |
1+
| test.py:41:1:41:33 | Entry definition for SsaSourceVariable NONSOURCE | test.py:42:10:42:18 | ControlFlowNode for NONSOURCE |
2+
| test.py:41:1:41:33 | Entry definition for SsaSourceVariable SINK | test.py:44:5:44:8 | ControlFlowNode for SINK |
3+
| test.py:41:1:41:33 | Entry definition for SsaSourceVariable SOURCE | test.py:42:21:42:26 | ControlFlowNode for SOURCE |
44
| test.py:42:5:42:5 | ControlFlowNode for x | test.py:43:9:43:9 | ControlFlowNode for x |
55
| test.py:42:10:42:26 | ControlFlowNode for Tuple | test.py:42:5:42:5 | ControlFlowNode for x |
66
| test.py:43:5:43:5 | ControlFlowNode for y | test.py:44:10:44:10 | ControlFlowNode for y |
77
| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:43:5:43:5 | ControlFlowNode for y |
8-
| test.py:208:1:208:53 | Entry node for Function test_nested_comprehension_deep_with_local_flow | test.py:209:25:209:30 | ControlFlowNode for SOURCE |
9-
| test.py:208:1:208:53 | Entry node for Function test_nested_comprehension_deep_with_local_flow | test.py:210:5:210:8 | ControlFlowNode for SINK |
8+
| test.py:208:1:208:53 | Entry definition for SsaSourceVariable SINK | test.py:210:5:210:8 | ControlFlowNode for SINK |
9+
| test.py:208:1:208:53 | Entry definition for SsaSourceVariable SOURCE | test.py:209:25:209:30 | ControlFlowNode for SOURCE |
1010
| test.py:209:5:209:5 | ControlFlowNode for x | test.py:210:10:210:10 | ControlFlowNode for x |
1111
| test.py:209:9:209:68 | ControlFlowNode for .0 | test.py:209:9:209:68 | ControlFlowNode for .0 |
1212
| test.py:209:9:209:68 | ControlFlowNode for ListComp | test.py:209:5:209:5 | ControlFlowNode for x |

python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
| generator.py:1:1:1:23 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 |
1919
| generator.py:1:1:1:23 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 |
2020
| generator.py:1:1:1:23 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for ListComp |
21-
| generator.py:1:1:1:23 | Function generator_func | generator.py:2:12:2:26 | Entry node for Function listcomp |
2221
| generator.py:1:1:1:23 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for Yield |
2322
| generator.py:1:1:1:23 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for x |
2423
| generator.py:1:1:1:23 | Function generator_func | generator.py:2:19:2:19 | ControlFlowNode for x |

python/ql/test/experimental/dataflow/typetracking/moduleattr.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ module_attr_tracker
66
| import_as_attr.py:1:28:1:35 | ControlFlowNode for attr_ref |
77
| import_as_attr.py:3:1:3:1 | ControlFlowNode for x |
88
| import_as_attr.py:3:5:3:12 | ControlFlowNode for attr_ref |
9-
| import_as_attr.py:5:1:5:10 | Entry node for Function fun |
9+
| import_as_attr.py:5:1:5:10 | Entry definition for SsaSourceVariable attr_ref |
1010
| import_as_attr.py:6:5:6:5 | ControlFlowNode for y |
1111
| import_as_attr.py:6:9:6:16 | ControlFlowNode for attr_ref |

python/ql/test/experimental/dataflow/typetracking/tracked.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module TrackedTest implements TestSig {
2626
not e.getLocation().getStartLine() = 0 and
2727
// We do not wish to annotate scope entry definitions,
2828
// as they do not appear in the source code.
29-
not e.asCfgNode() = any(ScopeEntryDefinition def).getDefiningNode() and
29+
not e instanceof DataFlow::ScopeEntryDefinitionNode and
3030
tag = "tracked" and
3131
location = e.getLocation() and
3232
value = t.getAttr() and
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
| test_crosstalk.py:8:16:8:18 | ControlFlowNode for f() | bar |
2-
| test_crosstalk.py:8:16:8:18 | ControlFlowNode for f() | baz |
3-
| test_crosstalk.py:13:16:13:18 | ControlFlowNode for g() | bar |
42
| test_crosstalk.py:13:16:13:18 | ControlFlowNode for g() | baz |

0 commit comments

Comments
 (0)