mirror of
https://github.com/github/codeql.git
synced 2026-04-27 01:35:13 +02:00
Initial work on user-facing AST library
This commit is contained in:
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -46,8 +46,8 @@ jobs:
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: ruby_ast.qll
|
||||
path: ql/src/codeql_ruby/ast.qll
|
||||
name: Generated.qll
|
||||
path: ql/src/codeql_ruby/Generated.qll
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: extractor-${{ matrix.os }}
|
||||
|
||||
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -64,9 +64,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.62"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
|
||||
checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -397,9 +397,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
|
||||
checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
@@ -409,9 +409,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.48"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
|
||||
checksum = "443b4178719c5a851e1bde36ce12da21d74a0e60b4d982ec3385a933c812f0f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -535,7 +535,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tree-sitter-ruby"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/tree-sitter/tree-sitter-ruby.git?rev=93632008c63e0577413bbedf120fb92a96397785#93632008c63e0577413bbedf120fb92a96397785"
|
||||
source = "git+https://github.com/nickrolfe/tree-sitter-ruby.git?branch=refinements#cf76d2c60dac54259912f0c3c4d5c73e78304ace"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
|
||||
@@ -12,13 +12,13 @@ cargo build --release
|
||||
|
||||
## Generating the database schema and QL library
|
||||
|
||||
The generated `ql/src/ruby.dbscheme` and `ql/src/codeql_ruby/ast.qll` files are included in the repository, but they can be re-generated as follows:
|
||||
The generated `ql/src/ruby.dbscheme` and `ql/src/codeql_ruby/Generated.qll` files are included in the repository, but they can be re-generated as follows:
|
||||
|
||||
```bash
|
||||
# Run the generator
|
||||
cargo run --release -p ruby-generator
|
||||
# Then auto-format the QL library
|
||||
codeql query format -i ql/src/codeql_ruby/ast.qll
|
||||
codeql query format -i ql/src/codeql_ruby/Generated.qll
|
||||
```
|
||||
|
||||
## Building a CodeQL database for a Ruby program
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cargo build --release
|
||||
|
||||
cargo run --release -p ruby-generator
|
||||
codeql query format -i ql\src\codeql_ruby\ast.qll
|
||||
codeql query format -i ql\src\codeql_ruby\Generated.qll
|
||||
|
||||
rm -Recurse -Force extractor-pack
|
||||
mkdir extractor-pack | Out-Null
|
||||
|
||||
@@ -13,7 +13,7 @@ fi
|
||||
cargo build --release
|
||||
|
||||
cargo run --release -p ruby-generator
|
||||
codeql query format -i ql/src/codeql_ruby/ast.qll
|
||||
codeql query format -i ql/src/codeql_ruby/Generated.qll
|
||||
|
||||
rm -rf extractor-pack
|
||||
mkdir -p extractor-pack
|
||||
|
||||
@@ -10,7 +10,7 @@ edition = "2018"
|
||||
flate2 = "1.0"
|
||||
node-types = { path = "../node-types" }
|
||||
tree-sitter = "0.17"
|
||||
tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "93632008c63e0577413bbedf120fb92a96397785" }
|
||||
tree-sitter-ruby = { git = "https://github.com/nickrolfe/tree-sitter-ruby.git", branch = "refinements" }
|
||||
clap = "2.33"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.2", features = ["env-filter"] }
|
||||
|
||||
@@ -10,4 +10,4 @@ edition = "2018"
|
||||
node-types = { path = "../node-types" }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.2", features = ["env-filter"] }
|
||||
tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "93632008c63e0577413bbedf120fb92a96397785" }
|
||||
tree-sitter-ruby = { git = "https://github.com/nickrolfe/tree-sitter-ruby.git", branch = "refinements" }
|
||||
|
||||
@@ -574,7 +574,7 @@ fn main() {
|
||||
name: "Ruby".to_owned(),
|
||||
node_types: tree_sitter_ruby::NODE_TYPES,
|
||||
dbscheme_path: PathBuf::from("ql/src/ruby.dbscheme"),
|
||||
ql_library_path: PathBuf::from("ql/src/codeql_ruby/ast.qll"),
|
||||
ql_library_path: PathBuf::from("ql/src/codeql_ruby/Generated.qll"),
|
||||
};
|
||||
match node_types::read_node_types_str(&ruby.node_types) {
|
||||
Err(e) => {
|
||||
|
||||
@@ -214,10 +214,12 @@ pub fn write<'a>(
|
||||
" * Automatically generated from the tree-sitter grammar; do not edit\n"
|
||||
)?;
|
||||
write!(file, " */\n\n")?;
|
||||
write!(file, "module Generated {{\n")?;
|
||||
|
||||
for element in elements {
|
||||
write!(file, "{}\n\n", &element)?;
|
||||
}
|
||||
|
||||
write!(file, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
40
ql/src/codeql_ruby/AST.qll
Normal file
40
ql/src/codeql_ruby/AST.qll
Normal file
@@ -0,0 +1,40 @@
|
||||
import codeql_ruby.Method
|
||||
import codeql_ruby.Parameter
|
||||
private import codeql_ruby.Generated
|
||||
|
||||
class Location = Generated::Location;
|
||||
|
||||
/**
|
||||
* A node in the abstract syntax tree. This class is the base class for all Ruby
|
||||
* program elements.
|
||||
*/
|
||||
class AstNode extends @ast_node {
|
||||
/**
|
||||
* 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 describeQlClass() { result = "???" }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() { result = "AstNode" }
|
||||
|
||||
/** Gets the location if this node. */
|
||||
Location getLocation() { result = this.(Generated::AstNode).getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Models program elements for destructured patterns.
|
||||
*/
|
||||
abstract class Pattern extends AstNode {
|
||||
/** Gets the number of elements in this pattern. */
|
||||
abstract int getNumberOfElements();
|
||||
|
||||
/** Gets the nth element in this pattern. */
|
||||
abstract AstNode getElement(int n);
|
||||
|
||||
/** Gets an element in this pattern. */
|
||||
AstNode getAnElement() { result = this.getElement(_) }
|
||||
}
|
||||
1540
ql/src/codeql_ruby/Generated.qll
Normal file
1540
ql/src/codeql_ruby/Generated.qll
Normal file
File diff suppressed because it is too large
Load Diff
119
ql/src/codeql_ruby/Method.qll
Normal file
119
ql/src/codeql_ruby/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/Parameter.qll
Normal file
182
ql/src/codeql_ruby/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() }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/** Provides classes representing basic blocks. */
|
||||
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import codeql_ruby.controlflow.ControlFlowGraph
|
||||
private import internal.ControlFlowGraphImpl
|
||||
private import SuccessorTypes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Provides classes representing the control flow graph. */
|
||||
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import codeql_ruby.controlflow.BasicBlocks
|
||||
private import SuccessorTypes
|
||||
private import internal.ControlFlowGraphImpl
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* will likely be part of the hand-written user-facing AST layer.
|
||||
*/
|
||||
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
|
||||
class LogicalNotAstNode extends Unary {
|
||||
AstNode operand;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* A completion represents how a statement or expression terminates.
|
||||
*/
|
||||
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import codeql_ruby.controlflow.ControlFlowGraph
|
||||
private import AstNodes
|
||||
private import NonReturning
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import codeql_ruby.controlflow.ControlFlowGraph
|
||||
private import Completion
|
||||
private import Splitting
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
* caught up by its surrounding loop and turned into a `NormalCompletion`.
|
||||
*/
|
||||
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import AstNodes
|
||||
private import codeql_ruby.controlflow.ControlFlowGraph
|
||||
private import Completion
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Provides a simple analysis for identifying calls that will not return. */
|
||||
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import Completion
|
||||
|
||||
/** A call that definitely does not return (conservative analysis). */
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Provides classes and predicates relevant for splitting the control flow graph.
|
||||
*/
|
||||
|
||||
private import codeql_ruby.ast
|
||||
private import codeql_ruby.Generated::Generated
|
||||
private import AstNodes
|
||||
private import Completion
|
||||
private import ControlFlowGraphImpl
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* to hold for only the AST nodes you wish to view.
|
||||
*/
|
||||
|
||||
import codeql_ruby.ast
|
||||
import codeql_ruby.Generated
|
||||
|
||||
/**
|
||||
* The query can extend this class to control which nodes are printed.
|
||||
@@ -17,13 +17,13 @@ class PrintAstConfiguration extends string {
|
||||
/**
|
||||
* Holds if the given node should be printed.
|
||||
*/
|
||||
predicate shouldPrintNode(AstNode n) { any() }
|
||||
predicate shouldPrintNode(Generated::AstNode n) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in the output tree.
|
||||
*/
|
||||
class PrintAstNode extends AstNode {
|
||||
class PrintAstNode extends Generated::AstNode {
|
||||
string getProperty(string key) {
|
||||
key = "semmle.label" and
|
||||
result = "[" + this.describeQlClass() + "] " + this.toString()
|
||||
@@ -36,15 +36,15 @@ class PrintAstNode extends AstNode {
|
||||
*/
|
||||
predicate shouldPrint() {
|
||||
(
|
||||
not this instanceof Token
|
||||
not this instanceof Generated::Token
|
||||
or
|
||||
exists(AstNode parent | parent.getAFieldOrChild() = this)
|
||||
exists(Generated::AstNode parent | parent.getAFieldOrChild() = this)
|
||||
) and
|
||||
shouldPrintNode(this)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate shouldPrintNode(AstNode n) {
|
||||
private predicate shouldPrintNode(Generated::AstNode n) {
|
||||
exists(PrintAstConfiguration config | config.shouldPrintNode(n))
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import codeql_ruby.printAst
|
||||
import codeql.files.FileSystem
|
||||
|
||||
/**
|
||||
* The source file to generate an AST from.
|
||||
@@ -36,7 +37,7 @@ File getFileBySourceArchiveName(string name) {
|
||||
* Overrides the configuration to print only nodes in the selected source file.
|
||||
*/
|
||||
class Cfg extends PrintAstConfiguration {
|
||||
override predicate shouldPrintNode(AstNode n) {
|
||||
override predicate shouldPrintNode(Generated::AstNode n) {
|
||||
n.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,12 @@ binary_def(
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
@block_child_type = @block_parameters | @token_empty_statement | @underscore_statement
|
||||
block_parameters(
|
||||
unique int block: @block ref,
|
||||
unique int block_parameters: @block_parameters ref
|
||||
);
|
||||
|
||||
@block_child_type = @token_empty_statement | @underscore_statement
|
||||
|
||||
#keyset[block, index]
|
||||
block_child(
|
||||
@@ -399,7 +404,12 @@ do_def(
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
@do_block_child_type = @block_parameters | @else | @ensure | @rescue | @token_empty_statement | @underscore_statement
|
||||
do_block_parameters(
|
||||
unique int do_block: @do_block ref,
|
||||
unique int block_parameters: @block_parameters ref
|
||||
);
|
||||
|
||||
@do_block_child_type = @else | @ensure | @rescue | @token_empty_statement | @underscore_statement
|
||||
|
||||
#keyset[do_block, index]
|
||||
do_block_child(
|
||||
@@ -1063,7 +1073,7 @@ setter_def(
|
||||
unique int id: @setter,
|
||||
int parent: @ast_node_parent ref,
|
||||
int parent_index: int ref,
|
||||
int child: @token_identifier ref,
|
||||
int name: @token_identifier ref,
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
|
||||
1
ql/src/ruby.qll
Normal file
1
ql/src/ruby.qll
Normal file
@@ -0,0 +1 @@
|
||||
import codeql_ruby.AST
|
||||
139
ql/test/library-tests/ast/params/params.expected
Normal file
139
ql/test/library-tests/ast/params/params.expected
Normal file
@@ -0,0 +1,139 @@
|
||||
idParams
|
||||
| params.rb:4:30:4:32 | foo | foo |
|
||||
| params.rb:4:35:4:37 | bar | bar |
|
||||
| params.rb:4:40:4:42 | baz | baz |
|
||||
| params.rb:9:15:9:17 | key | key |
|
||||
| params.rb:9:20:9:24 | value | value |
|
||||
| params.rb:14:11:14:13 | foo | foo |
|
||||
| params.rb:14:16:14:18 | bar | bar |
|
||||
| params.rb:17:32:17:32 | a | a |
|
||||
| params.rb:17:35:17:35 | b | b |
|
||||
| params.rb:17:38:17:38 | c | c |
|
||||
| params.rb:22:16:22:16 | a | a |
|
||||
| params.rb:22:19:22:19 | b | b |
|
||||
| params.rb:25:24:25:28 | first | first |
|
||||
| params.rb:25:31:25:36 | second | second |
|
||||
| params.rb:25:41:25:45 | third | third |
|
||||
| params.rb:25:48:25:53 | fourth | fourth |
|
||||
| params.rb:30:23:30:28 | wibble | wibble |
|
||||
| params.rb:34:16:34:18 | val | val |
|
||||
| params.rb:38:26:38:26 | x | x |
|
||||
| params.rb:41:32:41:32 | x | x |
|
||||
| params.rb:53:34:53:34 | x | x |
|
||||
| params.rb:58:33:58:36 | val1 | val1 |
|
||||
| params.rb:65:29:65:32 | name | name |
|
||||
| params.rb:70:35:70:35 | a | a |
|
||||
blockParams
|
||||
| params.rb:46:28:46:33 | &block | block |
|
||||
| params.rb:62:29:62:34 | &block | block |
|
||||
patternParams
|
||||
| params.rb:17:31:17:39 | (..., ...) | params.rb:17:32:17:32 | a | 0 |
|
||||
| params.rb:17:31:17:39 | (..., ...) | params.rb:17:35:17:35 | b | 1 |
|
||||
| params.rb:17:31:17:39 | (..., ...) | params.rb:17:38:17:38 | c | 2 |
|
||||
| params.rb:22:15:22:20 | (..., ...) | params.rb:22:16:22:16 | a | 0 |
|
||||
| params.rb:22:15:22:20 | (..., ...) | params.rb:22:19:22:19 | b | 1 |
|
||||
| params.rb:25:23:25:37 | (..., ...) | params.rb:25:24:25:28 | first | 0 |
|
||||
| params.rb:25:23:25:37 | (..., ...) | params.rb:25:31:25:36 | second | 1 |
|
||||
| params.rb:25:40:25:54 | (..., ...) | params.rb:25:41:25:45 | third | 0 |
|
||||
| params.rb:25:40:25:54 | (..., ...) | params.rb:25:48:25:53 | fourth | 1 |
|
||||
splatParams
|
||||
| params.rb:30:31:30:36 | splat | splat |
|
||||
| params.rb:34:21:34:26 | splat | splat |
|
||||
| params.rb:38:29:38:33 | blah | blah |
|
||||
hashSplatParams
|
||||
| params.rb:30:39:30:52 | **double_splat | double_splat |
|
||||
| params.rb:34:29:34:42 | **double_splat | double_splat |
|
||||
| params.rb:38:36:38:43 | **wibble | wibble |
|
||||
keywordParams
|
||||
| params.rb:41:35:41:38 | foo | foo | (none) |
|
||||
| params.rb:41:41:41:46 | bar | bar | AstNode |
|
||||
| params.rb:49:28:49:30 | xx | xx | (none) |
|
||||
| params.rb:49:33:49:39 | yy | yy | AstNode |
|
||||
| params.rb:53:37:53:38 | y | y | (none) |
|
||||
| params.rb:53:41:53:44 | z | z | AstNode |
|
||||
optionalParams
|
||||
| params.rb:58:39:58:46 | val2 | val2 | params.rb:58:46:58:46 | AstNode |
|
||||
| params.rb:58:49:58:58 | val3 | val3 | params.rb:58:56:58:58 | AstNode |
|
||||
| params.rb:65:35:65:42 | age | age | params.rb:65:41:65:42 | AstNode |
|
||||
| params.rb:70:38:70:45 | b | b | params.rb:70:42:70:45 | AstNode |
|
||||
| params.rb:70:48:70:53 | c | c | params.rb:70:52:70:53 | AstNode |
|
||||
paramsInMethods
|
||||
| params.rb:4:1:5:3 | identifier_method_params | 0 | params.rb:4:30:4:32 | foo | IdentifierParameter |
|
||||
| params.rb:4:1:5:3 | identifier_method_params | 1 | params.rb:4:35:4:37 | bar | IdentifierParameter |
|
||||
| params.rb:4:1:5:3 | identifier_method_params | 2 | params.rb:4:40:4:42 | baz | IdentifierParameter |
|
||||
| params.rb:17:1:18:3 | destructured_method_param | 0 | params.rb:17:31:17:39 | (..., ...) | PatternParameter |
|
||||
| params.rb:30:1:31:3 | method_with_splat | 0 | params.rb:30:23:30:28 | wibble | IdentifierParameter |
|
||||
| params.rb:30:1:31:3 | method_with_splat | 1 | params.rb:30:31:30:36 | splat | SplatParameter |
|
||||
| params.rb:30:1:31:3 | method_with_splat | 2 | params.rb:30:39:30:52 | **double_splat | HashSplatParameter |
|
||||
| params.rb:41:1:43:3 | method_with_keyword_params | 0 | params.rb:41:32:41:32 | x | IdentifierParameter |
|
||||
| params.rb:41:1:43:3 | method_with_keyword_params | 1 | params.rb:41:35:41:38 | foo | KeywordParameter |
|
||||
| params.rb:41:1:43:3 | method_with_keyword_params | 2 | params.rb:41:41:41:46 | bar | KeywordParameter |
|
||||
| params.rb:46:1:48:3 | use_block_with_keyword | 0 | params.rb:46:28:46:33 | &block | BlockParameter |
|
||||
| params.rb:58:1:59:3 | method_with_optional_params | 0 | params.rb:58:33:58:36 | val1 | IdentifierParameter |
|
||||
| params.rb:58:1:59:3 | method_with_optional_params | 1 | params.rb:58:39:58:46 | val2 | OptionalParameter |
|
||||
| params.rb:58:1:59:3 | method_with_optional_params | 2 | params.rb:58:49:58:58 | val3 | OptionalParameter |
|
||||
| params.rb:62:1:64:3 | use_block_with_optional | 0 | params.rb:62:29:62:34 | &block | BlockParameter |
|
||||
paramsInBlocks
|
||||
| params.rb:9:11:11:3 | \| ... \| | 0 | params.rb:9:15:9:17 | key | IdentifierParameter |
|
||||
| params.rb:9:11:11:3 | \| ... \| | 1 | params.rb:9:20:9:24 | value | IdentifierParameter |
|
||||
| params.rb:22:12:22:32 | { ... } | 0 | params.rb:22:15:22:20 | (..., ...) | PatternParameter |
|
||||
| params.rb:34:12:35:3 | \| ... \| | 0 | params.rb:34:16:34:18 | val | IdentifierParameter |
|
||||
| params.rb:34:12:35:3 | \| ... \| | 1 | params.rb:34:21:34:26 | splat | SplatParameter |
|
||||
| params.rb:34:12:35:3 | \| ... \| | 2 | params.rb:34:29:34:42 | **double_splat | HashSplatParameter |
|
||||
| params.rb:49:24:51:3 | \| ... \| | 0 | params.rb:49:28:49:30 | xx | KeywordParameter |
|
||||
| params.rb:49:24:51:3 | \| ... \| | 1 | params.rb:49:33:49:39 | yy | KeywordParameter |
|
||||
| params.rb:65:25:67:3 | \| ... \| | 0 | params.rb:65:29:65:32 | name | IdentifierParameter |
|
||||
| params.rb:65:25:67:3 | \| ... \| | 1 | params.rb:65:35:65:42 | age | OptionalParameter |
|
||||
paramsInLambdas
|
||||
| params.rb:14:7:14:33 | -> { ... } | 0 | params.rb:14:11:14:13 | foo | IdentifierParameter |
|
||||
| params.rb:14:7:14:33 | -> { ... } | 1 | params.rb:14:16:14:18 | bar | IdentifierParameter |
|
||||
| params.rb:25:19:27:1 | -> { ... } | 0 | params.rb:25:23:25:37 | (..., ...) | PatternParameter |
|
||||
| params.rb:25:19:27:1 | -> { ... } | 1 | params.rb:25:40:25:54 | (..., ...) | PatternParameter |
|
||||
| params.rb:38:22:38:47 | -> { ... } | 0 | params.rb:38:26:38:26 | x | IdentifierParameter |
|
||||
| params.rb:38:22:38:47 | -> { ... } | 1 | params.rb:38:29:38:33 | blah | SplatParameter |
|
||||
| params.rb:38:22:38:47 | -> { ... } | 2 | params.rb:38:36:38:43 | **wibble | HashSplatParameter |
|
||||
| params.rb:53:30:55:1 | -> { ... } | 0 | params.rb:53:34:53:34 | x | IdentifierParameter |
|
||||
| params.rb:53:30:55:1 | -> { ... } | 1 | params.rb:53:37:53:38 | y | KeywordParameter |
|
||||
| params.rb:53:30:55:1 | -> { ... } | 2 | params.rb:53:41:53:44 | z | KeywordParameter |
|
||||
| params.rb:70:31:70:64 | -> { ... } | 0 | params.rb:70:35:70:35 | a | IdentifierParameter |
|
||||
| params.rb:70:31:70:64 | -> { ... } | 1 | params.rb:70:38:70:45 | b | OptionalParameter |
|
||||
| params.rb:70:31:70:64 | -> { ... } | 2 | params.rb:70:48:70:53 | c | OptionalParameter |
|
||||
#select
|
||||
| params.rb:4:30:4:32 | foo | 0 | IdentifierParameter |
|
||||
| params.rb:4:35:4:37 | bar | 1 | IdentifierParameter |
|
||||
| params.rb:4:40:4:42 | baz | 2 | IdentifierParameter |
|
||||
| params.rb:9:15:9:17 | key | 0 | IdentifierParameter |
|
||||
| params.rb:9:20:9:24 | value | 1 | IdentifierParameter |
|
||||
| params.rb:14:11:14:13 | foo | 0 | IdentifierParameter |
|
||||
| params.rb:14:16:14:18 | bar | 1 | IdentifierParameter |
|
||||
| params.rb:17:31:17:39 | (..., ...) | 0 | PatternParameter |
|
||||
| params.rb:22:15:22:20 | (..., ...) | 0 | PatternParameter |
|
||||
| params.rb:25:23:25:37 | (..., ...) | 0 | PatternParameter |
|
||||
| params.rb:25:40:25:54 | (..., ...) | 1 | PatternParameter |
|
||||
| params.rb:30:23:30:28 | wibble | 0 | IdentifierParameter |
|
||||
| params.rb:30:31:30:36 | splat | 1 | SplatParameter |
|
||||
| params.rb:30:39:30:52 | **double_splat | 2 | HashSplatParameter |
|
||||
| params.rb:34:16:34:18 | val | 0 | IdentifierParameter |
|
||||
| params.rb:34:21:34:26 | splat | 1 | SplatParameter |
|
||||
| params.rb:34:29:34:42 | **double_splat | 2 | HashSplatParameter |
|
||||
| params.rb:38:26:38:26 | x | 0 | IdentifierParameter |
|
||||
| params.rb:38:29:38:33 | blah | 1 | SplatParameter |
|
||||
| params.rb:38:36:38:43 | **wibble | 2 | HashSplatParameter |
|
||||
| params.rb:41:32:41:32 | x | 0 | IdentifierParameter |
|
||||
| params.rb:41:35:41:38 | foo | 1 | KeywordParameter |
|
||||
| params.rb:41:41:41:46 | bar | 2 | KeywordParameter |
|
||||
| params.rb:46:28:46:33 | &block | 0 | BlockParameter |
|
||||
| params.rb:49:28:49:30 | xx | 0 | KeywordParameter |
|
||||
| params.rb:49:33:49:39 | yy | 1 | KeywordParameter |
|
||||
| params.rb:53:34:53:34 | x | 0 | IdentifierParameter |
|
||||
| params.rb:53:37:53:38 | y | 1 | KeywordParameter |
|
||||
| params.rb:53:41:53:44 | z | 2 | KeywordParameter |
|
||||
| params.rb:58:33:58:36 | val1 | 0 | IdentifierParameter |
|
||||
| params.rb:58:39:58:46 | val2 | 1 | OptionalParameter |
|
||||
| params.rb:58:49:58:58 | val3 | 2 | OptionalParameter |
|
||||
| params.rb:62:29:62:34 | &block | 0 | BlockParameter |
|
||||
| params.rb:65:29:65:32 | name | 0 | IdentifierParameter |
|
||||
| params.rb:65:35:65:42 | age | 1 | OptionalParameter |
|
||||
| params.rb:70:35:70:35 | a | 0 | IdentifierParameter |
|
||||
| params.rb:70:38:70:45 | b | 1 | OptionalParameter |
|
||||
| params.rb:70:48:70:53 | c | 2 | OptionalParameter |
|
||||
46
ql/test/library-tests/ast/params/params.ql
Normal file
46
ql/test/library-tests/ast/params/params.ql
Normal file
@@ -0,0 +1,46 @@
|
||||
import ruby
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Query predicates for various types of parameter
|
||||
query predicate idParams(IdentifierParameter ip, string name) { name = ip.getName() }
|
||||
|
||||
query predicate blockParams(BlockParameter bp, string name) { name = bp.getName() }
|
||||
|
||||
query predicate patternParams(PatternParameter pp, Parameter child, int childIndex) {
|
||||
pp.getElement(childIndex) = child
|
||||
}
|
||||
|
||||
query predicate splatParams(SplatParameter sp, string name) { name = sp.getName() }
|
||||
|
||||
query predicate hashSplatParams(HashSplatParameter hsp, string name) { name = hsp.getName() }
|
||||
|
||||
query predicate keywordParams(KeywordParameter kp, string name, string defaultValueStr) {
|
||||
name = kp.getName() and
|
||||
if exists(kp.getDefaultValue())
|
||||
then defaultValueStr = kp.getDefaultValue().toString()
|
||||
else defaultValueStr = "(none)"
|
||||
}
|
||||
|
||||
query predicate optionalParams(OptionalParameter op, string name, AstNode defaultValue) {
|
||||
name = op.getName() and
|
||||
defaultValue = op.getDefaultValue()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Query predicates for various contexts of parameters
|
||||
query predicate paramsInMethods(Method m, int i, Parameter p, string pClass) {
|
||||
p = m.getParameter(i) and pClass = p.describeQlClass()
|
||||
}
|
||||
|
||||
query predicate paramsInBlocks(Block b, int i, Parameter p, string pClass) {
|
||||
p = b.getParameter(i) and pClass = p.describeQlClass()
|
||||
}
|
||||
|
||||
query predicate paramsInLambdas(Lambda l, int i, Parameter p, string pClass) {
|
||||
p = l.getParameter(i) and pClass = p.describeQlClass()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// General query selecting all parameters
|
||||
from Parameter p
|
||||
select p, p.getPosition(), p.describeQlClass()
|
||||
70
ql/test/library-tests/ast/params/params.rb
Normal file
70
ql/test/library-tests/ast/params/params.rb
Normal file
@@ -0,0 +1,70 @@
|
||||
# Tests for the different kinds and contexts of parameters.
|
||||
|
||||
# Method containing identifier parameters
|
||||
def identifier_method_params(foo, bar, baz)
|
||||
end
|
||||
|
||||
# Block containing identifier parameters
|
||||
hash = {}
|
||||
hash.each do |key, value|
|
||||
puts "#{key} -> #{value}"
|
||||
end
|
||||
|
||||
# Lambda containing identifier parameters
|
||||
sum = -> (foo, bar) { foo + bar }
|
||||
|
||||
# Method containing destructured parameters
|
||||
def destructured_method_param((a, b, c))
|
||||
end
|
||||
|
||||
# Block containing destructured parameters
|
||||
array = []
|
||||
array.each { |(a, b)| puts a+b }
|
||||
|
||||
# Lambda containing destructured parameters
|
||||
sum_four_values = -> ((first, second), (third, fourth)) {
|
||||
first + second + third + fourth
|
||||
}
|
||||
|
||||
# Method containing splat and hash-splat params
|
||||
def method_with_splat(wibble, *splat, **double_splat)
|
||||
end
|
||||
|
||||
# Block with splat and hash-splat parameter
|
||||
array.each do |val, *splat, **double_splat|
|
||||
end
|
||||
|
||||
# Lambda with splat and hash-splat
|
||||
lambda_with_splats = -> (x, *blah, **wibble) {}
|
||||
|
||||
# Method containing keyword parameters
|
||||
def method_with_keyword_params(x, foo:, bar: 7)
|
||||
x + foo + bar
|
||||
end
|
||||
|
||||
# Block with keyword parameters
|
||||
def use_block_with_keyword(&block)
|
||||
puts(block.call bar: 2, foo: 3)
|
||||
end
|
||||
use_block_with_keyword do |xx:, yy: 100|
|
||||
xx + yy
|
||||
end
|
||||
|
||||
lambda_with_keyword_params = -> (x, y:, z: 3) {
|
||||
x + y + z
|
||||
}
|
||||
|
||||
# Method containing optional parameters
|
||||
def method_with_optional_params(val1, val2 = 0, val3 = 100)
|
||||
end
|
||||
|
||||
# Block containing optional parameter
|
||||
def use_block_with_optional(&block)
|
||||
block.call 'Zeus'
|
||||
end
|
||||
use_block_with_optional do |name, age = 99|
|
||||
puts "#{name} is #{age} years old"
|
||||
end
|
||||
|
||||
# Lambda containing optional parameters
|
||||
lambda_with_optional_params = -> (a, b = 1000, c = 20) { a+b+c }
|
||||
Reference in New Issue
Block a user