mirror of
https://github.com/github/codeql.git
synced 2026-02-21 09:23:40 +01:00
227 lines
6.5 KiB
Plaintext
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 }
|
|
}
|
|
}
|