Rust: Adopt shared local name resolution library

This commit is contained in:
Tom Hvitved
2026-05-27 09:09:19 +02:00
committed by yoff
parent 9fd266e576
commit 15cd643f8a
6 changed files with 223 additions and 593 deletions

View File

@@ -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() }
}
}

View File

@@ -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<Location> {
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<Location, Input>
/** 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<DenseRankInput>::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
}

View File

@@ -16,6 +16,7 @@ dependencies:
codeql/tutorial: ${workspace}
codeql/typeinference: ${workspace}
codeql/util: ${workspace}
codeql/namebinding: ${workspace}
dataExtensions:
- /**/*.model.yml
warnOnImplicitThis: true

View File

@@ -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 |

View File

@@ -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
}

View File

@@ -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" |