Files
codeql/ql/src/codeql_ruby/ast/Variable.qll
2021-01-29 17:45:48 +01:00

231 lines
7.1 KiB
Plaintext

/** Provides classes for modeling program variables. */
private import codeql_ruby.AST
private import codeql.Locations
private import internal.TreeSitter
private import internal.Variable
/** A scope in which variables can be declared. */
class VariableScope extends TScope {
VariableScope::Range range;
VariableScope() { range = this }
/** Gets a textual representation of this element. */
final string toString() { result = range.toString() }
/** Gets the program element this scope is associated with, if any. */
final AstNode getScopeElement() { result = range.getScopeElement() }
/** Gets the location of the program element this scope is associated with. */
final Location getLocation() { result = getScopeElement().getLocation() }
/** Gets a variable that is declared in this scope. */
final Variable getAVariable() { result.getDeclaringScope() = this }
/** Gets the variable with the given name that is declared in this scope. */
final Variable getVariable(string name) {
result = this.getAVariable() and
result.getName() = name
}
/** Gets the scope in which this scope is nested, if any. */
VariableScope getOuterScope() { result = enclosingScope(this.getScopeElement()) }
}
/** A variable declared in a scope. */
class Variable extends TVariable {
Variable::Range range;
Variable() { range = this }
/** Gets the name of this variable. */
final string getName() { result = range.getName() }
/** Gets a textual representation of this variable. */
final string toString() { result = this.getName() }
/** Gets the location of this variable. */
final Location getLocation() { result = range.getLocation() }
/** Gets the scope this variable is declared in. */
final VariableScope getDeclaringScope() { result = range.getDeclaringScope() }
/** Gets an access to this variable. */
VariableAccess getAnAccess() { result.getVariable() = this }
}
/** A local variable. */
class LocalVariable extends Variable, TLocalVariable {
override LocalVariable::Range range;
final override LocalVariableAccess getAnAccess() { result.getVariable() = this }
/** Gets the access where this local variable is first introduced. */
VariableAccess getDefiningAccess() { result = range.getDefiningAccess() }
/**
* Holds if this variable is captured. For example in
*
* ```rb
* def m x
* x.times do |y|
* puts x
* end
* puts x
* end
* ```
*
* `x` is a captured variable, whereas `y` is not.
*/
predicate isCaptured() { this.getAnAccess().isCapturedAccess() }
}
/** A global variable. */
class GlobalVariable extends Variable, TGlobalVariable {
override GlobalVariable::Range range;
final override GlobalVariableAccess getAnAccess() { result.getVariable() = this }
}
/** An instance variable. */
class InstanceVariable extends Variable, TInstanceVariable {
override InstanceVariable::Range range;
/** Holds is this variable is a class instance variable. */
final predicate isClassInstanceVariable() { range.isClassInstanceVariable() }
final override InstanceVariableAccess getAnAccess() { result.getVariable() = this }
}
/** A class variable. */
class ClassVariable extends Variable, TClassVariable {
override ClassVariable::Range range;
final override ClassVariableAccess getAnAccess() { result.getVariable() = this }
}
/** An access to a variable. */
class VariableAccess extends Expr {
override VariableAccess::Range range;
/** Gets the variable this identifier refers to. */
Variable getVariable() { result = range.getVariable() }
/**
* Holds if this access is a write access belonging to the explicit
* assignment `assignment`. For example, in
*
* ```rb
* a, b = foo
* ```
*
* both `a` and `b` are write accesses belonging to the same assignment.
*/
predicate isExplicitWrite(AstNode assignment) { explicitWriteAccess(this, assignment) }
/**
* Holds if this access is a write access belonging to an implicit assignment.
* For example, in
*
* ```rb
* def m elements
* for e in elements do
* puts e
* end
* end
* ```
*
* the access to `elements` in the parameter list is an implicit assignment,
* as is the first access to `e`.
*/
predicate isImplicitWrite() { implicitWriteAccess(this) }
final override string toString() { result = this.getVariable().getName() }
}
/** An access to a variable where the value is updated. */
class VariableWriteAccess extends VariableAccess {
VariableWriteAccess() {
this.isExplicitWrite(_) or
this.isImplicitWrite()
}
}
/** An access to a variable where the value is read. */
class VariableReadAccess extends VariableAccess {
VariableReadAccess() {
not this instanceof VariableWriteAccess
or
// `x` in `x += y` is considered both a read and a write
this = any(AssignOperation a).getLhs()
}
}
/** An access to a local variable. */
class LocalVariableAccess extends VariableAccess, @token_identifier {
final override LocalVariableAccess::Range range;
final override LocalVariable getVariable() { result = range.getVariable() }
final override string getAPrimaryQlClass() {
not this instanceof SimpleParameter and result = "LocalVariableAccess"
}
/**
* Holds if this access is a captured variable access. For example in
*
* ```rb
* def m x
* x.times do |y|
* puts x
* end
* puts x
* end
* ```
*
* the access to `x` in the first `puts x` is a captured access, while
* the access to `x` in the second `puts x` is not.
*/
final predicate isCapturedAccess() { isCapturedAccess(this) }
}
/** An access to a local variable where the value is updated. */
class LocalVariableWriteAccess extends LocalVariableAccess, VariableWriteAccess { }
/** An access to a local variable where the value is read. */
class LocalVariableReadAccess extends LocalVariableAccess, VariableReadAccess { }
/** An access to a global variable. */
class GlobalVariableAccess extends VariableAccess, @token_global_variable {
final override GlobalVariableAccess::Range range;
final override GlobalVariable getVariable() { result = range.getVariable() }
final override string getAPrimaryQlClass() { result = "GlobalVariableAccess" }
}
/** An access to a global variable where the value is updated. */
class GlobalVariableWriteAccess extends GlobalVariableAccess, VariableWriteAccess { }
/** An access to a global variable where the value is read. */
class GlobalVariableReadAccess extends GlobalVariableAccess, VariableReadAccess { }
/** An access to an instance variable. */
class InstanceVariableAccess extends VariableAccess, @token_instance_variable {
final override InstanceVariableAccess::Range range;
final override InstanceVariable getVariable() { result = range.getVariable() }
final override string getAPrimaryQlClass() { result = "InstanceVariableAccess" }
}
/** An access to a class variable. */
class ClassVariableAccess extends VariableAccess, @token_class_variable {
final override ClassVariableAccess::Range range;
final override ClassVariable getVariable() { result = range.getVariable() }
final override string getAPrimaryQlClass() { result = "ClassVariableAccess" }
}