Merge PR #21873 branch to get files

This commit is contained in:
copilot-swe-agent[bot]
2026-05-29 12:14:38 +00:00
committed by GitHub
24 changed files with 4340 additions and 3796 deletions

View File

@@ -134,7 +134,7 @@ class BlockParameter extends NamedParameter, TBlockParameter {
final override string getName() { result = g.getName().getValue() }
final override LocalVariable getVariable() {
result = TLocalVariableReal(_, _, g.getName()) or
result.(LocalVariableReal).getDefiningNode() = g.getName() or
result = TLocalVariableSynth(this, 0)
}
@@ -164,7 +164,7 @@ class HashSplatParameter extends NamedParameter, THashSplatParameter {
final override string getAPrimaryQlClass() { result = "HashSplatParameter" }
final override LocalVariable getVariable() {
result = TLocalVariableReal(_, _, g.getName()) or
result.(LocalVariableReal).getDefiningNode() = g.getName() or
result = TLocalVariableSynth(this, 0)
}
@@ -212,7 +212,9 @@ class KeywordParameter extends NamedParameter, TKeywordParameter {
final override string getAPrimaryQlClass() { result = "KeywordParameter" }
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
final override LocalVariable getVariable() {
result.(LocalVariableReal).getDefiningNode() = g.getName()
}
/**
* Gets the default value, i.e. the value assigned to the parameter when one
@@ -262,7 +264,9 @@ class OptionalParameter extends NamedParameter, TOptionalParameter {
*/
final Expr getDefaultValue() { toGenerated(result) = g.getValue() }
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
final override LocalVariable getVariable() {
result.(LocalVariableReal).getDefiningNode() = g.getName()
}
final override string toString() { result = this.getName() }
@@ -293,7 +297,7 @@ class SplatParameter extends NamedParameter, TSplatParameter {
final override string getAPrimaryQlClass() { result = "SplatParameter" }
final override LocalVariable getVariable() {
result = TLocalVariableReal(_, _, g.getName()) or
result.(LocalVariableReal).getDefiningNode() = g.getName() or
result = TLocalVariableSynth(this, 0)
}

View File

@@ -207,9 +207,7 @@ private module Cached {
TLambda(Ruby::Lambda g) or
TLine(Ruby::Line g) or
TLeftAssignmentList(Ruby::LeftAssignmentList g) or
TLocalVariableAccessReal(Ruby::Identifier g, TLocalVariableReal v) {
LocalVariableAccess::range(g, v)
} or
TLocalVariableAccessReal(Ruby::Identifier g, TLocalVariableReal v) { access(g, v) } or
TLocalVariableAccessSynth(Ast::AstNode parent, int i, Ast::LocalVariable v) {
mkSynthChild(LocalVariableAccessRealKind(v), parent, i)
or

View File

@@ -33,7 +33,7 @@ class SimpleParameterRealImpl extends SimpleParameterImpl, TSimpleParameterReal
SimpleParameterRealImpl() { this = TSimpleParameterReal(g) }
override LocalVariable getVariableImpl() { result = TLocalVariableReal(_, _, g) }
override LocalVariable getVariableImpl() { result.(LocalVariableReal).getDefiningNode() = g }
override string getNameImpl() { result = g.getValue() }
}

View File

@@ -118,7 +118,7 @@ private Ruby::AstNode specialParentOf(Ruby::AstNode n) {
]
}
private Ruby::AstNode parentOf(Ruby::AstNode n) {
Ruby::AstNode parentOf(Ruby::AstNode n) {
n = getHereDocBody(result)
or
result = specialParentOf(n).getParent()
@@ -172,13 +172,15 @@ private module Cached {
}
}
bindingset[n]
pragma[inline_late]
Scope::Range scopeOf(Ruby::AstNode n) { result = Cached::scopeOfImpl(n) }
import Cached
bindingset[n]
pragma[inline_late]
Scope scopeOfInclSynth(AstNode n) { result = Cached::scopeOfInclSynthImpl(n) }
Scope::Range scopeOf(Ruby::AstNode n) { result = scopeOfImpl(n) }
bindingset[n]
pragma[inline_late]
Scope scopeOfInclSynth(AstNode n) { result = scopeOfInclSynthImpl(n) }
abstract class ScopeImpl extends AstNode, TScopeType {
final Scope getOuterScopeImpl() { result = scopeOfInclSynth(this) }

View File

@@ -299,9 +299,12 @@ private predicate hasLocation(AstNode n, Location l) {
private module ImplicitSelfSynthesis {
pragma[nomagic]
private predicate identifierMethodCallSelfSynthesis(AstNode mc, int i, Child child) {
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
mc = TIdentifierMethodCall(_) and
i = 0
exists(SelfVariableImpl self |
self.getDeclaringScopeImpl() = scopeOf(toGenerated(mc)).getEnclosingSelfScope() and
child = SynthChild(SelfKind(self)) and
mc = TIdentifierMethodCall(_) and
i = 0
)
}
private class IdentifierMethodCallSelfSynthesis extends Synthesis {
@@ -312,13 +315,14 @@ private module ImplicitSelfSynthesis {
pragma[nomagic]
private predicate regularMethodCallSelfSynthesis(TRegularMethodCall mc, int i, Child child) {
exists(Ruby::AstNode g |
exists(Ruby::AstNode g, SelfVariableImpl self |
mc = TRegularMethodCall(g) and
// If there's no explicit receiver, then the receiver is implicitly `self`.
not exists(g.(Ruby::Call).getReceiver())
) and
child = SynthChild(SelfKind(TSelfVariable(scopeOf(toGenerated(mc)).getEnclosingSelfScope()))) and
i = 0
not exists(g.(Ruby::Call).getReceiver()) and
self.getDeclaringScopeImpl() = scopeOf(toGenerated(mc)).getEnclosingSelfScope() and
child = SynthChild(SelfKind(self)) and
i = 0
)
}
private class RegularMethodCallSelfSynthesis extends Synthesis {
@@ -341,9 +345,10 @@ private module ImplicitSelfSynthesis {
*/
pragma[nomagic]
private SelfKind getSelfKind(InstanceVariableAccess var) {
exists(Ruby::AstNode owner |
exists(Ruby::AstNode owner, SelfVariableImpl self |
self.getDeclaringScopeImpl() = scopeOf(owner).getEnclosingSelfScope() and
owner = toGenerated(instanceVarAccessSynthParentStar(var)) and
result = SelfKind(TSelfVariable(scopeOf(owner).getEnclosingSelfScope()))
result = SelfKind(self)
)
}
@@ -1566,20 +1571,20 @@ private module ForLoopDesugar {
* { a: a }
* ```
*/
private module ImplicitHashValueSynthesis {
private Ruby::AstNode keyWithoutValue(AstNode parent, int i) {
module ImplicitHashValueSynthesis {
Ruby::AstNode keyWithoutValue(Ruby::AstNode parent, int i) {
exists(Ruby::KeywordPattern pair |
result = pair.getKey() and
result = toGenerated(parent.(HashPattern).getKey(i)) and
result = parent.(Ruby::HashPattern).getChild(i).(Ruby::KeywordPattern).getKey() and
not exists(pair.getValue())
)
or
exists(Ruby::Pair pair |
i = 0 and
result = pair.getKey() and
pair = toGenerated(parent) and
not exists(pair.getValue())
)
parent =
any(Ruby::Pair pair |
i = 0 and
result = pair.getKey() and
not exists(pair.getValue())
)
}
private string keyName(Ruby::AstNode key) {
@@ -1589,7 +1594,7 @@ private module ImplicitHashValueSynthesis {
private class ImplicitHashValueSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
exists(Ruby::AstNode key | key = keyWithoutValue(parent, i) |
exists(Ruby::AstNode key | key = keyWithoutValue(toGenerated(parent), i) |
exists(TVariableReal variable |
access(key, variable) and
child = SynthChild(LocalVariableAccessRealKind(variable))
@@ -1616,7 +1621,7 @@ private module ImplicitHashValueSynthesis {
}
final override predicate location(AstNode n, Location l) {
exists(AstNode p, int i | l = keyWithoutValue(p, i).getLocation() |
exists(AstNode p, int i | l = keyWithoutValue(toGenerated(p), i).getLocation() |
n = p.(HashPattern).getValue(i)
or
i = 0 and n = p.(Pair).getValue()

View File

@@ -2,6 +2,7 @@ overlay[local]
module;
private import TreeSitter
private import codeql.namebinding.LocalNameBinding
private import codeql.ruby.AST
private import codeql.ruby.CFG
private import codeql.ruby.ast.internal.AST
@@ -94,10 +95,11 @@ predicate scopeDefinesParameterVariable(
// In case of overlapping parameter names (e.g. `_`), only the first
// parameter will give rise to a variable
i =
min(Ruby::Identifier other |
parameterAssignment(scope, name, other, _)
min(Ruby::Identifier other, int startline, int startcolumn |
parameterAssignment(scope, name, other, _) and
other.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
other order by startline, startcolumn
) and
parameterAssignment(scope, name, _, pos)
or
@@ -113,7 +115,8 @@ predicate scopeDefinesParameterVariable(
)
}
pragma[nomagic]
bindingset[i]
pragma[inline_late]
private string variableNameInScope(Ruby::AstNode i, Scope::Range scope) {
scope = scopeOf(i) and
(
@@ -137,40 +140,142 @@ private predicate scopeAssigns(Scope::Range scope, string name, Ruby::AstNode i)
name = variableNameInScope(i, scope)
}
private module Input implements LocalNameBindingInputSig<Location> {
predicate cacheRevRef() { exists(TVariable v) implies any() }
class AstNode = Ruby::AstNode;
AstNode getChild(AstNode parent, int index) {
parent = parentOf(result) and
(
index = result.getParentIndex()
or
not exists(result.getParentIndex()) and
index = -1
)
}
class Conditional extends AstNode {
Conditional() { none() }
AstNode getCondition() { none() }
AstNode getThen() { none() }
AstNode getElse() { none() }
}
class SiblingShadowingDecl extends AstNode {
SiblingShadowingDecl() { none() }
AstNode getLhs() { none() }
AstNode getRhs() { none() }
AstNode getElse() { none() }
}
predicate isTopScope(AstNode scope) {
scope instanceof Scope::Range and
not (
scope instanceof Ruby::Block or
scope instanceof Ruby::DoBlock or
scope instanceof Ruby::Lambda
)
}
private Scope::Range getParentScope(Scope::Range scope) {
result = scopeOf(scope) and
not isTopScope(scope)
}
bindingset[name, scope]
pragma[inline_late]
private predicate declInScope0(AstNode definingNode, string name, AstNode scope) {
scopeDefinesParameterVariable(scope, name, definingNode, _) or
scopeAssigns(scope, name, definingNode)
}
predicate declInScope(AstNode definingNode, string name, AstNode scope) {
scopeDefinesParameterVariable(scope, name, definingNode, _)
or
/*
* Variables are not declared explicitly in Ruby, so we consider the _first_ assignment to
* be the declaration:
*
* ```rb
* a = 1 # declares `a`
* a = 2 # does not declare `a`
* 1.times do | x | # declares `x`
* a = 2 # does not declare `a`
* end
* ```
*/
scopeAssigns(scope, name, definingNode) and
not scopeDefinesParameterVariable(scope, name, _, _) and
not exists(AstNode prev, AstNode prevScope |
prevScope = getParentScope*(scope) and
declInScope0(prev, name, prevScope) and
prev.getLocation().strictlyBefore(definingNode.getLocation())
)
}
predicate implicitDeclInScope(string name, AstNode scope) {
name = "self" and
scope instanceof SelfBase::Range
}
predicate accessCand(AstNode n, string name) {
name = variableNameInScope(n, _) and
(
explicitAssignmentNode(n, _)
or
implicitAssignmentNode(n)
or
scopeDefinesParameterVariable(_, _, n, _)
or
vcall(n)
or
n = any(Ruby::VariableReferencePattern vr).getName()
or
n = ImplicitHashValueSynthesis::keyWithoutValue(_, _)
)
or
n instanceof Ruby::Self and
name = "self"
}
}
private import LocalNameBinding<Location, Input>
cached
private module Cached {
cached
newtype TVariable =
TGlobalVariable(string name) { name = any(Ruby::GlobalVariable var).getValue() } or
TGlobalVariable(string name) {
CachedStage::ref() and
name = any(Ruby::GlobalVariable var).getValue()
} or
TClassVariable(Scope::Range scope, string name, Ruby::AstNode decl) {
decl =
min(Ruby::ClassVariable other |
classVariableAccess(other, name, scope)
min(Ruby::ClassVariable other, int startline, int startcolumn |
classVariableAccess(other, name, scope) and
other.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
other order by startline, startcolumn
)
} or
TInstanceVariable(Scope::Range scope, string name, boolean instance, Ruby::AstNode decl) {
decl =
min(Ruby::InstanceVariable other |
instanceVariableAccess(other, name, scope, instance)
min(Ruby::InstanceVariable other, int startline, int startcolumn |
instanceVariableAccess(other, name, scope, instance) and
other.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
other order by startline, startcolumn
)
} or
TLocalVariableReal(Scope::Range scope, string name, Ruby::AstNode i) {
scopeDefinesParameterVariable(scope, name, i, _)
or
i =
min(Ruby::AstNode other |
scopeAssigns(scope, name, other)
|
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
) and
not scopeDefinesParameterVariable(scope, name, _, _) and
not inherits(scope, name, _)
} or
TSelfVariable(SelfBase::Range scope) or
TLocalVariableReal(Local l) or
TLocalVariableSynth(AstNode n, int i) { any(Synthesis s).localVariable(n, i) }
// Db types that can be vcalls
@@ -321,39 +426,37 @@ private module Cached {
i = any(Ruby::ExpressionReferencePattern x).getValue()
}
pragma[nomagic]
private predicate hasScopeAndName(VariableReal variable, Scope::Range scope, string name) {
variable.getNameImpl() = name and
scope = variable.getDeclaringScopeImpl()
}
cached
predicate access(Ruby::AstNode access, VariableReal variable) {
exists(string name, Scope::Range scope |
pragma[only_bind_into](name) = variableNameInScope(access, scope)
exists(Local l |
variable = TLocalVariableReal(l) and
access = l.getAnAccess()
|
hasScopeAndName(variable, scope, name) and
not access.getLocation().strictlyBefore(variable.getLocationImpl()) and
// In case of overlapping parameter names, later parameters should not
// be considered accesses to the first parameter
if parameterAssignment(_, _, access, _)
then scopeDefinesParameterVariable(_, _, access, _)
else any()
l instanceof ImplicitLocal
or
exists(Scope::Range declScope |
hasScopeAndName(variable, declScope, pragma[only_bind_into](name)) and
inherits(scope, name, declScope)
)
/*
* In the example below, `a` is declared in the scope of `M`, but only the
* second mention of `a` is an actual access:
*
* ```rb
* module M
* puts a # calls method `a`
* a = 1 # declares `a`
* puts a # accesses variable `a`
* end
* ```
*/
not access.getLocation().strictlyBefore(l.getDefiningNode().getLocation())
)
}
private class Access extends Ruby::Token {
Access() {
access(this.(Ruby::Identifier), _) or
access(this, _) or
this instanceof Ruby::GlobalVariable or
this instanceof Ruby::InstanceVariable or
this instanceof Ruby::ClassVariable or
this instanceof Ruby::Self
this instanceof Ruby::ClassVariable
}
}
@@ -398,29 +501,6 @@ private module Cached {
import Cached
/** Holds if this scope inherits `name` from an outer scope `outer`. */
private predicate inherits(Scope::Range scope, string name, Scope::Range outer) {
(
scope instanceof Ruby::Block or
scope instanceof Ruby::DoBlock or
scope instanceof Ruby::Lambda
) and
not scopeDefinesParameterVariable(scope, name, _, _) and
(
outer = scope.getOuterScope() and
(
scopeDefinesParameterVariable(outer, name, _, _)
or
exists(Ruby::AstNode i |
scopeAssigns(outer, name, i) and
i.getLocation().strictlyBefore(scope.getLocation())
)
)
or
inherits(scope.getOuterScope(), name, outer)
)
}
abstract class VariableImpl extends TVariable {
abstract string getNameImpl();
@@ -429,10 +509,9 @@ abstract class VariableImpl extends TVariable {
abstract Location getLocationImpl();
}
class TVariableReal =
TGlobalVariable or TClassVariable or TInstanceVariable or TLocalVariableReal or TSelfVariable;
class TVariableReal = TGlobalVariable or TClassVariable or TInstanceVariable or TLocalVariableReal;
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth or TSelfVariable;
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth;
/**
* A "real" (i.e. non-synthesized) variable. This class only exists to
@@ -458,19 +537,19 @@ private class VariableRealAdapter extends VariableImpl, TVariableReal instanceof
}
class LocalVariableReal extends VariableReal, TLocalVariableReal {
private Scope::Range scope;
private string name;
private Ruby::AstNode i;
private Local l;
LocalVariableReal() { this = TLocalVariableReal(scope, name, i) }
LocalVariableReal() { this = TLocalVariableReal(l) }
final override string getNameImpl() { result = name }
Ruby::AstNode getDefiningNode() { result = l.getDefiningNode() }
final override Location getLocationImpl() { result = i.getLocation() }
final override string getNameImpl() { result = l.getName() }
final override Scope::Range getDeclaringScopeImpl() { result = scope }
final override Location getLocationImpl() { result = l.getLocation() }
final VariableAccess getDefiningAccessImpl() { toGenerated(result) = i }
final override Scope::Range getDeclaringScopeImpl() { result = l.getScope() }
final VariableAccess getDefiningAccessImpl() { toGenerated(result) = l.getDefiningNode() }
}
class LocalVariableSynth extends VariableImpl, TLocalVariableSynth {
@@ -531,34 +610,16 @@ class ClassVariableImpl extends VariableReal, TClassVariable {
final override Scope::Range getDeclaringScopeImpl() { result = scope }
}
class SelfVariableImpl extends VariableReal, TSelfVariable {
private SelfBase::Range scope;
class SelfVariableImpl extends LocalVariableReal {
private ImplicitLocal l;
SelfVariableImpl() { this = TSelfVariable(scope) }
final override string getNameImpl() { result = "self" }
final override Location getLocationImpl() { result = scope.getLocation() }
final override Scope::Range getDeclaringScopeImpl() { result = scope }
SelfVariableImpl() { this = TLocalVariableReal(l) }
}
abstract class VariableAccessImpl extends Expr, TVariableAccess {
abstract VariableImpl getVariableImpl();
}
module LocalVariableAccess {
predicate range(Ruby::Identifier id, TLocalVariableReal v) {
access(id, v) and
(
explicitWriteAccess(id, _) or
implicitWriteAccess(id) or
vcall(id) or
id = any(Ruby::VariableReferencePattern vr).getName()
)
}
}
class TVariableAccessReal =
TLocalVariableAccessReal or TGlobalVariableAccess or TInstanceVariableAccess or
TClassVariableAccess;
@@ -681,7 +742,8 @@ private class SelfVariableAccessReal extends SelfVariableAccessImpl, TSelfReal {
SelfVariableAccessReal() {
exists(Ruby::Self self |
this = TSelfReal(self) and var = TSelfVariable(scopeOf(self).getEnclosingSelfScope())
this = TSelfReal(self) and
access(self, var)
)
}

View File

@@ -14,6 +14,7 @@ dependencies:
codeql/ssa: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
codeql/namebinding: ${workspace}
dataExtensions:
- codeql/ruby/frameworks/**/model.yml
- codeql/ruby/frameworks/**/*.model.yml

View File

@@ -28,6 +28,7 @@ parameterVariable
| parameters.rb:59:22:59:26 | (..., ...) | parameters.rb:59:25:59:25 | c |
| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x |
| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x |
| scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b |
| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements |

View File

@@ -47,3 +47,27 @@ module M
#{var2}
EOF
end
module ExceptionVariable
class MyException < Exception
end
x = 1
puts x
begin
raise MyException
rescue MyException => x # reuses `x` from above
puts x
end
puts x # prints `MyException`, not `1`
end
module ParameterShadowing
x = 1
xs = [1, 2, 3]
xs.each do |x|
puts x
end
puts x # prints `1`, not `3`
end

View File

@@ -86,12 +86,12 @@ definition
| parameters.rb:59:20:59:20 | a | parameters.rb:59:20:59:20 | a |
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c |
| scopes.rb:1:1:49:4 | self (scopes.rb) | scopes.rb:1:1:49:4 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:49:4 | self |
| scopes.rb:1:1:73:3 | self (scopes.rb) | scopes.rb:1:1:73:3 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:73:3 | self |
| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a |
| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a |
| scopes.rb:9:9:18:3 | <captured entry> a | scopes.rb:7:1:7:1 | a |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self |
| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b |
@@ -99,13 +99,18 @@ definition
| scopes.rb:13:11:13:11 | c | scopes.rb:13:11:13:11 | c |
| scopes.rb:13:14:13:14 | d | scopes.rb:13:14:13:14 | d |
| scopes.rb:13:19:13:32 | __synth__3 | scopes.rb:13:4:13:32 | __synth__3 |
| scopes.rb:26:1:26:12 | self (A) | scopes.rb:26:1:26:12 | self |
| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x |
| scopes.rb:28:1:30:3 | self (B) | scopes.rb:28:1:30:3 | self |
| scopes.rb:34:1:36:3 | self (C) | scopes.rb:34:1:36:3 | self |
| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self |
| scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:4 | var |
| scopes.rb:46:5:46:8 | var2 | scopes.rb:46:5:46:8 | var2 |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self |
| scopes.rb:55:3:55:3 | x | scopes.rb:55:3:55:3 | x |
| scopes.rb:60:25:60:25 | x | scopes.rb:55:3:55:3 | x |
| scopes.rb:66:1:73:3 | self (ParameterShadowing) | scopes.rb:66:1:73:3 | self |
| scopes.rb:67:3:67:3 | x | scopes.rb:67:3:67:3 | x |
| scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:4 | xs |
| scopes.rb:69:11:71:5 | <captured entry> self | scopes.rb:66:1:73:3 | self |
| scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b |
| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i |
@@ -262,20 +267,20 @@ read
| parameters.rb:59:20:59:20 | a | parameters.rb:59:20:59:20 | a | parameters.rb:60:11:60:11 | a |
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c |
| scopes.rb:1:1:49:4 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:1:1:73:3 | self (scopes.rb) | scopes.rb:1:1:73:3 | self | scopes.rb:8:1:8:6 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured entry> a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:9:9:18:3 | <captured entry> a | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
@@ -294,6 +299,18 @@ read
| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self | scopes.rb:45:5:45:7 | self |
| scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:4 | var | scopes.rb:44:5:44:7 | var |
| scopes.rb:46:5:46:8 | var2 | scopes.rb:46:5:46:8 | var2 | scopes.rb:47:5:47:8 | var2 |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:56:3:56:8 | self |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:59:5:59:21 | self |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:61:5:61:10 | self |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:63:3:63:8 | self |
| scopes.rb:55:3:55:3 | x | scopes.rb:55:3:55:3 | x | scopes.rb:56:8:56:8 | x |
| scopes.rb:60:25:60:25 | x | scopes.rb:55:3:55:3 | x | scopes.rb:61:10:61:10 | x |
| scopes.rb:60:25:60:25 | x | scopes.rb:55:3:55:3 | x | scopes.rb:63:8:63:8 | x |
| scopes.rb:66:1:73:3 | self (ParameterShadowing) | scopes.rb:66:1:73:3 | self | scopes.rb:72:3:72:8 | self |
| scopes.rb:67:3:67:3 | x | scopes.rb:67:3:67:3 | x | scopes.rb:72:8:72:8 | x |
| scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:4 | xs | scopes.rb:69:3:69:4 | xs |
| scopes.rb:69:11:71:5 | <captured entry> self | scopes.rb:66:1:73:3 | self | scopes.rb:70:5:70:10 | self |
| scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x | scopes.rb:70:10:70:10 | x |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:7:5:7:10 | self |
@@ -443,12 +460,12 @@ firstRead
| parameters.rb:59:20:59:20 | a | parameters.rb:59:20:59:20 | a | parameters.rb:60:11:60:11 | a |
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c |
| scopes.rb:1:1:49:4 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:1:1:73:3 | self (scopes.rb) | scopes.rb:1:1:73:3 | self | scopes.rb:8:1:8:6 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured entry> a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
@@ -460,6 +477,14 @@ firstRead
| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self | scopes.rb:45:5:45:7 | self |
| scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:4 | var | scopes.rb:44:5:44:7 | var |
| scopes.rb:46:5:46:8 | var2 | scopes.rb:46:5:46:8 | var2 | scopes.rb:47:5:47:8 | var2 |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:56:3:56:8 | self |
| scopes.rb:55:3:55:3 | x | scopes.rb:55:3:55:3 | x | scopes.rb:56:8:56:8 | x |
| scopes.rb:60:25:60:25 | x | scopes.rb:55:3:55:3 | x | scopes.rb:61:10:61:10 | x |
| scopes.rb:66:1:73:3 | self (ParameterShadowing) | scopes.rb:66:1:73:3 | self | scopes.rb:72:3:72:8 | self |
| scopes.rb:67:3:67:3 | x | scopes.rb:67:3:67:3 | x | scopes.rb:72:8:72:8 | x |
| scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:4 | xs | scopes.rb:69:3:69:4 | xs |
| scopes.rb:69:11:71:5 | <captured entry> self | scopes.rb:66:1:73:3 | self | scopes.rb:70:5:70:10 | self |
| scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x | scopes.rb:70:10:70:10 | x |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:5:6:5:6 | b |
| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:3:8:3:8 | i |
@@ -532,14 +557,14 @@ adjacentReads
| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:26:3:26:11 | self | parameters.rb:27:3:27:11 | self |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | parameters.rb:26:8:26:11 | name |
| parameters.rb:54:9:57:3 | <captured entry> self | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self | parameters.rb:56:4:56:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:3:4:3:9 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:3:9:3:9 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:10:4:10:9 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:12:4:12:9 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:14:4:14:9 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:15:4:15:9 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured entry> self | scopes.rb:1:1:73:3 | self | scopes.rb:16:4:16:9 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:13:10:13:15 | __synth__2__1 | scopes.rb:13:10:13:15 | __synth__2__1 | scopes.rb:13:11:13:11 | __synth__2__1 | scopes.rb:13:14:13:14 | __synth__2__1 |
| scopes.rb:13:19:13:32 | __synth__3 | scopes.rb:13:4:13:32 | __synth__3 | scopes.rb:13:4:13:4 | __synth__3 | scopes.rb:13:7:13:7 | __synth__3 |
| scopes.rb:13:19:13:32 | __synth__3 | scopes.rb:13:4:13:32 | __synth__3 | scopes.rb:13:7:13:7 | __synth__3 | scopes.rb:13:10:13:15 | __synth__3 |
@@ -547,6 +572,10 @@ adjacentReads
| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:31:10:31:10 | x | scopes.rb:34:7:34:7 | x |
| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:34:7:34:7 | x | scopes.rb:34:14:34:14 | x |
| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:34:14:34:14 | x | scopes.rb:37:5:37:5 | x |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:56:3:56:8 | self | scopes.rb:59:5:59:21 | self |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:59:5:59:21 | self | scopes.rb:61:5:61:10 | self |
| scopes.rb:51:1:64:3 | self (ExceptionVariable) | scopes.rb:51:1:64:3 | self | scopes.rb:61:5:61:10 | self | scopes.rb:63:3:63:8 | self |
| scopes.rb:60:25:60:25 | x | scopes.rb:55:3:55:3 | x | scopes.rb:61:10:61:10 | x | scopes.rb:63:8:63:8 | x |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:3:3:3:8 | self | ssa.rb:4:3:4:12 | self |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | ssa.rb:7:5:7:10 | self |
| ssa.rb:1:1:16:3 | self (m) | ssa.rb:1:1:16:3 | self | ssa.rb:4:3:4:12 | self | ssa.rb:11:5:11:10 | self |

View File

@@ -155,43 +155,43 @@ variableAccess
| parameters.rb:60:16:60:16 | b | parameters.rb:59:23:59:23 | b | parameters.rb:59:1:61:3 | tuples_nested |
| parameters.rb:60:21:60:21 | c | parameters.rb:59:25:59:25 | c | parameters.rb:59:1:61:3 | tuples_nested |
| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | scopes.rb:2:9:6:3 | do ... end |
| scopes.rb:3:4:3:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:3:9:3:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:3:4:3:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:3:9:3:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end |
| scopes.rb:5:4:5:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:5:4:5:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:5:9:5:9 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end |
| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:8:1:8:6 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:8:6:8:6 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:8:1:8:6 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:8:6:8:6 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:10:4:10:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:10:9:10:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:12:4:12:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:12:9:12:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:10:4:10:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:10:9:10:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:12:4:12:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:12:9:12:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:13:11:13:11 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:13:14:13:14 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:14:4:14:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:14:9:14:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:15:4:15:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:14:4:14:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:14:9:14:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:15:4:15:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:15:9:15:9 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:16:4:16:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:16:4:16:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:16:9:16:9 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:17:4:17:9 | self | scopes.rb:1:1:49:4 | self | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:17:4:17:9 | self | scopes.rb:1:1:73:3 | self | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:17:9:17:9 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:28:8:28:8 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:28:8:28:8 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:29:3:29:3 | x | scopes.rb:29:3:29:3 | x | scopes.rb:28:1:30:3 | B |
| scopes.rb:31:10:31:10 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:31:10:31:10 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:32:3:32:3 | x | scopes.rb:32:3:32:3 | x | scopes.rb:31:1:33:3 | class << ... |
| scopes.rb:34:7:34:7 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:34:14:34:14 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:34:7:34:7 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:34:14:34:14 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:35:3:35:3 | x | scopes.rb:35:3:35:3 | x | scopes.rb:34:1:36:3 | C |
| scopes.rb:37:5:37:5 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:37:5:37:5 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:38:3:38:3 | x | scopes.rb:38:3:38:3 | x | scopes.rb:37:1:39:3 | foo |
| scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:4 | var | scopes.rb:41:1:49:3 | M |
| scopes.rb:43:2:43:4 | foo | scopes.rb:43:2:43:4 | foo | scopes.rb:41:1:49:3 | M |
@@ -199,6 +199,23 @@ variableAccess
| scopes.rb:45:5:45:7 | self | scopes.rb:41:1:49:3 | self | scopes.rb:41:1:49:3 | M |
| scopes.rb:46:5:46:8 | var2 | scopes.rb:46:5:46:8 | var2 | scopes.rb:41:1:49:3 | M |
| scopes.rb:47:5:47:8 | var2 | scopes.rb:46:5:46:8 | var2 | scopes.rb:41:1:49:3 | M |
| scopes.rb:55:3:55:3 | x | scopes.rb:55:3:55:3 | x | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:56:3:56:8 | self | scopes.rb:51:1:64:3 | self | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:56:8:56:8 | x | scopes.rb:55:3:55:3 | x | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:59:5:59:21 | self | scopes.rb:51:1:64:3 | self | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:60:25:60:25 | x | scopes.rb:55:3:55:3 | x | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:61:5:61:10 | self | scopes.rb:51:1:64:3 | self | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:61:10:61:10 | x | scopes.rb:55:3:55:3 | x | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:63:3:63:8 | self | scopes.rb:51:1:64:3 | self | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:63:8:63:8 | x | scopes.rb:55:3:55:3 | x | scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:67:3:67:3 | x | scopes.rb:67:3:67:3 | x | scopes.rb:66:1:73:3 | ParameterShadowing |
| scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:4 | xs | scopes.rb:66:1:73:3 | ParameterShadowing |
| scopes.rb:69:3:69:4 | xs | scopes.rb:68:3:68:4 | xs | scopes.rb:66:1:73:3 | ParameterShadowing |
| scopes.rb:69:15:69:15 | x | scopes.rb:69:15:69:15 | x | scopes.rb:69:11:71:5 | do ... end |
| scopes.rb:70:5:70:10 | self | scopes.rb:66:1:73:3 | self | scopes.rb:66:1:73:3 | ParameterShadowing |
| scopes.rb:70:10:70:10 | x | scopes.rb:69:15:69:15 | x | scopes.rb:69:11:71:5 | do ... end |
| scopes.rb:72:3:72:8 | self | scopes.rb:66:1:73:3 | self | scopes.rb:66:1:73:3 | ParameterShadowing |
| scopes.rb:72:8:72:8 | x | scopes.rb:67:3:67:3 | x | scopes.rb:66:1:73:3 | ParameterShadowing |
| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | m |
| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m |
| ssa.rb:3:3:3:8 | self | ssa.rb:1:1:16:3 | self | ssa.rb:1:1:16:3 | m |
@@ -350,6 +367,9 @@ explicitWrite
| scopes.rb:42:2:42:4 | var | scopes.rb:42:2:42:9 | ... = ... |
| scopes.rb:43:2:43:4 | foo | scopes.rb:43:2:43:13 | ... = ... |
| scopes.rb:46:5:46:8 | var2 | scopes.rb:46:5:46:13 | ... = ... |
| scopes.rb:55:3:55:3 | x | scopes.rb:55:3:55:7 | ... = ... |
| scopes.rb:67:3:67:3 | x | scopes.rb:67:3:67:7 | ... = ... |
| scopes.rb:68:3:68:4 | xs | scopes.rb:68:3:68:16 | ... = ... |
| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:7 | ... = ... |
| ssa.rb:6:5:6:5 | i | ssa.rb:6:5:6:9 | ... = ... |
| ssa.rb:10:5:10:5 | i | ssa.rb:10:5:10:9 | ... = ... |
@@ -400,6 +420,8 @@ implicitWrite
| parameters.rb:59:25:59:25 | c |
| scopes.rb:2:14:2:14 | x |
| scopes.rb:9:14:9:14 | x |
| scopes.rb:60:25:60:25 | x |
| scopes.rb:69:15:69:15 | x |
| ssa.rb:1:7:1:7 | b |
| ssa.rb:18:8:18:8 | x |
| ssa.rb:25:8:25:15 | elements |
@@ -550,6 +572,18 @@ readAccess
| scopes.rb:44:5:44:7 | var |
| scopes.rb:45:5:45:7 | self |
| scopes.rb:47:5:47:8 | var2 |
| scopes.rb:56:3:56:8 | self |
| scopes.rb:56:8:56:8 | x |
| scopes.rb:59:5:59:21 | self |
| scopes.rb:61:5:61:10 | self |
| scopes.rb:61:10:61:10 | x |
| scopes.rb:63:3:63:8 | self |
| scopes.rb:63:8:63:8 | x |
| scopes.rb:69:3:69:4 | xs |
| scopes.rb:70:5:70:10 | self |
| scopes.rb:70:10:70:10 | x |
| scopes.rb:72:3:72:8 | self |
| scopes.rb:72:8:72:8 | x |
| ssa.rb:3:3:3:8 | self |
| ssa.rb:3:8:3:8 | i |
| ssa.rb:4:3:4:12 | self |
@@ -647,6 +681,7 @@ captureAccess
| scopes.rb:15:4:15:9 | self |
| scopes.rb:16:4:16:9 | self |
| scopes.rb:17:4:17:9 | self |
| scopes.rb:70:5:70:10 | self |
| ssa.rb:26:7:26:10 | elem |
| ssa.rb:27:5:27:13 | self |
| ssa.rb:27:10:27:13 | elem |

View File

@@ -94,7 +94,7 @@
| parameters.rb:59:23:59:23 | b |
| parameters.rb:59:25:59:25 | c |
| scopes.rb:1:1:1:15 | self |
| scopes.rb:1:1:49:4 | self |
| scopes.rb:1:1:73:3 | self |
| scopes.rb:2:14:2:14 | x |
| scopes.rb:4:4:4:4 | a |
| scopes.rb:7:1:7:1 | a |
@@ -124,6 +124,13 @@
| scopes.rb:42:2:42:4 | var |
| scopes.rb:43:2:43:4 | foo |
| scopes.rb:46:5:46:8 | var2 |
| scopes.rb:51:1:64:3 | self |
| scopes.rb:52:3:53:5 | self |
| scopes.rb:55:3:55:3 | x |
| scopes.rb:66:1:73:3 | self |
| scopes.rb:67:3:67:3 | x |
| scopes.rb:68:3:68:4 | xs |
| scopes.rb:69:15:69:15 | x |
| ssa.rb:1:1:16:3 | self |
| ssa.rb:1:1:103:3 | self |
| ssa.rb:1:7:1:7 | b |

View File

@@ -47,7 +47,7 @@
| parameters.rb:54:9:57:3 | do ... end |
| parameters.rb:59:1:61:3 | tuples_nested |
| scopes.rb:1:1:1:15 | a |
| scopes.rb:1:1:49:4 | scopes.rb |
| scopes.rb:1:1:73:3 | scopes.rb |
| scopes.rb:2:9:6:3 | do ... end |
| scopes.rb:9:9:18:3 | do ... end |
| scopes.rb:26:1:26:12 | A |
@@ -56,6 +56,10 @@
| scopes.rb:34:1:36:3 | C |
| scopes.rb:37:1:39:3 | foo |
| scopes.rb:41:1:49:3 | M |
| scopes.rb:51:1:64:3 | ExceptionVariable |
| scopes.rb:52:3:53:5 | MyException |
| scopes.rb:66:1:73:3 | ParameterShadowing |
| scopes.rb:69:11:71:5 | do ... end |
| ssa.rb:1:1:16:3 | m |
| ssa.rb:1:1:103:3 | ssa.rb |
| ssa.rb:18:1:23:3 | m1 |

View File

@@ -332,7 +332,6 @@ lib/codeql/rust/elements/internal/NeverTypeReprConstructor.qll 2e0a9c75e389e9ef4
lib/codeql/rust/elements/internal/OffsetOfExprConstructor.qll 616e146562adb3ac0fba4d6f55dd6ce60518ed377c0856f1f09ba49593e7bfab 80518ce90fc6d08011d6f5fc2a543958067739e1b0a6a5f2ed90fc9b1db078f0
lib/codeql/rust/elements/internal/OffsetOfExprImpl.qll e52d4596068cc54719438121f7d5afcaab04e0c70168ac5e4df1a3a0969817a6 6ab37e659d79e02fb2685d6802ae124157bf14b6f790b31688f437c87f40f52c
lib/codeql/rust/elements/internal/OrPatConstructor.qll 4ef583e07298487c0c4c6d7c76ffcc04b1e5fe58aba0c1da3e2c8446a9e0c92b 980a6bd176ae5e5b11c134569910c5468ba91f480982d846e222d031a6a05f1a
lib/codeql/rust/elements/internal/ParamBaseImpl.qll fe11999c728c443c46c992e9bed7a2b3e23afa16ae99592e70054bc57ae371b8 df86fdb23266bdfb9ed8a8f02558a760b67f173943b9d075b081229eb5844f66
lib/codeql/rust/elements/internal/ParamConstructor.qll b98a2d8969f289fdcc8c0fb11cbd19a3b0c71be038c4a74f5988295a2bae52f0 77d81b31064167945b79b19d9697b57ca24462c3a7cc19e462c4693ce87db532
lib/codeql/rust/elements/internal/ParamListConstructor.qll 3123142ab3cab46fb53d7f3eff6ba2d3ff7a45b78839a53dc1979a9c6a54920e 165f3d777ea257cfcf142cc4ba9a0ebcd1902eb99842b8a6657c87087f3df6fe
lib/codeql/rust/elements/internal/ParenExprConstructor.qll 104b67dc3fd53ab52e2a42ffde37f3a3a50647aa7bf35df9ba9528e9670da210 d1f5937756e87a477710c61698d141cdad0ccce8b07ecb51bab00330a1ca9835

1
rust/ql/.gitattributes generated vendored
View File

@@ -334,7 +334,6 @@
/lib/codeql/rust/elements/internal/OffsetOfExprConstructor.qll linguist-generated
/lib/codeql/rust/elements/internal/OffsetOfExprImpl.qll linguist-generated
/lib/codeql/rust/elements/internal/OrPatConstructor.qll linguist-generated
/lib/codeql/rust/elements/internal/ParamBaseImpl.qll linguist-generated
/lib/codeql/rust/elements/internal/ParamConstructor.qll linguist-generated
/lib/codeql/rust/elements/internal/ParamListConstructor.qll linguist-generated
/lib/codeql/rust/elements/internal/ParenExprConstructor.qll linguist-generated

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,14 +5,19 @@
*/
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
* be referenced directly.
*/
module Impl {
// the following QLdoc is generated: if you need to edit it, do it in the schema file
/**
* 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -110,11 +110,46 @@ fn let_pattern4() {
}
fn let_pattern5() {
let s1 = Some(String::from("Hello!")); // s1
let s = Some(String::from("Hello!")); // s1
while let Some(ref s2) // s2
= s1 { // $ read_access=s1
print_str(s2); // $ read_access=s2
while let Some(ref s) // s2
= s { // $ read_access=s1
print_str(s); // $ read_access=s2
}
}
#[rustfmt::skip]
fn let_pattern6() {
if let Some(x) = Some(43) // x1
&& let Ok(x) = // x2
Ok::<_, ()>(x) // $ read_access=x1
{
print_i64(x); // $ read_access=x2
}
}
#[rustfmt::skip]
fn let_pattern7() {
let x = 1; // x1
if let x = // x2
x + 1 // $ read_access=x1
&& let x = // x3
x + 1 // $ read_access=x2
&& let x = // x4
x + 1 // $ read_access=x3
&& let x = // x5
x + 1 // $ read_access=x4
&& let x = // x6
x + 1 // $ read_access=x5
&& let x = // x7
x + 1 // $ read_access=x6
&& let x = // x8
x + 1 // $ read_access=x7
{
print_i64(x); // $ read_access=x8
}
else {
print_i64(x); // $ read_access=x1
}
}
@@ -373,7 +408,8 @@ fn match_pattern16() {
let x = Some(32);
match x { // $ read_access=x
Some(y) // y1
if let Some(y) = // y2
if y > 0 && // $ read_access=y1
let Some(y) = // y2
Some(y) // $ read_access=y1
=> print_i64(y), // $ read_access=y2
_ => {},
@@ -798,6 +834,18 @@ mod patterns {
}
}
fn let_in_block_in_cond() {
let x = 1; // x1
if {
let x = 1; // x2
x > 0 // $ read_access=x2
} {
print_i64(x); // $ read_access=x1
} else {
print_i64(x); // $ read_access=x1
}
}
fn main() {
immutable_variable();
mutable_variable();
@@ -808,6 +856,8 @@ fn main() {
let_pattern2();
let_pattern3();
let_pattern4();
let_pattern5();
let_pattern6();
match_pattern1();
match_pattern2();
match_pattern3();
@@ -842,4 +892,5 @@ fn main() {
ref_methodcall_receiver();
macro_invocation();
capture_phi();
let_in_block_in_cond();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,450 @@
/**
* Provides a library for resolving local names based on syntactic scopes, including
* handling of shadowing sibling declarations.
*/
overlay[local?]
module;
private import codeql.util.DenseRank
private import codeql.util.Location
/** Provides the input to `LocalNameBinding`. */
signature module LocalNameBindingInputSig<LocationSig Location> {
/**
* Reverse references to the cached predicates that reference
* `CachedStage::ref()`.
*/
default predicate cacheRevRef() { none() }
/** An AST node. */
class AstNode {
/** Gets a textual representation of this element. */
string toString();
/** Gets the location of this element. */
Location getLocation();
}
/**
* Gets the child of AST node `n` at the specified index.
*
* The order of the children is only relevant for determining nearest preceding
* shadowing sibling declarations.
*/
AstNode getChild(AstNode n, int index);
/**
* A conditional where any local declarations in the condition are in scope
* in the then-branch but not the else-branch.
*
* Example:
*
* ```rust
* if let Some(x) = opt {
* // x is in scope here
* } else {
* // x is not in scope here
* }
* ```
*/
class Conditional extends AstNode {
/** Gets the condition of this conditional. */
AstNode getCondition();
/** Gets the then-branch of this conditional. */
AstNode getThen();
/** Gets the else-branch of this conditional. */
AstNode getElse();
}
/**
* A declaration where all local declarations in the left-hand side are in
* scope _after_ the declaration, and where any sibling declarations with
* the same name and syntactic scope preceding it are shadowed.
*
* Example:
*
* ```rust
* fn f() {
* let x = 1;
* // this declaration of `x` shadows the previous one (in the syntactic scope
* // being the body of `f`), but the `x` in the right-hand side still refers
* // to the first declaration
* let x = x + 1;
* // this access of `x` refers to the second declaration
* println!("{}", x);
* }
* ```
*/
class SiblingShadowingDecl extends AstNode {
/** Gets the left-hand side of this declaration. */
AstNode getLhs();
/**
* Gets the right-hand side of this declaration.
*
* Any local declared in the left-hand side of this declaration is _not_ in scope
* in the right-hand side.
*/
AstNode getRhs();
/**
* Gets the else-branch of this declaration, if any.
*
* Any local declared in the left-hand side of this declaration is _not_ in scope
* in the else-branch.
*/
AstNode getElse();
}
/**
* Holds if a local declaration named `name` exists at `definingNode` inside
* the syntactic scope `scope`.
*
* Note that declarations with a `definingNode` in the left-hand side of a
* shadowing sibling declaration `decl` should use `scope = decl`.
*/
predicate declInScope(AstNode definingNode, string name, AstNode scope);
/**
* Holds if a local declaration named `name` is implicitly in scope in the given `scope`.
*/
default predicate implicitDeclInScope(string name, AstNode scope) { none() }
/**
* Holds if `scope` is a top scope, meaning that names may not be looked up
* in ancestor scopes.
*/
default predicate isTopScope(AstNode scope) { none() }
/**
* Holds if `n` is a node that may access a local named `name`.
*/
predicate accessCand(AstNode n, string name);
/**
* Holds if the access candidate `n` should begin its lookup in `scope` instead
* of its immediately enclosing scope.
*
* For example, the `this` variable in an instance field initializer might need
* to be resolved relative to a constructor body.
*
* If `scope` declares a local with the name of `n`, then `scope` is guaranteed
* to be the scope that `n` ultimately resolves to. This can thus be used to take
* full control of scope resolution for for specific types of references.
*/
default predicate lookupStartsAt(AstNode n, AstNode scope) { none() }
}
/**
* Provides logic for resolving local names based on syntactic scopes, including
* handling of shadowing sibling declarations.
*/
module LocalNameBinding<LocationSig Location, LocalNameBindingInputSig<Location> Input> {
private import Input
final private class AstNodeFinal = AstNode;
private class Scope extends AstNodeFinal {
Scope() {
declInScope(_, _, this)
or
implicitDeclInScope(_, this)
or
isTopScope(this)
or
lookupStartsAt(_, this)
}
}
pragma[nomagic]
private predicate conditionHasChildAt(Conditional conditional, AstNode condition, int index) {
condition = conditional.getCondition() and
(
exists(getChild(condition, index))
or
// safeguard against empty conditions
not exists(getChild(condition, _)) and index = 0
)
}
/**
* An adjusted version of `getChild` from the `Input` module where in conditionals like
* `if cond body`, instead of letting `body` be a child of `if`, we make it the last
* child of `cond`. This ensures that shadowing sibling declarations inside `cond` are
* properly handled inside `body`.
*
* Example:
*
* ```rust
* if let Some(x) = opt && let x = x + 1 {
* // the second declaration of `x` is in scope here
* }
* ```
*
* We also move any `else` branch _before_ the condition to ensure that shadowing sibling
* declarations inside the condition are not in scope.
*/
private AstNode getChildAdj(AstNode parent, int index) {
result = getChild(parent, index) and
not exists(Conditional cond | result = [cond.getElse(), cond.getThen()])
or
exists(Conditional cond |
parent = cond and
result = cond.getElse() and
index = -1
or
exists(int last |
result = cond.getThen() and
last = max(int i | conditionHasChildAt(cond, parent, i)) and
index = last + 1
)
)
}
private module DenseRankInput implements DenseRankInputSig1 {
class C = AstNode;
class Ranked = AstNode;
int getRank(C parent, Ranked child) {
child = getChildAdj(parent, result) and
getChildAdj(parent, _) instanceof SiblingShadowingDecl
}
}
private predicate getRankedChild = DenseRank1<DenseRankInput>::denseRank/2;
/**
* Holds if `n` is the `i`th child of `parent`, but should instead be considered
* a child of a shadowing sibling declaration `decl` when resolving accesses.
*
* This is the case when `decl` is the nearest shadowing sibling declaration
* preceding `n` amongst all the children of `parent`.
*
* Note that `decl` may itself also have to be nested under another shadowing
* sibling declaration.
*/
private predicate shouldBeShadowingDeclChild(
AstNode parent, SiblingShadowingDecl decl, int i, AstNode n
) {
n = getRankedChild(parent, i) and
(
decl = getRankedChild(parent, i - 1)
or
shouldBeShadowingDeclChild(parent, decl, i - 1,
any(AstNode prev | not prev instanceof SiblingShadowingDecl))
)
}
/**
* Gets the AST parent of `n` with respect to determining enclosing scopes.
*
* For example, in
*
* ```rust
* let x = 1;
* let x = x + 1;
* println!("{}", x);
* ```
*
* we will have (eliding leaf nodes)
*
* ```text
* let x = 1;
* / \
* x + 1 let x = x + 1
* |
* println!("{}", x);
* ```
*
* and in
*
* ```rust
* if let Some(x) = opt && let x = x + 1 {
* println!("{}", x);
* }
* ```
*
* we will have (again eliding leaf nodes)
*
* ```text
* if ...
* |
* ... && ...
* / \
* let Some(x) = opt opt
* / \
* let x = x + 1 x + 1
* |
* println!("{}", x);
* ```
*/
private AstNode getParentForScoping(AstNode n) {
not shouldBeShadowingDeclChild(_, _, _, n) and
not exists(SiblingShadowingDecl decl | n = [decl.getRhs(), decl.getElse()]) and
n = getChildAdj(result, _)
or
shouldBeShadowingDeclChild(_, result, _, n)
or
exists(SiblingShadowingDecl decl |
result = getParentForScoping(decl) and
n = [decl.getRhs(), decl.getElse()]
)
}
/** Gets the immediately enclosing variable scope of `n`. */
private Scope getEnclosingScope(AstNode n) {
result = getParentForScoping(n)
or
exists(AstNode mid |
result = getEnclosingScope(mid) and
mid = getParentForScoping(n) and
not mid instanceof Scope
)
}
private predicate accessCandInLookupScope(AstNode n, string name, Scope lookup) {
accessCand(n, name) and
(
lookupStartsAt(n, lookup)
or
not lookupStartsAt(n, _) and
lookup = getEnclosingScope(n)
)
}
pragma[nomagic]
private predicate lookupInScope(string name, Scope lookup, Scope scope) {
accessCandInLookupScope(_, name, lookup) and
scope = lookup
or
exists(Scope mid |
lookupInScope(name, lookup, mid) and
not declInScope(_, name, mid) and
not implicitDeclInScope(name, mid) and
not isTopScope(mid) and
scope = getEnclosingScope(mid)
)
}
cached
private newtype TLocal =
TExplicitLocal(AstNode definingNode, string name, AstNode scope) {
CachedStage::ref() and
declInScope(definingNode, name, scope)
} or
TImplicitLocal(string name, AstNode scope) { implicitDeclInScope(name, scope) }
/** A locally declared entity, for example a variable or a parameter. */
abstract private class LocalImpl extends TLocal {
/** Gets the AST node that defines this local entity, if any. */
abstract AstNode getDefiningNode();
/** Gets the AST node that defines the scope of this local entity. */
abstract AstNode getScope();
/** Gets the name of this local entity. */
abstract string getName();
/** Gets the location of this local entity. */
abstract Location getLocation();
/** Gets an access to this local entity. */
LocalAccess getAnAccess() { result.getLocal() = this }
/** Gets a textual representation of this local entity. */
string toString() { result = this.getName() }
}
final class Local = LocalImpl;
/** An explicitly locally declared entity, for example a variable or a parameter. */
class ExplicitLocal extends LocalImpl, TExplicitLocal {
private AstNode definingNode;
private string name;
private AstNode scope;
ExplicitLocal() { this = TExplicitLocal(definingNode, name, scope) }
override AstNode getDefiningNode() { result = definingNode }
override AstNode getScope() { result = scope }
override string getName() { result = name }
override Location getLocation() { result = definingNode.getLocation() }
}
/** An implicitly locally declared entity, for example a `self` parameter. */
class ImplicitLocal extends LocalImpl, TImplicitLocal {
private string name;
private AstNode scope;
ImplicitLocal() { this = TImplicitLocal(name, scope) }
override AstNode getDefiningNode() { none() }
override AstNode getScope() { result = scope }
override string getName() { result = name }
override Location getLocation() { result = scope.getLocation() }
}
pragma[nomagic]
private predicate resolveInScope(string name, Scope lookup, Local l) {
exists(Scope scope | lookupInScope(name, lookup, scope) |
l = TExplicitLocal(_, name, scope) or
l = TImplicitLocal(name, scope)
)
}
cached
private predicate access(AstNode access, Local l) {
CachedStage::ref() and
exists(Scope lookup, string name |
accessCandInLookupScope(access, name, lookup) and
resolveInScope(name, lookup, l)
)
}
/** A local access. */
final class LocalAccess extends AstNodeFinal {
private Local l;
LocalAccess() { access(this, l) }
/** Gets the local entity being accessed. */
Local getLocal() { result = l }
}
/**
* The cached stage of this module.
*
* Should not be exposed.
*/
cached
module CachedStage {
/** Reference to the cached stage of this module. */
cached
predicate ref() { any() }
/**
* DO NOT USE!
*
* Reverse references to the cached predicates that reference `ref()`.
*/
cached
predicate revRef() {
any()
or
cacheRevRef()
or
(exists(Local l) implies any())
or
(exists(LocalAccess a) implies any())
}
}
}

View File

@@ -0,0 +1,7 @@
name: codeql/namebinding
version: 0.0.1-dev
groups: shared
library: true
dependencies:
codeql/util: ${workspace}
warnOnImplicitThis: true