@@ -288,42 +288,50 @@ class DataFlowExpr = Expr;
288
288
* Some syntaxtic constructs are handled separately.
289
289
*/
290
290
module LocalFlow {
291
- /** Holds if `nodeFrom` is the expression defining the value for the variable `nodeTo`. */
291
+ /**
292
+ * Holds if `nodeFrom` is the expression defining the value for the variable `nodeTo`.
293
+ * This includes cases like
294
+ * `x = f(42)`
295
+ * and
296
+ * `with f(42) as x:`
297
+ * where `nodeFrom` is `f(42)` and `nodeTo` is `x`.
298
+ */
292
299
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
- // General definition
303
- // TODO: remove other cases that are now redundant
304
- nodeFrom .( CfgNode ) .getNode ( ) = nodeTo .( CfgNode ) .getNode ( ) .( DefinitionNode ) .getValue ( )
305
- or
306
- // With definition
307
- // `with f(42) as x:`
308
- // nodeFrom is `f(42)`
309
- // nodeTo is `x`
310
- exists ( With with , ControlFlowNode contextManager , WithDefinition withDef , ControlFlowNode var |
311
- var = withDef .getDefiningNode ( )
312
- |
313
- nodeFrom .( CfgNode ) .getNode ( ) = contextManager and
314
- nodeTo .( CfgNode ) .getNode ( ) = var and
315
- // see `with_flow` in `python/ql/src/semmle/python/dataflow/Implementation.qll`
316
- with .getContextExpr ( ) = contextManager .getNode ( ) and
317
- with .getOptionalVars ( ) = var .getNode ( ) and
318
- contextManager .strictlyDominates ( var )
319
- // note: we allow this for both `with` and `async with`, since some
320
- // implementations do `async def __aenter__(self): return self`, so you can do
321
- // both:
322
- // * `foo = x.foo(); await foo.async_method(); foo.close()` and
323
- // * `async with x.foo() as foo: await foo.async_method()`.
324
- )
300
+ nodeFrom .( CfgNode ) .getNode ( ) = nodeTo .( CfgNode ) .getNode ( ) .( DefinitionNode ) .getValue ( ) and
301
+ // This excludes steps that should be jump steps,
302
+ // such as assignment of a parameters default value.
303
+ // (Since the parameter will be in the scope of the function,
304
+ // while the default value itself will be in the scope that _defines_ the
305
+ // function.)
306
+ nodeFrom .getEnclosingCallable ( ) = nodeTo .getEnclosingCallable ( )
325
307
}
326
308
309
+ // predicate generalDefinitionFlowStep(Node nodeFrom, Node nodeTo) {
310
+ // // General definition
311
+ // // TODO: remove other cases that are now redundant
312
+ // nodeFrom.(CfgNode).getNode() = nodeTo.(CfgNode).getNode().(DefinitionNode).getValue()
313
+ // }
314
+ // predicate localGeneral(
315
+ // Node nodeFrom, DataFlowCallable callableFrom, Node nodeTo, DataFlowCallable callableTo
316
+ // ) {
317
+ // generalDefinitionFlowStep(nodeFrom, nodeTo) and
318
+ // callableFrom = nodeFrom.getEnclosingCallable() and
319
+ // callableTo = nodeTo.getEnclosingCallable() and
320
+ // callableFrom != callableTo
321
+ // }
322
+ // predicate compareDef(Node nodeFrom, Node nodeTo, string message) {
323
+ // definitionFlowStep(nodeFrom, nodeTo) and
324
+ // not generalDefinitionFlowStep(nodeFrom, nodeTo) and
325
+ // message = "only in def"
326
+ // or
327
+ // not definitionFlowStep(nodeFrom, nodeTo) and
328
+ // generalDefinitionFlowStep(nodeFrom, nodeTo) and
329
+ // message = "only in general"
330
+ // or
331
+ // definitionFlowStep(nodeFrom, nodeTo) and
332
+ // generalDefinitionFlowStep(nodeFrom, nodeTo) and
333
+ // message = "in both"
334
+ // }
327
335
predicate expressionFlowStep ( Node nodeFrom , Node nodeTo ) {
328
336
// If expressions
329
337
nodeFrom .asCfgNode ( ) = nodeTo .asCfgNode ( ) .( IfExprNode ) .getAnOperand ( )
@@ -341,6 +349,19 @@ module LocalFlow {
341
349
matchFlowStep ( nodeFrom , nodeTo )
342
350
}
343
351
352
+ // predicate compareExpr(Node nodeFrom, Node nodeTo, string message) {
353
+ // expressionFlowStep(nodeFrom, nodeTo) and
354
+ // not generalDefinitionFlowStep(nodeFrom, nodeTo) and
355
+ // message = "only in def"
356
+ // or
357
+ // not expressionFlowStep(nodeFrom, nodeTo) and
358
+ // generalDefinitionFlowStep(nodeFrom, nodeTo) and
359
+ // message = "only in general"
360
+ // or
361
+ // expressionFlowStep(nodeFrom, nodeTo) and
362
+ // generalDefinitionFlowStep(nodeFrom, nodeTo) and
363
+ // message = "in both"
364
+ // }
344
365
predicate useToNextUse ( NameNode nodeFrom , NameNode nodeTo ) {
345
366
AdjacentUses:: adjacentUseUse ( nodeFrom , nodeTo )
346
367
}
@@ -509,14 +530,10 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
509
530
// Setting the possible values of the variable at the end of import time
510
531
nodeFrom = nodeTo .( ModuleVariableNode ) .getADefiningWrite ( )
511
532
or
512
- // a parameter with a default value, since the parameter will be in the scope of the
513
- // function, while the default value itself will be in the scope that _defines_ the
514
- // function.
515
- exists ( ParameterDefinition param |
516
- // note: we go to the _control-flow node_ of the parameter, and not the ESSA node of the parameter, since for type-tracking, the ESSA node is not a LocalSourceNode, so we would get in trouble.
517
- nodeFrom .asCfgNode ( ) = param .getDefault ( ) and
518
- nodeTo .asCfgNode ( ) = param .getDefiningNode ( )
519
- )
533
+ // Non-local definitions, such as assignment of a parameters default value.
534
+ nodeFrom .( CfgNode ) .getNode ( ) = nodeTo .( CfgNode ) .getNode ( ) .( DefinitionNode ) .getValue ( ) and
535
+ // This excludes steps that should be local.
536
+ nodeFrom .getEnclosingCallable ( ) != nodeTo .getEnclosingCallable ( )
520
537
}
521
538
522
539
//--------
0 commit comments