diff --git a/rust/ql/lib/codeql/rust/elements/internal/ParamBaseImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/ParamBaseImpl.qll index 3b0f82eb6c3..5badd696f1a 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/ParamBaseImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/ParamBaseImpl.qll @@ -1,4 +1,3 @@ -// generated by codegen, remove this comment if you wish to edit this file /** * This module provides a hand-modifiable wrapper around the generated class `ParamBase`. * @@ -6,6 +5,7 @@ */ private import codeql.rust.elements.internal.generated.ParamBase +private import codeql.rust.elements.Callable /** * INTERNAL: This module contains the customizable definition of `ParamBase` and should not @@ -15,5 +15,8 @@ module Impl { /** * A normal parameter, `Param`, or a self parameter `SelfParam`. */ - class ParamBase extends Generated::ParamBase { } + class ParamBase extends Generated::ParamBase { + /** Gets the callable this parameter belongs to. */ + Callable getCallable() { this = result.getParamList().getAParamBase() } + } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll index 37a2e4dacc0..f1df0a144b8 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll @@ -1,71 +1,13 @@ private import rust +private import codeql.namebinding.LocalNameBinding private import codeql.rust.controlflow.ControlFlowGraph private import codeql.rust.internal.PathResolution as PathResolution private import codeql.rust.elements.internal.generated.ParentChild as ParentChild private import codeql.rust.elements.internal.AstNodeImpl::Impl as AstNodeImpl private import codeql.rust.elements.internal.PathImpl::Impl as PathImpl private import codeql.rust.elements.internal.FormatTemplateVariableAccessImpl::Impl as FormatTemplateVariableAccessImpl -private import codeql.util.DenseRank module Impl { - /** - * A variable scope. Either a block `{ ... }`, the guard/rhs - * of a match arm, or the body of a closure. - */ - abstract class VariableScope extends AstNode { } - - class BlockExprScope extends VariableScope, BlockExpr { } - - class MatchArmExprScope extends VariableScope { - MatchArmExprScope() { this = any(MatchArm arm).getExpr() } - } - - class MatchArmGuardScope extends VariableScope { - MatchArmGuardScope() { this = any(MatchArm arm).getGuard() } - } - - class ClosureBodyScope extends VariableScope { - ClosureBodyScope() { this = any(ClosureExpr ce).getBody() } - } - - /** - * A scope for conditions, which may introduce variables using `let` expressions. - * - * Such variables are only available in the body guarded by the condition. - */ - class ConditionScope extends VariableScope { - private AstNode parent; - private AstNode body; - - ConditionScope() { - parent = - any(IfExpr ie | - this = ie.getCondition() and - body = ie.getThen() - ) - or - parent = - any(WhileExpr we | - this = we.getCondition() and - body = we.getLoopBody() - ) - or - parent = - any(MatchArm ma | - this = ma.getGuard() and - body = ma.getExpr() - ) - } - - /** Gets the parent of this condition. */ - AstNode getParent() { result = parent } - - /** - * Gets the body in which variables introduced in this scope are available. - */ - AstNode getBody() { result = body } - } - private Pat getAPatAncestor(Pat p) { (p instanceof IdentPat or p instanceof OrPat) and exists(Pat p0 | result = p0.getParentPat() | @@ -100,7 +42,7 @@ module Impl { */ cached predicate variableDecl(AstNode definingNode, Name name, string text) { - Cached::ref() and + CachedStage::ref() and exists(SelfParam sp | name = sp.getName() and definingNode = name and @@ -127,34 +69,204 @@ module Impl { ) } + /** + * `let` chains like + * + * ```rust + * if let x1 = ... && let x2 = ... && ... && let xn = ... { ... } + * ``` + * + * are parsed left-associatively, so the AST for the condition looks like + * + * ```rust + * ((let x1 = ... && let x2 = ...) && ...) & let xn = ... + * ``` + * + * This, however, does not work with scoping and shadowing, so we instead treat + * `let` chains as if there is just a single root `&&` node with `n` children, + * skipping all intermediate `&&` nodes. + */ + private module LetChains { + predicate isLetChainAncestor(LogicalAndExpr lae) { + lae.getAnOperand() instanceof LetExpr + or + isLetChainAncestor(lae.getLhs()) + } + + private predicate isLetChainRoot(LogicalAndExpr root) { + isLetChainAncestor(root) and + not root = any(LogicalAndExpr lae).getLhs() + } + + private predicate leftMostChildOfLetChainRoot(LogicalAndExpr left, LogicalAndExpr root) { + isLetChainRoot(root) and + left = root.getLhs*() and + not left.getLhs() instanceof LogicalAndExpr + } + + private AstNode getLetChainChild(LogicalAndExpr sub, LogicalAndExpr root, int i) { + leftMostChildOfLetChainRoot(sub, root) and + i = 1 and + result = sub.getRhs() + or + exists(LogicalAndExpr mid | + exists(getLetChainChild(mid, root, i - 1)) and + sub.getLhs() = mid and + result = sub.getRhs() + ) + } + + AstNode getLetChainChild(LogicalAndExpr lae, int i) { + exists(LogicalAndExpr left | + leftMostChildOfLetChainRoot(left, lae) and + i = 0 and + result = left.getLhs() + ) + or + result = getLetChainChild(_, lae, i) + } + } + + private import LetChains + + private module Input implements LocalNameBindingInputSig { + private import rust as Rust + + predicate cacheRevRef() { + (variableDecl(_, _, _) implies any()) + or + (exists(VariableReadAccess a) implies any()) + or + (exists(VariableWriteAccess a) implies any()) + or + (exists(any(Variable v).getParameter()) implies any()) + } + + class AstNode = Rust::AstNode; + + AstNode getChild(AstNode parent, int index) { + result = ParentChild::getImmediateChild(parent, index) and + not isLetChainAncestor(parent) + or + result = getLetChainChild(parent, index) + or + exists(Format f | + f = result.(FormatTemplateVariableAccess).getArgument().getParent() and + parent = f.getParent() and + index = f.getIndex() + ) + } + + abstract class Conditional extends AstNode { + abstract AstNode getCondition(); + + abstract AstNode getThen(); + + abstract AstNode getElse(); + } + + private class IfExprConditional extends Conditional instanceof IfExpr { + override AstNode getCondition() { result = IfExpr.super.getCondition() } + + override AstNode getThen() { result = IfExpr.super.getThen() } + + override AstNode getElse() { result = IfExpr.super.getElse() } + } + + private class WhileExprConditional extends Conditional instanceof WhileExpr { + override AstNode getCondition() { result = WhileExpr.super.getCondition() } + + override AstNode getThen() { result = WhileExpr.super.getLoopBody() } + + override AstNode getElse() { none() } + } + + private class MatchGuardConditional extends Conditional instanceof MatchGuard { + override AstNode getCondition() { result = MatchGuard.super.getCondition() } + + override AstNode getThen() { + exists(MatchArm arm | this = arm.getGuard() and result = arm.getExpr()) + } + + override AstNode getElse() { none() } + } + + abstract class SiblingShadowingDecl extends AstNode { + abstract AstNode getLhs(); + + abstract AstNode getRhs(); + + abstract AstNode getElse(); + } + + private class LetStmtSiblingShadowingDecl extends SiblingShadowingDecl instanceof LetStmt { + override AstNode getLhs() { result = LetStmt.super.getPat() } + + override AstNode getRhs() { result = LetStmt.super.getInitializer() } + + override AstNode getElse() { result = LetStmt.super.getLetElse() } + } + + private class LetExprSiblingShadowingDecl extends SiblingShadowingDecl instanceof LetExpr { + override AstNode getLhs() { result = LetExpr.super.getPat() } + + override AstNode getRhs() { result = LetExpr.super.getScrutinee() } + + override AstNode getElse() { none() } + } + + predicate declInScope(AstNode definingNode, string name, AstNode scope) { + // local variable + exists(Name n | variableDecl(definingNode, n, name) | + scope = any(SelfParam self | n = self.getName()).getCallable() + or + exists(Pat pat, Pat pat0 | + pat = getAPatAncestor*(pat0) and + (pat0 = definingNode or pat0.(IdentPat).getName() = n) + | + scope = any(MatchArm arm | pat = arm.getPat()) + or + scope = any(Input::SiblingShadowingDecl let | pat = let.getLhs()) + or + scope = any(ForExpr fe | pat = fe.getPat()).getLoopBody() + or + scope = any(Param p | pat = p.getPat()).getCallable() + ) + ) + or + // local function; behave as if they are defined at the beginning of the scope + definingNode = scope.(BlockExpr).getStmtList().getAStatement() and + name = definingNode.(Function).getName().getText() + } + + predicate accessCand(AstNode n, string name) { + name = n.(PathExpr).getPath().(PathImpl::IdentPath).getName() + or + name = n.(FormatTemplateVariableAccess).getName() + } + } + + private import LocalNameBinding + /** A variable. */ - class Variable extends MkVariable { - private AstNode definingNode; - private string text; - - Variable() { this = MkVariable(definingNode, text) } - - /** Gets the name of this variable as a string. */ - string getText() { result = text } - - /** Gets the location of this variable. */ - Location getLocation() { result = definingNode.getLocation() } - - /** Gets a textual representation of this variable. */ - string toString() { result = this.getText() } + class Variable extends Local { + Variable() { variableDecl(this.getDefiningNode(), _, _) } /** Gets an access to this variable. */ VariableAccess getAnAccess() { result.getVariable() = this } + /** Gets the name of this variable. */ + string getText() { result = super.getName() } + /** * Get the name of this variable. * * Normally, the name is unique, except when introduced in an or pattern. */ - Name getName() { variableDecl(definingNode, result, text) } + Name getName() { variableDecl(this.getDefiningNode(), result, super.getName()) } /** Gets the block that encloses this variable, if any. */ - BlockExpr getEnclosingBlock() { result = definingNode.getEnclosingBlock() } + BlockExpr getEnclosingBlock() { result = this.getDefiningNode().getEnclosingBlock() } /** Gets the `self` parameter that declares this variable, if any. */ SelfParam getSelfParam() { result.getName() = this.getName() } @@ -173,12 +285,20 @@ module Impl { IdentPat getPat() { result.getName() = this.getName() } /** Gets the enclosing CFG scope for this variable declaration. */ - CfgScope getEnclosingCfgScope() { result = definingNode.getEnclosingCfgScope() } + CfgScope getEnclosingCfgScope() { result = this.getDefiningNode().getEnclosingCfgScope() } - /** Gets the `let` statement that introduces this variable, if any. */ + /** + * Gets the `let` statement that introduces this variable, if any. + * + * This is restricted to simple `let` statements of the form `let x = ...;`. + */ LetStmt getLetStmt() { this.getPat() = result.getPat() } - /** Gets the `let` expression that introduces this variable, if any. */ + /** + * Gets the `let` expression that introduces this variable, if any. + * + * This is restricted to simple `let` expressions of the form `let x = ...`. + */ LetExpr getLetExpr() { this.getPat() = result.getPat() } /** Gets the initial value of this variable, if any. */ @@ -193,10 +313,10 @@ module Impl { /** Gets the parameter that introduces this variable, if any. */ cached ParamBase getParameter() { - Cached::ref() and + CachedStage::ref() and result = this.getSelfParam() or - result.(Param).getPat() = getAVariablePatAncestor(this) + result.(Param).getPat() = getAPatAncestor*(this.getPat()) } /** Hold is this variable is mutable. */ @@ -206,474 +326,17 @@ module Impl { predicate isImmutable() { not this.isMutable() } } - /** - * A path expression that may access a local variable. These are paths that - * only consist of a simple name (i.e., without generic arguments, - * qualifiers, etc.). - */ - private class VariableAccessCand extends PathExprBase { - string name_; - - VariableAccessCand() { - name_ = this.(PathExpr).getPath().(PathImpl::IdentPath).getName() - or - this.(FormatTemplateVariableAccess).getName() = name_ - } - - string toString() { result = name_ } - - string getName() { result = name_ } - } - - pragma[nomagic] - private Element getImmediateChildAdj(Element e, int preOrd, int index) { - result = ParentChild::getImmediateChild(e, index) and - preOrd = 0 and - not exists(ConditionScope cs | - e = cs.getParent() and - result = cs.getBody() - ) - or - result = e.(ConditionScope).getBody() and - preOrd = 1 and - index = 0 - } - - /** - * An adjusted version of `ParentChild::getImmediateChild`, which makes the following - * two adjustments: - * - * 1. For conditions like `if cond body`, instead of letting `body` be the second child - * of `if`, we make it the last child of `cond`. This ensures that variables - * introduced in the `cond` scope are available in `body`. - * - * 2. A similar adjustment is made for `while` loops: the body of the loop is made a - * child of the loop condition instead of the loop itself. - */ - pragma[nomagic] - private Element getImmediateChildAdj(Element e, int index) { - result = - rank[index + 1](Element res, int preOrd, int i | - res = getImmediateChildAdj(e, preOrd, i) - | - res order by preOrd, i - ) - } - - private Element getImmediateParentAdj(Element e) { e = getImmediateChildAdj(result, _) } - - private AstNode getAnAncestorInVariableScope(AstNode n) { - ( - n instanceof Pat or - n instanceof VariableAccessCand or - n instanceof LetStmt or - n = any(LetExpr le).getScrutinee() or - n instanceof VariableScope - ) and - exists(AstNode n0 | - result = getImmediateParentAdj(n0) or - result = n0.(FormatTemplateVariableAccess).getArgument().getParent().getParent() - | - n0 = n - or - n0 = getAnAncestorInVariableScope(n) and - not n0 instanceof VariableScope - ) - } - - /** Gets the immediately enclosing variable scope of `n`. */ - private VariableScope getEnclosingScope(AstNode n) { result = getAnAncestorInVariableScope(n) } - - /** - * Get all the pattern ancestors of this variable up to an including the - * root of the pattern. - */ - private Pat getAVariablePatAncestor(Variable v) { - result = v.getPat() - or - exists(Pat mid | - mid = getAVariablePatAncestor(v) and - result = mid.getParentPat() - ) - } - - /** - * Holds if a parameter declares the variable `v` inside variable scope `scope`. - */ - private predicate parameterDeclInScope(Variable v, VariableScope scope) { - exists(Callable f | - v.getParameter() = f.getParamList().getAParamBase() and - scope = f.getBody() - ) - } - - /** A subset of `Element`s for which we want to compute pre-order numbers. */ - private class RelevantElement extends Element { - RelevantElement() { - this instanceof VariableScope or - this instanceof VariableAccessCand or - this instanceof LetStmt or - this = any(LetExpr le).getScrutinee() or - getImmediateChildAdj(this, _) instanceof RelevantElement - } - - pragma[nomagic] - private RelevantElement getChild(int index) { result = getImmediateChildAdj(this, index) } - - pragma[nomagic] - private RelevantElement getImmediateChildAdjMin(int index) { - // A child may have multiple positions for different accessors, - // so always use the first - result = this.getChild(index) and - index = min(int i | result = this.getChild(i) | i) - } - - pragma[nomagic] - RelevantElement getImmediateChildAdj(int index) { - result = - rank[index + 1](Element res, int i | res = this.getImmediateChildAdjMin(i) | res order by i) - } - - pragma[nomagic] - RelevantElement getImmediateLastChild() { - exists(int last | - result = this.getImmediateChildAdj(last) and - not exists(this.getImmediateChildAdj(last + 1)) - ) - } - } - - /** - * Gets the pre-order numbering of `n`, where the immediately enclosing - * variable scope of `n` is `scope`. - */ - pragma[nomagic] - private int getPreOrderNumbering(VariableScope scope, RelevantElement n) { - n = scope and - result = 0 - or - exists(RelevantElement parent | - not parent instanceof VariableScope - or - parent = scope - | - // first child of a previously numbered node - result = getPreOrderNumbering(scope, parent) + 1 and - n = parent.getImmediateChildAdj(0) - or - // non-first child of a previously numbered node - exists(RelevantElement child, int i | - result = getLastPreOrderNumbering(scope, child) + 1 and - child = parent.getImmediateChildAdj(i) and - n = parent.getImmediateChildAdj(i + 1) - ) - ) - } - - /** - * Gets the pre-order numbering of the _last_ node nested under `n`, where the - * immediately enclosing variable scope of `n` (and the last node) is `scope`. - */ - pragma[nomagic] - private int getLastPreOrderNumbering(VariableScope scope, RelevantElement n) { - exists(RelevantElement leaf | - result = getPreOrderNumbering(scope, leaf) and - leaf != scope and - ( - not exists(leaf.getImmediateChildAdj(_)) - or - leaf instanceof VariableScope - ) - | - n = leaf - or - n.getImmediateLastChild() = leaf and - not n instanceof VariableScope - ) - or - exists(RelevantElement mid | - mid = n.getImmediateLastChild() and - result = getLastPreOrderNumbering(scope, mid) and - not mid instanceof VariableScope and - not n instanceof VariableScope - ) - } - - /** - * Holds if `v` is named `name` and is declared inside variable scope - * `scope`. The pre-order numbering of the binding site of `v`, amongst - * all nodes nested under `scope`, is `ord`. - */ - private predicate variableDeclInScope(Variable v, VariableScope scope, string name, int ord) { - name = v.getText() and - ( - parameterDeclInScope(v, scope) and - ord = getPreOrderNumbering(scope, scope) - or - exists(Pat pat | pat = getAVariablePatAncestor(v) | - exists(MatchArm arm | - pat = arm.getPat() and - ord = getPreOrderNumbering(scope, scope) - | - scope = arm.getGuard() - or - not arm.hasGuard() and scope = arm.getExpr() - ) - or - exists(LetStmt let | - let.getPat() = pat and - scope = getEnclosingScope(let) and - // for `let` statements, variables are bound _after_ the statement, i.e. - // not in the RHS - ord = getLastPreOrderNumbering(scope, let) + 1 - ) - or - exists(LetExpr let, Expr scrutinee | - let.getPat() = pat and - scrutinee = let.getScrutinee() and - scope = getEnclosingScope(scrutinee) and - // for `let` expressions, variables are bound _after_ the expression, i.e. - // not in the RHS - ord = getLastPreOrderNumbering(scope, scrutinee) + 1 - ) - or - exists(ForExpr fe | - fe.getPat() = pat and - scope = fe.getLoopBody() and - ord = getPreOrderNumbering(scope, scope) - ) - ) - ) - } - - /** - * Holds if `cand` may access a variable named `name` at pre-order number `ord` - * in the variable scope `scope`. - * - * `nestLevel` is the number of nested scopes that need to be traversed - * to reach `scope` from `cand`. - */ - private predicate variableAccessCandInScope( - VariableAccessCand cand, VariableScope scope, string name, int nestLevel, int ord - ) { - name = cand.getName() and - ( - scope = cand - or - not cand instanceof VariableScope and - scope = getEnclosingScope(cand) - ) and - ord = getPreOrderNumbering(scope, cand) and - nestLevel = 0 - or - exists(VariableScope inner | - variableAccessCandInScope(cand, inner, name, nestLevel - 1, _) and - scope = getEnclosingScope(inner) and - // Use the pre-order number of the inner scope as the number of the access. This allows - // us to collapse multiple accesses in inner scopes to a single entity - ord = getPreOrderNumbering(scope, inner) - ) - } - - private newtype TDefOrAccessCand = - TDefOrAccessCandNestedFunction(Function f, BlockExprScope scope) { - f = scope.getStmtList().getAStatement() - } or - TDefOrAccessCandVariable(Variable v) or - TDefOrAccessCandVariableAccessCand(VariableAccessCand va) - - /** - * A nested function declaration, variable declaration, or variable (or function) - * access candidate. - * - * In order to determine whether a candidate is an actual variable/function access, - * we rank declarations and candidates by their position in the AST. - * - * The ranking must take names into account, but also variable scopes; below a comment - * `rank(scope, name, i)` means that the declaration/access on the given line has rank - * `i` amongst all declarations/accesses inside variable scope `scope`, for name `name`: - * - * ```rust - * fn f() { // scope0 - * let x = 0; // rank(scope0, "x", 0) - * use(x); // rank(scope0, "x", 1) - * let x = // rank(scope0, "x", 3) - * x + 1; // rank(scope0, "x", 2) - * let y = // rank(scope0, "y", 0) - * x; // rank(scope0, "x", 4) - * - * { // scope1 - * use(x); // rank(scope1, "x", 0), rank(scope0, "x", 4) - * use(y); // rank(scope1, "y", 0), rank(scope0, "y", 1) - * let x = 2; // rank(scope1, "x", 1) - * use(x); // rank(scope1, "x", 2), rank(scope0, "x", 4) - * } - * } - * ``` - * - * Function/variable declarations are only ranked in the scope that they bind into, - * while accesses candidates propagate outwards through scopes, as they may access - * declarations from outer scopes. - * - * For an access candidate with ranks `{ rank(scope_i, name, rnk_i) | i in I }` and - * declarations `d in D` with ranks `rnk(scope_d, name, rnk_d)`, the target is - * calculated as - * ``` - * max_{i in I} ( - * max_{d in D | scope_d = scope_i and rnk_d < rnk_i} ( - * d - * ) - * ) - * ``` - * - * i.e., its the nearest declaration before the access in the same (or outer) scope - * as the access. - */ - abstract private class DefOrAccessCand extends TDefOrAccessCand { - abstract string toString(); - - abstract Location getLocation(); - - pragma[nomagic] - abstract predicate rankBy(string name, VariableScope scope, int ord, int kind); - } - - abstract private class NestedFunctionOrVariable extends DefOrAccessCand { } - - private class DefOrAccessCandNestedFunction extends NestedFunctionOrVariable, - TDefOrAccessCandNestedFunction - { - private Function f; - private BlockExprScope scope_; - - DefOrAccessCandNestedFunction() { this = TDefOrAccessCandNestedFunction(f, scope_) } - - override string toString() { result = f.toString() } - - override Location getLocation() { result = f.getLocation() } - - override predicate rankBy(string name, VariableScope scope, int ord, int kind) { - // nested functions behave as if they are defined at the beginning of the scope - name = f.getName().getText() and - scope = scope_ and - ord = 0 and - kind = 0 - } - } - - private class DefOrAccessCandVariable extends NestedFunctionOrVariable, TDefOrAccessCandVariable { - private Variable v; - - DefOrAccessCandVariable() { this = TDefOrAccessCandVariable(v) } - - override string toString() { result = v.toString() } - - override Location getLocation() { result = v.getLocation() } - - override predicate rankBy(string name, VariableScope scope, int ord, int kind) { - variableDeclInScope(v, scope, name, ord) and - kind = 1 - } - } - - private class DefOrAccessCandVariableAccessCand extends DefOrAccessCand, - TDefOrAccessCandVariableAccessCand - { - private VariableAccessCand va; - - DefOrAccessCandVariableAccessCand() { this = TDefOrAccessCandVariableAccessCand(va) } - - override string toString() { result = va.toString() } - - override Location getLocation() { result = va.getLocation() } - - override predicate rankBy(string name, VariableScope scope, int ord, int kind) { - variableAccessCandInScope(va, scope, name, _, ord) and - kind = 2 - } - } - - private module DenseRankInput implements DenseRankInputSig2 { - class C1 = VariableScope; - - class C2 = string; - - class Ranked = DefOrAccessCand; - - int getRank(VariableScope scope, string name, DefOrAccessCand v) { - v = - rank[result](DefOrAccessCand v0, int ord, int kind | - v0.rankBy(name, scope, ord, kind) - | - v0 order by ord, kind - ) - } - } - - /** - * Gets the rank of `v` amongst all other declarations or access candidates - * to a variable named `name` in the variable scope `scope`. - */ - private int rankVariableOrAccess(VariableScope scope, string name, DefOrAccessCand v) { - v = DenseRank2::denseRank(scope, name, result + 1) - } - - /** - * Holds if `v` can reach rank `rnk` in the variable scope `scope`. This is needed to - * take shadowing into account, for example in - * - * ```rust - * let x = 0; // rank 0 - * use(x); // rank 1 - * let x = ""; // rank 2 - * use(x); // rank 3 - * ``` - * - * the declaration at rank 0 can only reach the access at rank 1, while the declaration - * at rank 2 can only reach the access at rank 3. - */ - private predicate variableReachesRank( - VariableScope scope, string name, NestedFunctionOrVariable v, int rnk - ) { - rnk = rankVariableOrAccess(scope, name, v) - or - variableReachesRank(scope, name, v, rnk - 1) and - rnk = rankVariableOrAccess(scope, name, TDefOrAccessCandVariableAccessCand(_)) - } - - private predicate variableReachesCand( - VariableScope scope, string name, NestedFunctionOrVariable v, VariableAccessCand cand, - int nestLevel - ) { - exists(int rnk | - variableReachesRank(scope, name, v, rnk) and - rnk = rankVariableOrAccess(scope, name, TDefOrAccessCandVariableAccessCand(cand)) and - variableAccessCandInScope(cand, scope, name, nestLevel, _) - ) - } - - pragma[nomagic] - predicate access(string name, NestedFunctionOrVariable v, VariableAccessCand cand) { - v = - min(NestedFunctionOrVariable v0, int nestLevel | - variableReachesCand(_, name, v0, cand, nestLevel) - | - v0 order by nestLevel - ) - } - /** A variable access. */ - class VariableAccess extends PathExprBase { - private string name; - private Variable v; - - VariableAccess() { variableAccess(name, v, this) } + class VariableAccess extends LocalAccess { + VariableAccess() { this.getLocal() instanceof Variable } /** Gets the variable being accessed. */ - Variable getVariable() { result = v } + Variable getVariable() { result = super.getLocal() } /** Holds if this access is a capture. */ - predicate isCapture() { this.getEnclosingCfgScope() != v.getEnclosingCfgScope() } + predicate isCapture() { + this.getEnclosingCfgScope() != this.getVariable().getEnclosingCfgScope() + } } /** Holds if `e` occurs in the LHS of an assignment operation. */ @@ -682,7 +345,7 @@ module Impl { or exists(Expr mid | assignmentOperationDescendant(ao, mid) and - getImmediateParentAdj(e) = mid and + mid = e.getParentNode() and not mid instanceof DerefExpr and not mid instanceof FieldExpr and not mid instanceof IndexExpr @@ -695,7 +358,7 @@ module Impl { cached VariableWriteAccess() { - Cached::ref() and + CachedStage::ref() and assignmentOperationDescendant(ae, this) } @@ -707,7 +370,7 @@ module Impl { class VariableReadAccess extends VariableAccess { cached VariableReadAccess() { - Cached::ref() and + CachedStage::ref() and not this instanceof VariableWriteAccess and not this = any(RefExpr re).getExpr() and not this = any(CompoundAssignmentExpr cae).getLhs() @@ -715,47 +378,10 @@ module Impl { } /** A nested function access. */ - class NestedFunctionAccess extends PathExprBase { + class NestedFunctionAccess extends LocalAccess { private Function f; - NestedFunctionAccess() { nestedFunctionAccess(_, f, this) } - /** Gets the function being accessed. */ - Function getFunction() { result = f } + Function getFunction() { result = super.getLocal().getDefiningNode() } } - - cached - private module Cached { - cached - predicate ref() { 1 = 1 } - - cached - predicate backref() { - 1 = 1 - or - variableDecl(_, _, _) - or - exists(VariableReadAccess a) - or - exists(VariableWriteAccess a) - or - exists(any(Variable v).getParameter()) - } - - cached - newtype TVariable = - MkVariable(AstNode definingNode, string name) { variableDecl(definingNode, _, name) } - - cached - predicate variableAccess(string name, Variable v, VariableAccessCand cand) { - access(name, TDefOrAccessCandVariable(v), cand) - } - - cached - predicate nestedFunctionAccess(string name, Function f, VariableAccessCand cand) { - access(name, TDefOrAccessCandNestedFunction(f, _), cand) - } - } - - private import Cached } diff --git a/rust/ql/lib/qlpack.yml b/rust/ql/lib/qlpack.yml index e97302ac1f9..931c069ad24 100644 --- a/rust/ql/lib/qlpack.yml +++ b/rust/ql/lib/qlpack.yml @@ -16,6 +16,7 @@ dependencies: codeql/tutorial: ${workspace} codeql/typeinference: ${workspace} codeql/util: ${workspace} + codeql/namebinding: ${workspace} dataExtensions: - /**/*.model.yml warnOnImplicitThis: true diff --git a/rust/ql/test/library-tests/variables/Ssa.expected b/rust/ql/test/library-tests/variables/Ssa.expected index d428d49cf1d..a5583df8be4 100644 --- a/rust/ql/test/library-tests/variables/Ssa.expected +++ b/rust/ql/test/library-tests/variables/Ssa.expected @@ -442,9 +442,9 @@ read | main.rs:823:13:823:13 | x | main.rs:823:13:823:13 | x | main.rs:824:15:824:15 | x | | main.rs:823:13:823:13 | x | main.rs:823:13:823:13 | x | main.rs:829:15:829:15 | x | | main.rs:825:18:825:18 | x | main.rs:825:18:825:18 | x | main.rs:826:20:826:20 | x | +| main.rs:838:9:838:9 | x | main.rs:838:9:838:9 | x | main.rs:843:19:843:19 | x | | main.rs:838:9:838:9 | x | main.rs:838:9:838:9 | x | main.rs:845:19:845:19 | x | | main.rs:840:13:840:13 | x | main.rs:840:13:840:13 | x | main.rs:841:9:841:9 | x | -| main.rs:840:13:840:13 | x | main.rs:840:13:840:13 | x | main.rs:843:19:843:19 | x | firstRead | main.rs:5:14:5:14 | s | main.rs:5:14:5:14 | s | main.rs:7:20:7:20 | s | | main.rs:10:14:10:14 | i | main.rs:10:14:10:14 | i | main.rs:12:20:12:20 | i | @@ -628,6 +628,7 @@ firstRead | main.rs:814:13:814:16 | test | main.rs:814:13:814:16 | test | main.rs:816:9:816:12 | test | | main.rs:823:13:823:13 | x | main.rs:823:13:823:13 | x | main.rs:824:15:824:15 | x | | main.rs:825:18:825:18 | x | main.rs:825:18:825:18 | x | main.rs:826:20:826:20 | x | +| main.rs:838:9:838:9 | x | main.rs:838:9:838:9 | x | main.rs:843:19:843:19 | x | | main.rs:838:9:838:9 | x | main.rs:838:9:838:9 | x | main.rs:845:19:845:19 | x | | main.rs:840:13:840:13 | x | main.rs:840:13:840:13 | x | main.rs:841:9:841:9 | x | adjacentReads @@ -678,7 +679,6 @@ adjacentReads | main.rs:712:13:712:13 | a | main.rs:712:13:712:13 | a | main.rs:714:5:714:5 | a | main.rs:715:15:715:15 | a | | main.rs:721:9:721:9 | x | main.rs:721:9:721:9 | x | main.rs:722:20:722:20 | x | main.rs:723:15:723:15 | x | | main.rs:823:13:823:13 | x | main.rs:823:13:823:13 | x | main.rs:824:15:824:15 | x | main.rs:829:15:829:15 | x | -| main.rs:840:13:840:13 | x | main.rs:840:13:840:13 | x | main.rs:841:9:841:9 | x | main.rs:843:19:843:19 | x | phi | main.rs:245:9:245:44 | SSA phi(a3) | main.rs:245:9:245:44 | a3 | main.rs:245:22:245:23 | a3 | | main.rs:245:9:245:44 | SSA phi(a3) | main.rs:245:9:245:44 | a3 | main.rs:245:42:245:43 | a3 | diff --git a/rust/ql/test/library-tests/variables/main.rs b/rust/ql/test/library-tests/variables/main.rs index c20f1fe42c3..1435d79aaca 100644 --- a/rust/ql/test/library-tests/variables/main.rs +++ b/rust/ql/test/library-tests/variables/main.rs @@ -840,7 +840,7 @@ fn let_in_block_in_cond() { let x = 1; // x2 x > 0 // $ read_access=x2 } { - print_i64(x); // $ SPURIOUS: read_access=x2 $ MISSING: read_access=x1 + print_i64(x); // $ read_access=x1 } else { print_i64(x); // $ read_access=x1 } diff --git a/rust/ql/test/library-tests/variables/variables.expected b/rust/ql/test/library-tests/variables/variables.expected index b28d16a5c34..de94e826393 100644 --- a/rust/ql/test/library-tests/variables/variables.expected +++ b/rust/ql/test/library-tests/variables/variables.expected @@ -405,7 +405,7 @@ variableAccess | main.rs:826:20:826:20 | x | main.rs:825:18:825:18 | x | | main.rs:829:15:829:15 | x | main.rs:823:13:823:13 | x | | main.rs:841:9:841:9 | x | main.rs:840:13:840:13 | x | -| main.rs:843:19:843:19 | x | main.rs:840:13:840:13 | x | +| main.rs:843:19:843:19 | x | main.rs:838:9:838:9 | x | | main.rs:845:19:845:19 | x | main.rs:838:9:838:9 | x | variableWriteAccess | main.rs:27:5:27:6 | x2 | main.rs:25:13:25:14 | x2 | @@ -640,7 +640,7 @@ variableReadAccess | main.rs:826:20:826:20 | x | main.rs:825:18:825:18 | x | | main.rs:829:15:829:15 | x | main.rs:823:13:823:13 | x | | main.rs:841:9:841:9 | x | main.rs:840:13:840:13 | x | -| main.rs:843:19:843:19 | x | main.rs:840:13:840:13 | x | +| main.rs:843:19:843:19 | x | main.rs:838:9:838:9 | x | | main.rs:845:19:845:19 | x | main.rs:838:9:838:9 | x | variableInitializer | main.rs:20:9:20:10 | x1 | main.rs:20:14:20:16 | "a" |