@@ -288,53 +288,65 @@ class DataFlowExpr = Expr;
288
288
* Some syntaxtic constructs are handled separately.
289
289
*/
290
290
module LocalFlow {
291
+ private predicate capturedVariableRead ( Node n ) {
292
+ n .asExpr ( ) = any ( VariableCapture:: CapturedVariable v ) .getALoad ( )
293
+ }
294
+
291
295
/** Holds if `nodeFrom` is the expression defining the value for the variable `nodeTo`. */
292
296
predicate definitionFlowStep ( Node nodeFrom , Node nodeTo ) {
293
- // Definition
294
- // `x = f(42)`
295
- // nodeFrom is `f(42)`
296
- // nodeTo is `x`
297
- exists ( AssignmentDefinition def |
298
- nodeFrom .( CfgNode ) .getNode ( ) = def .getValue ( ) and
299
- nodeTo .( CfgNode ) .getNode ( ) = def .getDefiningNode ( )
300
- )
301
- or
302
- // With definition
303
- // `with f(42) as x:`
304
- // nodeFrom is `f(42)`
305
- // nodeTo is `x`
306
- exists ( With with , ControlFlowNode contextManager , WithDefinition withDef , ControlFlowNode var |
307
- var = withDef .getDefiningNode ( )
308
- |
309
- nodeFrom .( CfgNode ) .getNode ( ) = contextManager and
310
- nodeTo .( CfgNode ) .getNode ( ) = var and
311
- // see `with_flow` in `python/ql/src/semmle/python/dataflow/Implementation.qll`
312
- with .getContextExpr ( ) = contextManager .getNode ( ) and
313
- with .getOptionalVars ( ) = var .getNode ( ) and
314
- contextManager .strictlyDominates ( var )
315
- // note: we allow this for both `with` and `async with`, since some
316
- // implementations do `async def __aenter__(self): return self`, so you can do
317
- // both:
318
- // * `foo = x.foo(); await foo.async_method(); foo.close()` and
319
- // * `async with x.foo() as foo: await foo.async_method()`.
297
+ not capturedVariableRead ( nodeTo ) and
298
+ (
299
+ // Definition
300
+ // `x = f(42)`
301
+ // nodeFrom is `f(42)`
302
+ // nodeTo is `x`
303
+ exists ( AssignmentDefinition def |
304
+ nodeFrom .( CfgNode ) .getNode ( ) = def .getValue ( ) and
305
+ nodeTo .( CfgNode ) .getNode ( ) = def .getDefiningNode ( )
306
+ )
307
+ or
308
+ // With definition
309
+ // `with f(42) as x:`
310
+ // nodeFrom is `f(42)`
311
+ // nodeTo is `x`
312
+ exists (
313
+ With with , ControlFlowNode contextManager , WithDefinition withDef , ControlFlowNode var
314
+ |
315
+ var = withDef .getDefiningNode ( )
316
+ |
317
+ nodeFrom .( CfgNode ) .getNode ( ) = contextManager and
318
+ nodeTo .( CfgNode ) .getNode ( ) = var and
319
+ // see `with_flow` in `python/ql/src/semmle/python/dataflow/Implementation.qll`
320
+ with .getContextExpr ( ) = contextManager .getNode ( ) and
321
+ with .getOptionalVars ( ) = var .getNode ( ) and
322
+ contextManager .strictlyDominates ( var )
323
+ // note: we allow this for both `with` and `async with`, since some
324
+ // implementations do `async def __aenter__(self): return self`, so you can do
325
+ // both:
326
+ // * `foo = x.foo(); await foo.async_method(); foo.close()` and
327
+ // * `async with x.foo() as foo: await foo.async_method()`.
328
+ )
320
329
)
321
330
}
322
331
323
332
predicate expressionFlowStep ( Node nodeFrom , Node nodeTo ) {
324
- // If expressions
325
- nodeFrom .asCfgNode ( ) = nodeTo .asCfgNode ( ) .( IfExprNode ) .getAnOperand ( )
326
- or
327
- // Assignment expressions
328
- nodeFrom .asCfgNode ( ) = nodeTo .asCfgNode ( ) .( AssignmentExprNode ) .getValue ( )
329
- or
330
- // boolean inline expressions such as `x or y` or `x and y`
331
- nodeFrom .asCfgNode ( ) = nodeTo .asCfgNode ( ) .( BoolExprNode ) .getAnOperand ( )
332
- or
333
- // Flow inside an unpacking assignment
334
- iterableUnpackingFlowStep ( nodeFrom , nodeTo )
335
- or
336
- // Flow inside a match statement
337
- matchFlowStep ( nodeFrom , nodeTo )
333
+ not capturedVariableRead ( nodeTo ) and
334
+ (
335
+ // If expressions
336
+ nodeFrom .asCfgNode ( ) = nodeTo .asCfgNode ( ) .( IfExprNode ) .getAnOperand ( )
337
+ or
338
+ // Assignment expressions
339
+ nodeFrom .asCfgNode ( ) = nodeTo .asCfgNode ( ) .( AssignmentExprNode ) .getValue ( )
340
+ or
341
+ // boolean inline expressions such as `x or y` or `x and y`
342
+ nodeFrom .asCfgNode ( ) = nodeTo .asCfgNode ( ) .( BoolExprNode ) .getAnOperand ( )
343
+ or
344
+ // Flow inside an unpacking assignment
345
+ iterableUnpackingFlowStep ( nodeFrom , nodeTo )
346
+ or
347
+ // Flow inside a match statement
348
+ matchFlowStep ( nodeFrom , nodeTo )
349
+ )
338
350
}
339
351
340
352
predicate useToNextUse ( NameNode nodeFrom , NameNode nodeTo ) {
@@ -346,22 +358,25 @@ module LocalFlow {
346
358
}
347
359
348
360
predicate useUseFlowStep ( Node nodeFrom , Node nodeTo ) {
349
- // First use after definition
350
- // `y = 42`
351
- // `x = f(y)`
352
- // nodeFrom is `y` on first line
353
- // nodeTo is `y` on second line
354
- exists ( EssaDefinition def |
355
- nodeFrom .( CfgNode ) .getNode ( ) = def .( EssaNodeDefinition ) .getDefiningNode ( ) and
356
- AdjacentUses:: firstUse ( def , nodeTo .( CfgNode ) .getNode ( ) )
361
+ // not capturedVariableRead(nodeTo) and
362
+ (
363
+ // First use after definition
364
+ // `y = 42`
365
+ // `x = f(y)`
366
+ // nodeFrom is `y` on first line
367
+ // nodeTo is `y` on second line
368
+ exists ( EssaDefinition def |
369
+ nodeFrom .( CfgNode ) .getNode ( ) = def .( EssaNodeDefinition ) .getDefiningNode ( ) and
370
+ AdjacentUses:: firstUse ( def , nodeTo .( CfgNode ) .getNode ( ) )
371
+ )
372
+ or
373
+ // Next use after use
374
+ // `x = f(y)`
375
+ // `z = y + 1`
376
+ // nodeFrom is 'y' on first line, cfg node
377
+ // nodeTo is `y` on second line, cfg node
378
+ useToNextUse ( nodeFrom .asCfgNode ( ) , nodeTo .asCfgNode ( ) )
357
379
)
358
- or
359
- // Next use after use
360
- // `x = f(y)`
361
- // `z = y + 1`
362
- // nodeFrom is 'y' on first line, cfg node
363
- // nodeTo is `y` on second line, cfg node
364
- useToNextUse ( nodeFrom .asCfgNode ( ) , nodeTo .asCfgNode ( ) )
365
380
}
366
381
367
382
predicate localFlowStep ( Node nodeFrom , Node nodeTo ) {
0 commit comments