Skip to content

Commit

Permalink
Merge pull request #17696 from hvitved/rust/captured-variables
Browse files Browse the repository at this point in the history
Rust: Account for captured variables
  • Loading branch information
hvitved authored Oct 9, 2024
2 parents 8352d17 + aa7215b commit 6a87eb0
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 111 deletions.
43 changes: 32 additions & 11 deletions rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
private import rust
private import codeql.rust.elements.internal.generated.ParentChild
private import codeql.rust.elements.internal.PathExprImpl::Impl as PathExprImpl
private import codeql.util.DenseRank

module Impl {
/**
Expand Down Expand Up @@ -119,6 +120,9 @@ module Impl {
result = let.getInitializer()
)
}

/** Holds if this variable is captured. */
predicate isCaptured() { this.getAnAccess().isCapture() }
}

/** A path expression that may access a local variable. */
Expand Down Expand Up @@ -251,7 +255,7 @@ module Impl {
// Use the location of the inner scope as the location of the access, instead of the
// actual access location. This allows us to collapse multiple accesses in inner
// scopes to a single entity
scope.getLocation().hasLocationInfo(_, startline, startcolumn, endline, endcolumn)
inner.getLocation().hasLocationInfo(_, startline, startcolumn, endline, endcolumn)
)
}

Expand Down Expand Up @@ -334,18 +338,30 @@ module Impl {
}
}

private module DenseRankInput implements DenseRankInputSig3 {
class C1 = VariableScope;

class C2 = string;

class Ranked = VariableOrAccessCand;

int getRank(VariableScope scope, string name, VariableOrAccessCand v) {
v =
rank[result](VariableOrAccessCand v0, int startline, int startcolumn, int endline,
int endcolumn |
v0.rankBy(name, scope, startline, startcolumn, endline, endcolumn)
|
v0 order by startline, startcolumn, endline, endcolumn
)
}
}

/**
* 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, VariableOrAccessCand v) {
v =
rank[result + 1](VariableOrAccessCand v0, int startline, int startcolumn, int endline,
int endcolumn |
v0.rankBy(name, scope, startline, startcolumn, endline, endcolumn)
|
v0 order by startline, startcolumn, endline, endcolumn
)
result = DenseRank3<DenseRankInput>::denseRank(scope, name, v) - 1
}

/**
Expand Down Expand Up @@ -379,16 +395,21 @@ module Impl {
)
}

private import codeql.rust.controlflow.internal.Scope

/** A variable access. */
class VariableAccess extends PathExprImpl::PathExpr instanceof VariableAccessCand {
private string name;
private Variable v;

VariableAccess() { variableAccess(_, name, v, this) }
VariableAccess() { variableAccess(name, v, this) }

/** Gets the variable being accessed. */
Variable getVariable() { result = v }

/** Holds if this access is a capture. */
predicate isCapture() { scopeOfAst(this) != scopeOfAst(v.getPat()) }

override string toString() { result = name }

override string getAPrimaryQlClass() { result = "VariableAccess" }
Expand Down Expand Up @@ -426,10 +447,10 @@ module Impl {
MkVariable(AstNode definingNode, string name) { variableDecl(definingNode, _, name) }

cached
predicate variableAccess(VariableScope scope, string name, Variable v, VariableAccessCand cand) {
predicate variableAccess(string name, Variable v, VariableAccessCand cand) {
v =
min(Variable v0, int nestLevel |
variableReachesCand(scope, name, v0, cand, nestLevel)
variableReachesCand(_, name, v0, cand, nestLevel)
|
v0 order by nestLevel
)
Expand Down
34 changes: 17 additions & 17 deletions rust/ql/test/library-tests/controlflow/Cfg.expected
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,42 @@ edges
| test.rs:10:9:22:9 | ExprStmt | test.rs:11:13:11:24 | ExprStmt | |
| test.rs:10:9:22:9 | LoopExpr | test.rs:23:9:23:20 | ExprStmt | |
| test.rs:10:14:22:9 | BlockExpr | test.rs:11:13:11:24 | ExprStmt | |
| test.rs:11:13:11:13 | PathExpr | test.rs:11:17:11:20 | PathExpr | |
| test.rs:11:13:11:13 | i | test.rs:11:17:11:20 | PathExpr | |
| test.rs:11:13:11:23 | ... = ... | test.rs:12:13:14:13 | ExprStmt | |
| test.rs:11:13:11:24 | ExprStmt | test.rs:11:13:11:13 | PathExpr | |
| test.rs:11:17:11:20 | PathExpr | test.rs:11:22:11:22 | PathExpr | |
| test.rs:11:13:11:24 | ExprStmt | test.rs:11:13:11:13 | i | |
| test.rs:11:17:11:20 | PathExpr | test.rs:11:22:11:22 | i | |
| test.rs:11:17:11:23 | CallExpr | test.rs:11:13:11:23 | ... = ... | |
| test.rs:11:22:11:22 | PathExpr | test.rs:11:17:11:23 | CallExpr | |
| test.rs:12:13:14:13 | ExprStmt | test.rs:12:16:12:16 | PathExpr | |
| test.rs:11:22:11:22 | i | test.rs:11:17:11:23 | CallExpr | |
| test.rs:12:13:14:13 | ExprStmt | test.rs:12:16:12:16 | i | |
| test.rs:12:13:14:13 | IfExpr | test.rs:15:13:17:13 | ExprStmt | |
| test.rs:12:16:12:16 | PathExpr | test.rs:12:20:12:24 | 10000 | |
| test.rs:12:16:12:16 | i | test.rs:12:20:12:24 | 10000 | |
| test.rs:12:16:12:24 | ... > ... | test.rs:12:13:14:13 | IfExpr | false |
| test.rs:12:16:12:24 | ... > ... | test.rs:13:17:13:29 | ExprStmt | true |
| test.rs:12:20:12:24 | 10000 | test.rs:12:16:12:24 | ... > ... | |
| test.rs:13:17:13:28 | ReturnExpr | test.rs:8:5:24:5 | exit test_break_and_continue (normal) | return |
| test.rs:13:17:13:29 | ExprStmt | test.rs:13:24:13:28 | false | |
| test.rs:13:24:13:28 | false | test.rs:13:17:13:28 | ReturnExpr | |
| test.rs:15:13:17:13 | ExprStmt | test.rs:15:16:15:16 | PathExpr | |
| test.rs:15:13:17:13 | ExprStmt | test.rs:15:16:15:16 | i | |
| test.rs:15:13:17:13 | IfExpr | test.rs:18:13:20:13 | ExprStmt | |
| test.rs:15:16:15:16 | PathExpr | test.rs:15:21:15:21 | 1 | |
| test.rs:15:16:15:16 | i | test.rs:15:21:15:21 | 1 | |
| test.rs:15:16:15:21 | ... == ... | test.rs:15:13:17:13 | IfExpr | false |
| test.rs:15:16:15:21 | ... == ... | test.rs:16:17:16:22 | ExprStmt | true |
| test.rs:15:21:15:21 | 1 | test.rs:15:16:15:21 | ... == ... | |
| test.rs:16:17:16:21 | BreakExpr | test.rs:10:9:22:9 | LoopExpr | break |
| test.rs:16:17:16:22 | ExprStmt | test.rs:16:17:16:21 | BreakExpr | |
| test.rs:18:13:20:13 | ExprStmt | test.rs:18:16:18:16 | PathExpr | |
| test.rs:18:13:20:13 | IfExpr | test.rs:21:13:21:13 | PathExpr | |
| test.rs:18:16:18:16 | PathExpr | test.rs:18:20:18:20 | 2 | |
| test.rs:18:13:20:13 | ExprStmt | test.rs:18:16:18:16 | i | |
| test.rs:18:13:20:13 | IfExpr | test.rs:21:13:21:13 | i | |
| test.rs:18:16:18:16 | i | test.rs:18:20:18:20 | 2 | |
| test.rs:18:16:18:20 | ... % ... | test.rs:18:25:18:25 | 0 | |
| test.rs:18:16:18:25 | ... != ... | test.rs:18:13:20:13 | IfExpr | false |
| test.rs:18:16:18:25 | ... != ... | test.rs:19:17:19:25 | ExprStmt | true |
| test.rs:18:20:18:20 | 2 | test.rs:18:16:18:20 | ... % ... | |
| test.rs:18:25:18:25 | 0 | test.rs:18:16:18:25 | ... != ... | |
| test.rs:19:17:19:24 | ContinueExpr | test.rs:11:13:11:24 | ExprStmt | continue |
| test.rs:19:17:19:25 | ExprStmt | test.rs:19:17:19:24 | ContinueExpr | |
| test.rs:21:13:21:13 | PathExpr | test.rs:21:17:21:17 | PathExpr | |
| test.rs:21:13:21:13 | i | test.rs:21:17:21:17 | i | |
| test.rs:21:13:21:21 | ... = ... | test.rs:10:14:22:9 | BlockExpr | |
| test.rs:21:17:21:17 | PathExpr | test.rs:21:21:21:21 | 2 | |
| test.rs:21:17:21:17 | i | test.rs:21:21:21:21 | 2 | |
| test.rs:21:17:21:21 | ... / ... | test.rs:21:13:21:21 | ... = ... | |
| test.rs:21:21:21:21 | 2 | test.rs:21:17:21:21 | ... / ... | |
| test.rs:23:9:23:19 | ReturnExpr | test.rs:8:5:24:5 | exit test_break_and_continue (normal) | return |
Expand Down Expand Up @@ -134,9 +134,9 @@ edges
| test.rs:72:21:72:21 | 0 | test.rs:72:17:72:21 | ... > ... | |
| test.rs:73:17:73:21 | BreakExpr | test.rs:70:9:76:9 | WhileExpr | break |
| test.rs:73:17:73:22 | ExprStmt | test.rs:73:17:73:21 | BreakExpr | |
| test.rs:75:13:75:13 | PathExpr | test.rs:75:17:75:21 | false | |
| test.rs:75:13:75:13 | b | test.rs:75:17:75:21 | false | |
| test.rs:75:13:75:21 | ... = ... | test.rs:70:17:76:9 | BlockExpr | |
| test.rs:75:13:75:22 | ExprStmt | test.rs:75:13:75:13 | PathExpr | |
| test.rs:75:13:75:22 | ExprStmt | test.rs:75:13:75:13 | b | |
| test.rs:75:17:75:21 | false | test.rs:75:13:75:21 | ... = ... | |
| test.rs:79:5:86:5 | enter test_while_let | test.rs:80:9:80:29 | LetStmt | |
| test.rs:79:5:86:5 | exit test_while_let (normal) | test.rs:79:5:86:5 | exit test_while_let | |
Expand Down Expand Up @@ -230,13 +230,13 @@ edges
| test.rs:122:28:124:9 | BlockExpr | test.rs:122:9:124:9 | IfExpr | |
| test.rs:123:13:123:13 | n | test.rs:122:28:124:9 | BlockExpr | |
| test.rs:125:9:125:9 | 0 | test.rs:121:43:126:5 | BlockExpr | |
| test.rs:128:5:134:5 | enter test_nested_if | test.rs:129:16:129:16 | PathExpr | |
| test.rs:128:5:134:5 | enter test_nested_if | test.rs:129:16:129:16 | a | |
| test.rs:128:5:134:5 | exit test_nested_if (normal) | test.rs:128:5:134:5 | exit test_nested_if | |
| test.rs:128:38:134:5 | BlockExpr | test.rs:128:5:134:5 | exit test_nested_if (normal) | |
| test.rs:129:9:133:9 | IfExpr | test.rs:128:38:134:5 | BlockExpr | |
| test.rs:129:13:129:48 | [boolean(false)] IfExpr | test.rs:132:13:132:13 | 0 | false |
| test.rs:129:13:129:48 | [boolean(true)] IfExpr | test.rs:130:13:130:13 | 1 | true |
| test.rs:129:16:129:16 | PathExpr | test.rs:129:20:129:20 | 0 | |
| test.rs:129:16:129:16 | a | test.rs:129:20:129:20 | 0 | |
| test.rs:129:16:129:20 | ... < ... | test.rs:129:24:129:24 | a | true |
| test.rs:129:16:129:20 | ... < ... | test.rs:129:41:129:41 | a | false |
| test.rs:129:20:129:20 | 0 | test.rs:129:16:129:20 | ... < ... | |
Expand Down
Loading

0 comments on commit 6a87eb0

Please sign in to comment.