Files
codeql/ql/src/codeql_ruby/ast/internal/Variable.qll
Arthur Baars 22fd8908c5 Use private imports
No need to have everyting re-export the entire AST
2020-12-03 19:13:05 +01:00

227 lines
6.5 KiB
Plaintext

private import codeql_ruby.AST
private import TreeSitter
private import codeql.Locations
private import codeql_ruby.ast.internal.Pattern
private Generated::AstNode parent(Generated::AstNode n) {
result = n.getParent() and
not n = any(VariableScope s).getScopeElement()
}
/** Gets the enclosing scope for `node`. */
private VariableScope enclosingScope(Generated::AstNode node) {
result.getScopeElement() = parent*(node.getParent())
}
private predicate parameterAssignment(
CallableScope::Range scope, string name, Generated::Identifier i
) {
assignment(i, true) and
scope = enclosingScope(i) and
name = i.getValue()
}
/** Holds if `scope` defines `name` in its parameter declaration at `i`. */
private predicate scopeDefinesParameterVariable(
CallableScope::Range scope, string name, Generated::Identifier i
) {
parameterAssignment(scope, name, i) and
// In case of overlapping parameter names (e.g. `_`), only the first
// parameter will give rise to a variable
i =
min(Generated::Identifier other |
parameterAssignment(scope, name, other)
|
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
)
or
exists(Parameter p |
p = scope.getScopeElement().getAParameter() and
name = p.(NamedParameter).getName()
|
i = p.(Generated::BlockParameter).getName() or
i = p.(Generated::HashSplatParameter).getName() or
i = p.(Generated::KeywordParameter).getName() or
i = p.(Generated::OptionalParameter).getName() or
i = p.(Generated::SplatParameter).getName()
)
}
/** Holds if `name` is assigned in `scope` at `i`. */
private predicate scopeAssigns(VariableScope scope, string name, Generated::Identifier i) {
assignment(i, false) and
name = i.getValue() and
scope = enclosingScope(i)
}
/** Holds if location `one` starts strictly before location `two` */
pragma[inline]
private predicate strictlyBefore(Location one, Location two) {
one.getStartLine() < two.getStartLine()
or
one.getStartLine() = two.getStartLine() and one.getStartColumn() < two.getStartColumn()
}
/** A scope that may capture outer local variables. */
private class CapturingScope extends VariableScope {
CapturingScope() {
exists(Callable c | c = this.getScopeElement() |
c instanceof Block
or
c instanceof DoBlock
or
c instanceof Lambda // TODO: Check if this is actually the case
)
}
/** Gets the scope in which this scope is nested, if any. */
private VariableScope getOuterScope() { result = enclosingScope(this.getScopeElement()) }
/** Holds if this scope inherits `name` from an outer scope `outer`. */
predicate inherits(string name, VariableScope outer) {
not scopeDefinesParameterVariable(this, name, _) and
(
outer = this.getOuterScope() and
(
scopeDefinesParameterVariable(outer, name, _)
or
exists(Generated::Identifier i |
scopeAssigns(outer, name, i) and
strictlyBefore(i.getLocation(), this.getLocation())
)
)
or
this.getOuterScope().(CapturingScope).inherits(name, outer)
)
}
}
cached
private module Cached {
cached
newtype TScope =
TTopLevelScope(Generated::Program node) or
TModuleScope(Generated::Module node) or
TClassScope(AstNode cls) {
cls instanceof Generated::Class or cls instanceof Generated::SingletonClass
} or
TCallableScope(Callable c)
cached
newtype TVariable =
TLocalVariable(VariableScope scope, string name, Generated::Identifier i) {
scopeDefinesParameterVariable(scope, name, i)
or
scopeAssigns(scope, name, i) and
i =
min(Generated::Identifier other |
scopeAssigns(scope, name, other)
|
other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn()
) and
not scopeDefinesParameterVariable(scope, name, _) and
not scope.(CapturingScope).inherits(name, _)
}
cached
predicate access(Generated::Identifier access, Variable variable) {
exists(string name | name = access.getValue() |
variable = enclosingScope(access).getVariable(name) and
not strictlyBefore(access.getLocation(), variable.getLocation()) 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()
or
exists(VariableScope declScope |
variable = declScope.getVariable(name) and
enclosingScope(access).(CapturingScope).inherits(name, declScope)
)
)
}
}
import Cached
module VariableScope {
abstract class Range extends TScope {
abstract string toString();
abstract AstNode getScopeElement();
}
}
module TopLevelScope {
class Range extends VariableScope::Range, TTopLevelScope {
override string toString() { result = "top-level scope" }
override AstNode getScopeElement() { TTopLevelScope(result) = this }
}
}
module ModuleScope {
class Range extends VariableScope::Range, TModuleScope {
override string toString() { result = "module scope" }
override AstNode getScopeElement() { TModuleScope(result) = this }
}
}
module ClassScope {
class Range extends VariableScope::Range, TClassScope {
override string toString() { result = "class scope" }
override AstNode getScopeElement() { TClassScope(result) = this }
}
}
module CallableScope {
class Range extends VariableScope::Range, TCallableScope {
private Callable c;
Range() { this = TCallableScope(c) }
override string toString() {
(c instanceof Method or c instanceof SingletonMethod) and
result = "method scope"
or
c instanceof Lambda and
result = "lambda scope"
or
c instanceof Block and
result = "block scope"
}
override Callable getScopeElement() { TCallableScope(result) = this }
}
}
module Variable {
class Range extends TVariable {
abstract string getName();
string toString() { result = this.getName() }
abstract Location getLocation();
abstract VariableScope getDeclaringScope();
}
}
module LocalVariable {
class Range extends Variable::Range {
private VariableScope scope;
private string name;
private Generated::Identifier i;
Range() { this = TLocalVariable(scope, name, i) }
final override string getName() { result = name }
final override Location getLocation() { result = i.getLocation() }
final override VariableScope getDeclaringScope() { result = scope }
}
}