mirror of
https://github.com/github/codeql.git
synced 2026-01-01 00:27:24 +01:00
164 lines
4.6 KiB
Plaintext
164 lines
4.6 KiB
Plaintext
import codeql.Locations
|
|
import ast.Call
|
|
import ast.Control
|
|
import ast.Constant
|
|
import ast.Erb
|
|
import ast.Expr
|
|
import ast.Literal
|
|
import ast.Method
|
|
import ast.Module
|
|
import ast.Parameter
|
|
import ast.Operation
|
|
import ast.Pattern
|
|
import ast.Scope
|
|
import ast.Statement
|
|
import ast.Variable
|
|
private import ast.internal.AST
|
|
private import ast.internal.Scope
|
|
private import ast.internal.Synthesis
|
|
private import ast.internal.TreeSitter
|
|
private import Customizations
|
|
|
|
cached
|
|
private module Cached {
|
|
cached
|
|
ModuleBase getEnclosingModule(Scope s) {
|
|
result = s
|
|
or
|
|
not s instanceof ModuleBase and result = getEnclosingModule(s.getOuterScope())
|
|
}
|
|
|
|
cached
|
|
MethodBase getEnclosingMethod(Scope s) {
|
|
result = s
|
|
or
|
|
not s instanceof MethodBase and
|
|
not s instanceof ModuleBase and
|
|
result = getEnclosingMethod(s.getOuterScope())
|
|
}
|
|
|
|
cached
|
|
Toplevel getEnclosingToplevel(Scope s) {
|
|
result = s
|
|
or
|
|
result = getEnclosingToplevel(s.getOuterScope())
|
|
}
|
|
}
|
|
|
|
private import Cached
|
|
|
|
/**
|
|
* A node in the abstract syntax tree. This class is the base class for all Ruby
|
|
* program elements.
|
|
*/
|
|
class AstNode extends TAstNode {
|
|
/**
|
|
* Gets the name of a primary CodeQL class to which this node belongs.
|
|
*
|
|
* This predicate always has a result. If no primary class can be
|
|
* determined, the result is `"???"`. If multiple primary classes match,
|
|
* this predicate can have multiple results.
|
|
*/
|
|
string getAPrimaryQlClass() { result = "???" }
|
|
|
|
/**
|
|
* Gets a comma-separated list of the names of the primary CodeQL classes to
|
|
* which this element belongs.
|
|
*/
|
|
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
|
|
|
|
/** Gets the enclosing module, if any. */
|
|
final ModuleBase getEnclosingModule() { result = getEnclosingModule(scopeOfInclSynth(this)) }
|
|
|
|
/** Gets the enclosing method, if any. */
|
|
final MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) }
|
|
|
|
/** Gets the enclosing top-level. */
|
|
final Toplevel getEnclosingToplevel() { result = getEnclosingToplevel(scopeOfInclSynth(this)) }
|
|
|
|
/** Gets a textual representation of this node. */
|
|
cached
|
|
string toString() { none() }
|
|
|
|
/** Gets the location of this node. */
|
|
Location getLocation() { result = getLocation(this) }
|
|
|
|
/** Gets the file of this node. */
|
|
final File getFile() { result = this.getLocation().getFile() }
|
|
|
|
/** Gets a child node of this `AstNode`. */
|
|
final AstNode getAChild() { result = this.getAChild(_) }
|
|
|
|
/** Gets the parent of this `AstNode`, if this node is not a root node. */
|
|
final AstNode getParent() { result.getAChild() = this }
|
|
|
|
/**
|
|
* Gets a child of this node, which can also be retrieved using a predicate
|
|
* named `pred`.
|
|
*/
|
|
cached
|
|
AstNode getAChild(string pred) {
|
|
pred = "getDesugared" and
|
|
result = this.getDesugared()
|
|
}
|
|
|
|
/**
|
|
* Holds if this node was synthesized to represent an implicit AST node not
|
|
* present in the source code. In the following example method call, the
|
|
* receiver is an implicit `self` reference, for which there is a synthesized
|
|
* `Self` node.
|
|
*
|
|
* ```rb
|
|
* foo(123)
|
|
* ```
|
|
*/
|
|
final predicate isSynthesized() { this = getSynthChild(_, _) }
|
|
|
|
/**
|
|
* Gets the desugared version of this AST node, if any.
|
|
*
|
|
* For example, the desugared version of
|
|
*
|
|
* ```rb
|
|
* x += y
|
|
* ```
|
|
*
|
|
* is
|
|
*
|
|
* ```rb
|
|
* x = x + y
|
|
* ```
|
|
*
|
|
* when `x` is a variable. Whenever an AST node can be desugared,
|
|
* then the desugared version is used in the control-flow graph.
|
|
*/
|
|
final AstNode getDesugared() { result = getSynthChild(this, -1) }
|
|
}
|
|
|
|
/** A Ruby source file */
|
|
class RubyFile extends File {
|
|
RubyFile() { ruby_ast_node_info(_, this, _, _) }
|
|
|
|
/** Gets a token in this file. */
|
|
private Ruby::Token getAToken() { result.getLocation().getFile() = this }
|
|
|
|
/** Holds if `line` contains a token. */
|
|
private predicate line(int line, boolean comment) {
|
|
exists(Ruby::Token token, Location l |
|
|
token = this.getAToken() and
|
|
l = token.getLocation() and
|
|
line in [l.getStartLine() .. l.getEndLine()] and
|
|
if token instanceof @ruby_token_comment then comment = true else comment = false
|
|
)
|
|
}
|
|
|
|
/** Gets the number of lines in this file. */
|
|
int getNumberOfLines() { result = max([0, this.getAToken().getLocation().getEndLine()]) }
|
|
|
|
/** Gets the number of lines of code in this file. */
|
|
int getNumberOfLinesOfCode() { result = count(int line | this.line(line, false)) }
|
|
|
|
/** Gets the number of lines of comments in this file. */
|
|
int getNumberOfLinesOfComments() { result = count(int line | this.line(line, true)) }
|
|
}
|