diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll index 6b756d67bc6a..ec14749bece7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll @@ -564,8 +564,10 @@ class EssaVariable extends Ssa::SsaDefinition { * library. Provides the same interface as legacy * `semmle.python.essa.SsaCompute::AdjacentUses`. */ +cached module AdjacentUses { /** Holds if `nodeFrom` and `nodeTo` are adjacent uses of the same SSA variable. */ + cached predicate adjacentUseUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) { exists(CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 | Impl::adjacentUseUse(bb1, i1, bb2, i2, _, _) and @@ -575,6 +577,7 @@ module AdjacentUses { } /** Holds if `use` is a first use of definition `def`. */ + cached predicate firstUse(Ssa::SsaDefinition def, Cfg::NameNode use) { exists(CfgImpl::BasicBlock bb, int i | Impl::firstUse(def, bb, i, _) and @@ -586,6 +589,7 @@ module AdjacentUses { * Holds if `use` is any reachable use of definition `def`. Combines * `firstUse` with transitive use-use adjacency. */ + cached predicate useOfDef(Ssa::SsaDefinition def, Cfg::NameNode use) { firstUse(def, use) or diff --git a/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.expected b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.expected new file mode 100644 index 000000000000..f93ea8a137b2 --- /dev/null +++ b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.expected @@ -0,0 +1,12 @@ +exposed_first_use_count +| 9 | +exposed_adjacent_use_count +| 1 | +exposed_use_of_def_count +| 10 | +first_use_projection_mismatch_count +| 0 | +adjacent_use_projection_mismatch_count +| 0 | +use_of_def_expansion_mismatch_count +| 0 | diff --git a/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.ql b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.ql new file mode 100644 index 000000000000..1b6a8790dd28 --- /dev/null +++ b/python/ql/test/library-tests/dataflow-new-ssa/AdjacentUsesContract.ql @@ -0,0 +1,72 @@ +import python +private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl +private import semmle.python.controlflow.internal.Cfg as Cfg +private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl + +private predicate projectedFirstUse(SsaImpl::Definition def, Cfg::NameNode use) { + exists(CfgImpl::BasicBlock bb, int i | + SsaImpl::Impl::firstUse(def, bb, i, _) and + use = bb.getNode(i) + ) +} + +private predicate projectedAdjacentUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) { + exists(CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 | + SsaImpl::Impl::adjacentUseUse(bb1, i1, bb2, i2, _, _) and + nodeFrom = bb1.getNode(i1) and + nodeTo = bb2.getNode(i2) + ) +} + +private predicate expandedUseOfDef(SsaImpl::Definition def, Cfg::NameNode use) { + exists(Cfg::NameNode first | + SsaImpl::AdjacentUses::firstUse(def, first) and + SsaImpl::AdjacentUses::adjacentUseUse*(first, use) + ) +} + +query int exposed_first_use_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | SsaImpl::AdjacentUses::firstUse(def, use)) +} + +query int exposed_adjacent_use_count() { + result = + count(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo | + SsaImpl::AdjacentUses::adjacentUseUse(nodeFrom, nodeTo) + ) +} + +query int exposed_use_of_def_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | SsaImpl::AdjacentUses::useOfDef(def, use)) +} + +query int first_use_projection_mismatch_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | + SsaImpl::AdjacentUses::firstUse(def, use) and not projectedFirstUse(def, use) + or + projectedFirstUse(def, use) and not SsaImpl::AdjacentUses::firstUse(def, use) + ) +} + +query int adjacent_use_projection_mismatch_count() { + result = + count(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo | + SsaImpl::AdjacentUses::adjacentUseUse(nodeFrom, nodeTo) and + not projectedAdjacentUse(nodeFrom, nodeTo) + or + projectedAdjacentUse(nodeFrom, nodeTo) and + not SsaImpl::AdjacentUses::adjacentUseUse(nodeFrom, nodeTo) + ) +} + +query int use_of_def_expansion_mismatch_count() { + result = + count(SsaImpl::Definition def, Cfg::NameNode use | + SsaImpl::AdjacentUses::useOfDef(def, use) and not expandedUseOfDef(def, use) + or + expandedUseOfDef(def, use) and not SsaImpl::AdjacentUses::useOfDef(def, use) + ) +} diff --git a/python/ql/test/library-tests/dataflow-new-ssa/test.py b/python/ql/test/library-tests/dataflow-new-ssa/test.py index a05c871e04df..ad5f47c3852c 100644 --- a/python/ql/test/library-tests/dataflow-new-ssa/test.py +++ b/python/ql/test/library-tests/dataflow-new-ssa/test.py @@ -31,6 +31,11 @@ def basic_assign(): # $ def=basic_assign return y # $ use=y +def repeated_use(x): # $ def=repeated_use def=x + first = x # $ def=first use=x + return x + first # $ use=x use=first + + def reassignment(): # $ def=reassignment x = 1 x = 2 # $ def=x