mirror of
https://github.com/github/codeql.git
synced 2026-02-24 02:43:40 +01:00
Move AST files into ast folder
This commit is contained in:
119
ql/src/codeql_ruby/ast/Method.qll
Normal file
119
ql/src/codeql_ruby/ast/Method.qll
Normal file
@@ -0,0 +1,119 @@
|
||||
import codeql_ruby.AST
|
||||
private import codeql_ruby.Generated
|
||||
|
||||
/** A Ruby method. */
|
||||
class Method extends @method, AstNode {
|
||||
Generated::Method generated;
|
||||
|
||||
Method() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "Method" }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
/** Gets the name of the method. */
|
||||
string getName() {
|
||||
result = generated.getName().(Generated::Token).getValue() or
|
||||
// TODO: use hand-written Symbol class
|
||||
result = generated.getName().(Generated::Symbol).toString() or
|
||||
result = generated.getName().(Generated::Setter).getName().getValue() + "="
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a setter method, as in the following example:
|
||||
* ```
|
||||
* class Person
|
||||
* def name=(n)
|
||||
* @name = n
|
||||
* end
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
predicate isSetter() { generated.getName() instanceof Generated::Setter }
|
||||
|
||||
/** Gets the number of parameters of this method. */
|
||||
int getNumberOfParameters() { result = count(this.getAParameter()) }
|
||||
|
||||
/** Gets a parameter of this method. */
|
||||
Parameter getAParameter() { result = this.getParameter(_) }
|
||||
|
||||
/** Gets the nth parameter of this method. */
|
||||
Parameter getParameter(int n) { result = generated.getParameters().getChild(n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Ruby lambda (anonymous method). For example:
|
||||
* ```
|
||||
* -> (x) { x + 1 }
|
||||
* ```
|
||||
*/
|
||||
class Lambda extends @lambda, AstNode {
|
||||
Generated::Lambda generated;
|
||||
|
||||
Lambda() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "Lambda" }
|
||||
|
||||
override string toString() { result = "-> { ... }" }
|
||||
|
||||
/** Gets the number of parameters of this lambda. */
|
||||
int getNumberOfParameters() { result = count(this.getAParameter()) }
|
||||
|
||||
/** Gets a parameter of this lambda. */
|
||||
Parameter getAParameter() { result = this.getParameter(_) }
|
||||
|
||||
/** Gets the nth parameter of this lambda. */
|
||||
Parameter getParameter(int n) { result = generated.getParameters().getChild(n) }
|
||||
}
|
||||
|
||||
/** A Ruby block. */
|
||||
abstract class Block extends AstNode {
|
||||
/** Gets the number of parameters of this block. */
|
||||
abstract int getNumberOfParameters();
|
||||
|
||||
/** Gets the nth parameter of this block. */
|
||||
abstract Parameter getParameter(int n);
|
||||
|
||||
/** Gets a parameter of this block. */
|
||||
Parameter getAParameter() { result = this.getParameter(_) }
|
||||
// TODO: body/statements
|
||||
}
|
||||
|
||||
/** A Ruby block enclosed within `do` and `end`. */
|
||||
class DoBlock extends @do_block, Block {
|
||||
Generated::DoBlock generated;
|
||||
|
||||
DoBlock() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "DoBlock" }
|
||||
|
||||
override string toString() { result = "| ... |" }
|
||||
|
||||
/** Gets the number of parameters of this block. */
|
||||
override int getNumberOfParameters() { result = count(this.getAParameter()) }
|
||||
|
||||
/** Gets the nth parameter of this block. */
|
||||
override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Ruby block defined using curly braces, e.g. in the following code:
|
||||
* ```
|
||||
* names.each { |name| puts name }
|
||||
* ```
|
||||
*/
|
||||
class BraceBlock extends @block, Block {
|
||||
Generated::Block generated;
|
||||
|
||||
BraceBlock() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "BraceBlock" }
|
||||
|
||||
override string toString() { result = "{ ... }" }
|
||||
|
||||
/** Gets the number of parameters of this block. */
|
||||
override int getNumberOfParameters() { result = count(this.getAParameter()) }
|
||||
|
||||
/** Gets the nth parameter of this block. */
|
||||
override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) }
|
||||
}
|
||||
182
ql/src/codeql_ruby/ast/Parameter.qll
Normal file
182
ql/src/codeql_ruby/ast/Parameter.qll
Normal file
@@ -0,0 +1,182 @@
|
||||
import codeql_ruby.AST
|
||||
private import codeql_ruby.Generated
|
||||
|
||||
/**
|
||||
* A parameter to a block, lambda, or method.
|
||||
*/
|
||||
abstract class Parameter extends AstNode {
|
||||
/**
|
||||
* Gets the position of this parameter in the parent block, lambda, or
|
||||
* method's parameter list.
|
||||
*/
|
||||
int getPosition() {
|
||||
exists(Method m | m.getParameter(result) = this) or
|
||||
exists(Block b | b.getParameter(result) = this) or
|
||||
exists(Lambda l | l.getParameter(result) = this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter that is a block. For example, `&bar` in the following code:
|
||||
* ```
|
||||
* def foo(&bar)
|
||||
* bar.call if block_given?
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class BlockParameter extends @block_parameter, Parameter {
|
||||
Generated::BlockParameter generated;
|
||||
|
||||
BlockParameter() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "BlockParameter" }
|
||||
|
||||
override string toString() { result = "&" + this.getName() }
|
||||
|
||||
/** Gets the name of the parameter. */
|
||||
string getName() { result = generated.getName().getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter that is destructured. For example, the parameter `(a, b)` in the
|
||||
* following code:
|
||||
* ```
|
||||
* pairs.each do |(a, b)|
|
||||
* puts a + b
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class PatternParameter extends @destructured_parameter, Parameter, Pattern {
|
||||
Generated::DestructuredParameter generated;
|
||||
|
||||
PatternParameter() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "PatternParameter" }
|
||||
|
||||
override string toString() { result = "(..., ...)" }
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this destructuring.
|
||||
*/
|
||||
override int getNumberOfElements() { result = count(this.getElement(_)) }
|
||||
|
||||
/**
|
||||
* Gets the nth parameter of this pattern.
|
||||
*/
|
||||
override AstNode getElement(int n) { result = generated.getChild(n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A hash-splat (or double-splat) parameter. For example, `**options` in the
|
||||
* following code:
|
||||
* ```
|
||||
* def foo(bar, **options)
|
||||
* ...
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class HashSplatParameter extends @hash_splat_parameter, Parameter {
|
||||
Generated::HashSplatParameter generated;
|
||||
|
||||
HashSplatParameter() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "HashSplatParameter" }
|
||||
|
||||
override string toString() { result = "**" + this.getName() }
|
||||
|
||||
/** Gets the name of the parameter. */
|
||||
string getName() { result = generated.getName().getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
class KeywordParameter extends @keyword_parameter, Parameter {
|
||||
Generated::KeywordParameter generated;
|
||||
|
||||
KeywordParameter() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "KeywordParameter" }
|
||||
|
||||
/** Gets the name of the parameter. */
|
||||
string getName() { result = generated.getName().getValue() }
|
||||
|
||||
/**
|
||||
* Gets the default value, i.e. the value assigned to the parameter when one
|
||||
* is not provided by the caller. If the parameter is mandatory and does not
|
||||
* have a default value, this predicate has no result.
|
||||
* TODO: better return type (Expr?)
|
||||
*/
|
||||
AstNode getDefaultValue() { result = generated.getValue() }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional parameter. For example, the parameter `name` in the following
|
||||
* code:
|
||||
* ```
|
||||
* def say_hello(name = 'Anon')
|
||||
* puts "hello #{name}"
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class OptionalParameter extends @optional_parameter, Parameter {
|
||||
Generated::OptionalParameter generated;
|
||||
|
||||
OptionalParameter() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "OptionalParameter" }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
/** Gets the name of the parameter. */
|
||||
string getName() { result = generated.getName().getValue() }
|
||||
|
||||
/**
|
||||
* Gets the default value, i.e. the value assigned to the parameter when one
|
||||
* is not provided by the caller.
|
||||
* TODO: better return type (Expr?)
|
||||
*/
|
||||
AstNode getDefaultValue() { result = generated.getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A splat parameter. For example, `*values` in the following code:
|
||||
* ```
|
||||
* def foo(bar, *values)
|
||||
* ...
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class SplatParameter extends @splat_parameter, Parameter {
|
||||
Generated::SplatParameter generated;
|
||||
|
||||
SplatParameter() { generated = this }
|
||||
|
||||
override string describeQlClass() { result = "SplatParameter" }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
/** Gets the name of the parameter. */
|
||||
string getName() { result = generated.getName().getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An identifier that is a parameter in a block, lambda, or method.
|
||||
*/
|
||||
class IdentifierParameter extends @token_identifier, Parameter {
|
||||
IdentifierParameter() {
|
||||
block_parameters_child(_, _, this) or
|
||||
destructured_parameter_child(_, _, this) or
|
||||
lambda_parameters_child(_, _, this) or
|
||||
method_parameters_child(_, _, this)
|
||||
}
|
||||
|
||||
override string describeQlClass() { result = "IdentifierParameter" }
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
/** Gets the name of the parameter. */
|
||||
string getName() { result = this.(Generated::Identifier).getValue() }
|
||||
}
|
||||
256
ql/src/codeql_ruby/ast/Variable.qll
Normal file
256
ql/src/codeql_ruby/ast/Variable.qll
Normal file
@@ -0,0 +1,256 @@
|
||||
/** Provides classes for modeling program variables. */
|
||||
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import codeql.Locations
|
||||
|
||||
private AstNode parent(AstNode n) {
|
||||
result = n.getParent() and
|
||||
not n = any(VariableScope s).getScopeElement()
|
||||
}
|
||||
|
||||
/** Gets the enclosing scope for `node`. */
|
||||
private VariableScope enclosingScope(AstNode node) {
|
||||
result.getScopeElement() = parent*(node.getParent())
|
||||
}
|
||||
|
||||
/** A parameter. */
|
||||
class Parameter extends AstNode {
|
||||
private int position;
|
||||
private VariableScope scope;
|
||||
|
||||
Parameter() {
|
||||
this =
|
||||
scope.(BlockScope).getScopeElement().getAFieldOrChild().(BlockParameters).getChild(position)
|
||||
or
|
||||
this =
|
||||
scope.(MethodScope).getScopeElement().getAFieldOrChild().(MethodParameters).getChild(position)
|
||||
}
|
||||
|
||||
/** Gets the (zero-based) position of this parameter. */
|
||||
final int getPosition() { result = position }
|
||||
|
||||
/** Gets the scope this parameter is declared in. */
|
||||
final VariableScope getDeclaringScope() { result = scope }
|
||||
|
||||
/** Gets an access to this parameter. */
|
||||
final ParameterAccess getAnAccess() { result.getParameter() = this }
|
||||
}
|
||||
|
||||
private Identifier parameterIdentifier(Parameter p) {
|
||||
result = p or
|
||||
result = p.(SplatParameter).getName() or
|
||||
result = p.(HashSplatParameter).getName() or
|
||||
result = p.(BlockParameter).getName() or
|
||||
result = p.(OptionalParameter).getName() or
|
||||
result = p.(KeywordParameter).getName() or
|
||||
result = destructuredIdentifier(p.(DestructuredParameter))
|
||||
}
|
||||
|
||||
private Identifier destructuredIdentifier(AstNode node) {
|
||||
result = node or
|
||||
result = destructuredIdentifier(node.(DestructuredParameter).getAFieldOrChild())
|
||||
}
|
||||
|
||||
/** Holds if `scope` defines `name` in its parameter declaration. */
|
||||
private predicate scopeDefinesParameter(VariableScope scope, string name, Location location) {
|
||||
location =
|
||||
min(Parameter p, Identifier i |
|
||||
scope = p.getDeclaringScope() and
|
||||
i = parameterIdentifier(p) and
|
||||
name = i.getValue()
|
||||
|
|
||||
i.getLocation() as loc order by loc.getStartLine(), loc.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `var` is assigned in `scope`. */
|
||||
private predicate scopeAssigns(VariableScope scope, Identifier var) {
|
||||
var in [any(Assignment assign).getLeft(), any(OperatorAssignment assign).getLeft()] and
|
||||
scope = enclosingScope(var)
|
||||
}
|
||||
|
||||
/** Holds if location `one` starts strictly before location `two` */
|
||||
pragma[inline]
|
||||
predicate strictlyBefore(Location one, Location two) {
|
||||
one.getStartLine() < two.getStartLine()
|
||||
or
|
||||
one.getStartLine() = two.getStartLine() and one.getStartColumn() < two.getStartColumn()
|
||||
}
|
||||
|
||||
/** Holds if block scope `scope` inherits `var` from an outer scope `outer`. */
|
||||
private predicate blockScopeInherits(BlockScope scope, string var, VariableScope outer) {
|
||||
not scopeDefinesParameter(scope, var, _) and
|
||||
(
|
||||
outer = scope.getOuterScope() and
|
||||
(
|
||||
scopeDefinesParameter(outer, var, _)
|
||||
or
|
||||
exists(Identifier i | i.getValue() = var |
|
||||
scopeAssigns(outer, i) and
|
||||
strictlyBefore(i.getLocation(), scope.getLocation())
|
||||
)
|
||||
)
|
||||
or
|
||||
blockScopeInherits(scope.getOuterScope(), var, outer)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TScope =
|
||||
TTopLevelScope(Program node) or
|
||||
TModuleScope(Module node) or
|
||||
TClassScope(AstNode cls) { cls instanceof Class or cls instanceof SingletonClass } or
|
||||
TMethodScope(AstNode method) { method instanceof Method or method instanceof SingletonMethod } or
|
||||
TBlockScope(AstNode block) { block instanceof Block or block instanceof DoBlock }
|
||||
|
||||
cached
|
||||
newtype TVariable =
|
||||
TLocalVariable(VariableScope scope, string name, Location location) {
|
||||
scopeDefinesParameter(scope, name, location)
|
||||
or
|
||||
not scopeDefinesParameter(scope, name, _) and
|
||||
not blockScopeInherits(scope, name, _) and
|
||||
location =
|
||||
min(Location loc, Identifier other |
|
||||
loc = other.getLocation() and name = other.getValue() and scopeAssigns(scope, other)
|
||||
|
|
||||
loc order by loc.getStartLine(), loc.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate access(Identifier access, Variable variable) {
|
||||
exists(string name | name = access.getValue() |
|
||||
variable = enclosingScope(access).getVariable(name) and
|
||||
not strictlyBefore(access.getLocation(), variable.getLocation())
|
||||
or
|
||||
exists(VariableScope declScope |
|
||||
variable = declScope.getVariable(name) and
|
||||
blockScopeInherits(enclosingScope(access), name, declScope)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/** A scope in which variables can be declared. */
|
||||
class VariableScope extends TScope {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the program element this scope is associated with, if any. */
|
||||
AstNode getScopeElement() { none() }
|
||||
|
||||
/** 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
|
||||
}
|
||||
}
|
||||
|
||||
/** A variable declared in a scope. */
|
||||
class Variable extends TVariable {
|
||||
/** Gets the name of this variable. */
|
||||
string getName() { none() }
|
||||
|
||||
/** Gets a textual representation of this variable. */
|
||||
final string toString() { result = this.getName() }
|
||||
|
||||
/** Gets the location of this variable. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the scope this variable is declared in. */
|
||||
VariableScope getDeclaringScope() { none() }
|
||||
|
||||
/** Gets an access to this variable. */
|
||||
VariableAccess getAnAccess() { result.getVariable() = this }
|
||||
}
|
||||
|
||||
/** A local variable. */
|
||||
class LocalVariable extends Variable {
|
||||
private VariableScope scope;
|
||||
private string name;
|
||||
private Location location;
|
||||
|
||||
LocalVariable() { this = TLocalVariable(scope, name, location) }
|
||||
|
||||
final override string getName() { result = name }
|
||||
|
||||
final override Location getLocation() { result = location }
|
||||
|
||||
final override VariableScope getDeclaringScope() { result = scope }
|
||||
}
|
||||
|
||||
/** An identifier that refers to a variable. */
|
||||
class VariableAccess extends Identifier {
|
||||
Variable variable;
|
||||
|
||||
VariableAccess() { access(this, variable) }
|
||||
|
||||
/**
|
||||
* Gets the variable this identifier refers to.
|
||||
*/
|
||||
Variable getVariable() { result = variable }
|
||||
}
|
||||
|
||||
/** An identifier that refers to a parameter. */
|
||||
class ParameterAccess extends VariableAccess {
|
||||
Parameter parameter;
|
||||
|
||||
ParameterAccess() {
|
||||
exists(Identifier i |
|
||||
i = parameterIdentifier(parameter) and
|
||||
variable.getDeclaringScope() = parameter.getDeclaringScope() and
|
||||
variable.getLocation() = i.getLocation()
|
||||
)
|
||||
}
|
||||
|
||||
final Parameter getParameter() { result = parameter }
|
||||
}
|
||||
|
||||
/** A top-level scope. */
|
||||
class TopLevelScope extends VariableScope, TTopLevelScope {
|
||||
final override string toString() { result = "top-level scope" }
|
||||
|
||||
final override AstNode getScopeElement() { TTopLevelScope(result) = this }
|
||||
}
|
||||
|
||||
/** A module scope. */
|
||||
class ModuleScope extends VariableScope, TModuleScope {
|
||||
final override string toString() { result = "module scope" }
|
||||
|
||||
final override Module getScopeElement() { TModuleScope(result) = this }
|
||||
}
|
||||
|
||||
/** A class scope. */
|
||||
class ClassScope extends VariableScope, TClassScope {
|
||||
final override string toString() { result = "class scope" }
|
||||
|
||||
final override AstNode getScopeElement() { TClassScope(result) = this }
|
||||
}
|
||||
|
||||
/** A method scope. */
|
||||
class MethodScope extends VariableScope, TMethodScope {
|
||||
final override string toString() { result = "method scope" }
|
||||
|
||||
final override AstNode getScopeElement() { TMethodScope(result) = this }
|
||||
}
|
||||
|
||||
/** A block scope. */
|
||||
class BlockScope extends VariableScope, TBlockScope {
|
||||
final override string toString() { result = "block scope" }
|
||||
|
||||
final override AstNode getScopeElement() { TBlockScope(result) = this }
|
||||
|
||||
/** Gets the scope in which this scope is nested, if any. */
|
||||
final VariableScope getOuterScope() { result = enclosingScope(this.getScopeElement()) }
|
||||
}
|
||||
Reference in New Issue
Block a user