diff --git a/csharp/ql/lib/semmle/code/csharp/Assignable.qll b/csharp/ql/lib/semmle/code/csharp/Assignable.qll index a86e9d6de5c..066cdeaed15 100644 --- a/csharp/ql/lib/semmle/code/csharp/Assignable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Assignable.qll @@ -500,7 +500,9 @@ class AssignableDefinition extends TAssignableDefinition { */ pragma[nomagic] AssignableRead getAFirstRead() { - exists(Ssa::ExplicitDefinition def | result = def.getAFirstRead() | this = def.getADefinition()) + exists(Ssa::ExplicitDefinition def | result = Ssa::ssaGetAFirstUse(def) | + this = def.getADefinition() + ) } /** Gets a textual representation of this assignable definition. */ diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll index 693a91a3ce5..726f695e9b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll @@ -241,7 +241,7 @@ private Ssa::Definition getAnUltimateDefinition(Ssa::Definition def) { * exception. */ private predicate defReaches(Ssa::Definition def, ControlFlowNode cfn) { - def.getAFirstRead().getControlFlowNode() = cfn + Ssa::ssaGetAFirstUse(def).getControlFlowNode() = cfn or exists(ControlFlowNode mid | defReaches(def, mid) | SsaImpl::adjacentReadPairSameVar(_, mid, cfn) and diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll index 7670774da98..9568adf6c27 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll @@ -149,6 +149,47 @@ module Ssa { } } + /** + * Gets a read of the source variable underlying the SSA definition `def` + * that can be reached from `def` without passing through any + * other SSA definition or read. Example: + * + * ```csharp + * int Field; + * + * void SetField(int i) { + * this.Field = i; + * Use(this.Field); + * if (i > 0) + * this.Field = i - 1; + * else if (i < 0) + * SetField(1); + * Use(this.Field); + * Use(this.Field); + * } + * ``` + * + * - The read of `i` on line 4 can be reached from the explicit SSA + * definition (wrapping an implicit entry definition) on line 3. + * - The reads of `i` on lines 6 and 7 are not the first reads of any SSA + * definition. + * - The read of `this.Field` on line 5 can be reached from the explicit SSA + * definition on line 4. + * - The read of `this.Field` on line 10 can be reached from the phi node + * between lines 9 and 10. + * - The read of `this.Field` on line 11 is not the first read of any SSA + * definition. + * + * Subsequent reads can be found by following the steps defined by + * `AssignableRead.getANextRead()`. + */ + AssignableRead ssaGetAFirstUse(SsaDefinition def) { + exists(ControlFlowNode cfn | + SsaImpl::firstReadSameVar(def, cfn) and + result.getControlFlowNode() = cfn + ) + } + /** * A static single assignment (SSA) definition. Either an explicit variable * definition (`ExplicitDefinition`), an implicit variable definition @@ -229,6 +270,8 @@ module Ssa { } /** + * DEPRECATED: Use `ssaGetAFirstUse` instead. + * * Gets a read of the source variable underlying this SSA definition that * can be reached from this SSA definition without passing through any * other SSA definition or read. Example: @@ -262,9 +305,11 @@ module Ssa { * Subsequent reads can be found by following the steps defined by * `AssignableRead.getANextRead()`. */ - final AssignableRead getAFirstRead() { result = this.getAFirstReadAtNode(_) } + deprecated final AssignableRead getAFirstRead() { result = this.getAFirstReadAtNode(_) } /** + * DEPRECATED: Use `ssaGetAFirstUse` instead. + * * Gets a read of the source variable underlying this SSA definition at * control flow node `cfn` that can be reached from this SSA definition * without passing through any other SSA definition or read. Example: @@ -298,7 +343,7 @@ module Ssa { * Subsequent reads can be found by following the steps defined by * `AssignableRead.getANextRead()`. */ - final AssignableRead getAFirstReadAtNode(ControlFlowNode cfn) { + deprecated final AssignableRead getAFirstReadAtNode(ControlFlowNode cfn) { SsaImpl::firstReadSameVar(this, cfn) and result.getControlFlowNode() = cfn } diff --git a/csharp/ql/test/library-tests/dataflow/defuse/useUseEquivalence.ql b/csharp/ql/test/library-tests/dataflow/defuse/useUseEquivalence.ql index bc3d6d422a6..b6610aa2554 100644 --- a/csharp/ql/test/library-tests/dataflow/defuse/useUseEquivalence.ql +++ b/csharp/ql/test/library-tests/dataflow/defuse/useUseEquivalence.ql @@ -42,9 +42,9 @@ private TLocalScopeVariableReadOrSsaDef getANextReadOrDef(TLocalScopeVariableRea ) or exists(Ssa::Definition ssaDef | prev = TSsaDefinition(ssaDef) | - result = TLocalScopeVariableRead(ssaDef.getAFirstRead()) + result = TLocalScopeVariableRead(Ssa::ssaGetAFirstUse(ssaDef)) or - not exists(ssaDef.getAFirstRead()) and + not exists(Ssa::ssaGetAFirstUse(ssaDef)) and exists(Ssa::PhiNode phi | phi.getAnInput() = ssaDef and result = TSsaDefinition(phi)