mirror of
https://github.com/github/codeql.git
synced 2026-02-20 08:53:49 +01:00
Merge pull request #100 from github/call_ast
Add AST classes and tests for method calls
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -645,7 +645,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tree-sitter-ruby"
|
||||
version = "0.17.0"
|
||||
source = "git+https://github.com/tree-sitter/tree-sitter-ruby.git?rev=5021a6a6eda24e10f954dcfec00e7a7adafba8ba#5021a6a6eda24e10f954dcfec00e7a7adafba8ba"
|
||||
source = "git+https://github.com/tree-sitter/tree-sitter-ruby.git?rev=454f90628d1d14d0953b3be78bdb9a09f7a5bfd7#454f90628d1d14d0953b3be78bdb9a09f7a5bfd7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
|
||||
@@ -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 = "5021a6a6eda24e10f954dcfec00e7a7adafba8ba" }
|
||||
tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "454f90628d1d14d0953b3be78bdb9a09f7a5bfd7" }
|
||||
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 = "5021a6a6eda24e10f954dcfec00e7a7adafba8ba" }
|
||||
tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "454f90628d1d14d0953b3be78bdb9a09f7a5bfd7" }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import codeql.Locations
|
||||
import ast.Call
|
||||
import ast.Control
|
||||
import ast.Expr
|
||||
import ast.Method
|
||||
|
||||
178
ql/src/codeql_ruby/ast/Call.qll
Normal file
178
ql/src/codeql_ruby/ast/Call.qll
Normal file
@@ -0,0 +1,178 @@
|
||||
private import codeql_ruby.AST
|
||||
private import internal.Call
|
||||
|
||||
/**
|
||||
* A call.
|
||||
*/
|
||||
class Call extends Expr {
|
||||
override Call::Range range;
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Call" }
|
||||
|
||||
final override string toString() { result = "call to " + this.getMethodName() }
|
||||
|
||||
/**
|
||||
* Gets the receiver of this call, if any. For example:
|
||||
* ```rb
|
||||
* foo.bar
|
||||
* baz()
|
||||
* ```
|
||||
* The result for the call to `bar` is the `Expr` for `foo`, while the call
|
||||
* to `baz` has no result.
|
||||
*/
|
||||
final Expr getReceiver() { result = range.getReceiver() }
|
||||
|
||||
/**
|
||||
* Gets the name of the method being called. For example, in:
|
||||
* ```rb
|
||||
* foo.bar x, y
|
||||
* ```
|
||||
* the result is `"bar"`.
|
||||
*
|
||||
* N.B. in the following example, where the method name is a scope
|
||||
* resolution, the result is the name being resolved, i.e. `"bar"`. Use
|
||||
* `getMethodScopeResolution` to get the complete `ScopeResolution`.
|
||||
* ```rb
|
||||
* Foo::bar x, y
|
||||
* ```
|
||||
*/
|
||||
final string getMethodName() { result = range.getMethodName() }
|
||||
|
||||
/**
|
||||
* Gets the scope resolution of this call, if any. In the following example,
|
||||
* the result is the `ScopeResolution` for `Foo::bar`, while
|
||||
* `getMethodName()` returns `"bar"`.
|
||||
* ```rb
|
||||
* Foo::bar()
|
||||
* ```
|
||||
*/
|
||||
final ScopeResolution getMethodScopeResolution() { result = range.getMethodScopeResolution() }
|
||||
|
||||
/**
|
||||
* Gets the `n`th argument of this method call. In the following example, the
|
||||
* result for n=0 is the `IntegerLiteral` 0, while for n=1 the result is a
|
||||
* `Pair` (whose `getKey` returns the `SymbolLiteral` for `bar`, and
|
||||
* `getValue` returns the `IntegerLiteral` 1). Keyword arguments like this
|
||||
* can be accessed more naturally using the
|
||||
* `getKeywordArgument(string keyword)` predicate.
|
||||
* ```rb
|
||||
* foo(0, bar: 1)
|
||||
* ```
|
||||
*/
|
||||
final Expr getArgument(int n) { result = range.getArgument(n) }
|
||||
|
||||
/**
|
||||
* Gets an argument of this method call.
|
||||
*/
|
||||
final Expr getAnArgument() { result = this.getArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the value of the keyword argument whose key is `keyword`, if any. For
|
||||
* example, the result for `getKeywordArgument("qux")` in the following
|
||||
* example is the `IntegerLiteral` 123.
|
||||
* ```rb
|
||||
* foo :bar "baz", qux: 123
|
||||
* ```
|
||||
*/
|
||||
final Expr getKeywordArgument(string keyword) {
|
||||
exists(Pair p |
|
||||
p = this.getAnArgument() and
|
||||
p.getKey().(SymbolLiteral).getValueText() = keyword and
|
||||
result = p.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of this method call.
|
||||
*/
|
||||
final int getNumberOfArguments() { result = count(this.getAnArgument()) }
|
||||
|
||||
/**
|
||||
* Gets the block of this method call, if any.
|
||||
* ```rb
|
||||
* foo.each { |x| puts x }
|
||||
* ```
|
||||
*/
|
||||
final Block getBlock() { result = range.getBlock() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `yield`.
|
||||
* ```rb
|
||||
* yield x, y
|
||||
* ```
|
||||
*/
|
||||
class YieldCall extends Call, @yield {
|
||||
final override YieldCall::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "YieldCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A block argument in a method call.
|
||||
* ```rb
|
||||
* foo(&block)
|
||||
* ```
|
||||
*/
|
||||
class BlockArgument extends Expr, @block_argument {
|
||||
final override BlockArgument::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "BlockArgument" }
|
||||
|
||||
final override string toString() { result = "&..." }
|
||||
|
||||
/**
|
||||
* Gets the underlying expression representing the block. In the following
|
||||
* example, the result is the `Expr` for `bar`:
|
||||
* ```rb
|
||||
* foo(&bar)
|
||||
* ```
|
||||
*/
|
||||
final Expr getExpr() { result = range.getExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A splat argument in a method call.
|
||||
* ```rb
|
||||
* foo(*args)
|
||||
* ```
|
||||
*/
|
||||
class SplatArgument extends Expr, @splat_argument {
|
||||
final override SplatArgument::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "SplatArgument" }
|
||||
|
||||
final override string toString() { result = "*..." }
|
||||
|
||||
/**
|
||||
* Gets the underlying expression. In the following example, the result is
|
||||
* the `Expr` for `bar`:
|
||||
* ```rb
|
||||
* foo(*bar)
|
||||
* ```
|
||||
*/
|
||||
final Expr getExpr() { result = range.getExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A hash-splat (or 'double-splat') argument in a method call.
|
||||
* ```rb
|
||||
* foo(**options)
|
||||
* ```
|
||||
*/
|
||||
class HashSplatArgument extends Expr, @hash_splat_argument {
|
||||
final override HashSplatArgument::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "HashSplatArgument" }
|
||||
|
||||
final override string toString() { result = "**..." }
|
||||
|
||||
/**
|
||||
* Gets the underlying expression. In the following example, the result is
|
||||
* the `Expr` for `bar`:
|
||||
* ```rb
|
||||
* foo(**bar)
|
||||
* ```
|
||||
*/
|
||||
final Expr getExpr() { result = range.getExpr() }
|
||||
}
|
||||
@@ -20,9 +20,9 @@ class Expr extends AstNode {
|
||||
class Literal extends Expr {
|
||||
override Literal::Range range;
|
||||
|
||||
override string toString() { result = this.getValueText() }
|
||||
override string toString() { result = range.toString() }
|
||||
|
||||
/** Gets the source text for this literal. */
|
||||
/** Gets the source text for this literal, if it is constant. */
|
||||
final string getValueText() { result = range.getValueText() }
|
||||
}
|
||||
|
||||
@@ -74,6 +74,35 @@ class RegexLiteral extends Literal, @regex {
|
||||
final override string getAPrimaryQlClass() { result = "RegexLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A string literal.
|
||||
* ```rb
|
||||
* 'hello'
|
||||
* "hello, #{name}"
|
||||
* ```
|
||||
* TODO: expand this minimal placeholder.
|
||||
*/
|
||||
class StringLiteral extends Literal, @string__ {
|
||||
final override StringLiteral::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "StringLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A symbol literal.
|
||||
* ```rb
|
||||
* :foo
|
||||
* :"foo bar"
|
||||
* :"foo bar #{baz}"
|
||||
* ```
|
||||
* TODO: expand this minimal placeholder.
|
||||
*/
|
||||
class SymbolLiteral extends Literal {
|
||||
final override SymbolLiteral::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "SymbolLiteral" }
|
||||
}
|
||||
|
||||
/** A sequence of expressions. */
|
||||
class ExprSequence extends Expr {
|
||||
override ExprSequence::Range range;
|
||||
@@ -97,3 +126,78 @@ class ExprSequence extends Expr {
|
||||
/** Holds if this sequence has no expressions. */
|
||||
final predicate isEmpty() { this.getNumberOfExpressions() = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A scope resolution, typically used to access constants defined in a class or
|
||||
* module.
|
||||
* ```rb
|
||||
* Foo::Bar
|
||||
* ```
|
||||
*/
|
||||
class ScopeResolution extends Expr, @scope_resolution {
|
||||
final override ScopeResolution::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ScopeResolution" }
|
||||
|
||||
final override string toString() { result = "...::" + this.getName() }
|
||||
|
||||
/**
|
||||
* Gets the expression representing the scope, if any. In the following
|
||||
* example, the scope is the `Expr` for `Foo`:
|
||||
* ```rb
|
||||
* Foo::Bar
|
||||
* ```
|
||||
* However, in the following example, accessing the `Bar` constant in the
|
||||
* `Object` class, there is no result:
|
||||
* ```rb
|
||||
* ::Bar
|
||||
* ```
|
||||
*/
|
||||
final Expr getScope() { result = range.getScope() }
|
||||
|
||||
/**
|
||||
* Gets the name being resolved. For example, in `Foo::Bar`, the result is
|
||||
* `"Bar"`.
|
||||
*/
|
||||
final string getName() { result = range.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A pair expression. For example, in a hash:
|
||||
* ```rb
|
||||
* { foo: bar }
|
||||
* ```
|
||||
* Or a keyword argument:
|
||||
* ```rb
|
||||
* baz(qux: 1)
|
||||
* ```
|
||||
*/
|
||||
class Pair extends Expr, @pair {
|
||||
final override Pair::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "Pair" }
|
||||
|
||||
final override string toString() { result = "Pair" }
|
||||
|
||||
/**
|
||||
* Gets the key expression of this pair. For example, the `SymbolLiteral`
|
||||
* representing the keyword `foo` in the following example:
|
||||
* ```rb
|
||||
* bar(foo: 123)
|
||||
* ```
|
||||
* Or the `StringLiteral` for `'foo'` in the following hash pair:
|
||||
* ```rb
|
||||
* { 'foo' => 123 }
|
||||
* ```
|
||||
*/
|
||||
final Expr getKey() { result = range.getKey() }
|
||||
|
||||
/**
|
||||
* Gets the value expression of this pair. For example, the `InteralLiteral`
|
||||
* 123 in the following hash pair:
|
||||
* ```rb
|
||||
* { 'foo' => 123 }
|
||||
* ```
|
||||
*/
|
||||
final Expr getValue() { result = range.getValue() }
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class DoBlock extends Block, @do_block {
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "DoBlock" }
|
||||
|
||||
final override string toString() { result = "| ... |" }
|
||||
final override string toString() { result = "do ... end" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
230
ql/src/codeql_ruby/ast/internal/Call.qll
Normal file
230
ql/src/codeql_ruby/ast/internal/Call.qll
Normal file
@@ -0,0 +1,230 @@
|
||||
private import codeql_ruby.AST
|
||||
private import codeql_ruby.ast.internal.Expr
|
||||
private import codeql_ruby.ast.internal.TreeSitter
|
||||
private import codeql_ruby.ast.internal.Variable
|
||||
|
||||
module Call {
|
||||
abstract class Range extends Expr::Range {
|
||||
abstract Expr getReceiver();
|
||||
|
||||
abstract string getMethodName();
|
||||
|
||||
abstract ScopeResolution getMethodScopeResolution();
|
||||
|
||||
abstract Expr getArgument(int n);
|
||||
|
||||
abstract Block getBlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i` is an `identifier` node occurring in the context where it
|
||||
* should be considered a VCALL. VCALL is the term that MRI/Ripper uses
|
||||
* internally when there's an identifier without arguments or parentheses,
|
||||
* i.e. it *might* be a method call, but it might also be a variable access,
|
||||
* depending on the bindings in the current scope.
|
||||
* ```rb
|
||||
* foo # in MRI this is a VCALL, and the predicate should hold for this
|
||||
* bar() # in MRI this would be an FCALL. Tree-sitter gives us a `call` node,
|
||||
* # and the `method` field will be an `identifier`, but this predicate
|
||||
* # will not hold for that identifier.
|
||||
* ```
|
||||
*/
|
||||
private predicate vcall(Generated::Identifier i) {
|
||||
i = any(Generated::ArgumentList x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Array x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Assignment x).getRight()
|
||||
or
|
||||
i = any(Generated::Begin x).getChild(_)
|
||||
or
|
||||
i = any(Generated::BeginBlock x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Binary x).getLeft()
|
||||
or
|
||||
i = any(Generated::Binary x).getRight()
|
||||
or
|
||||
i = any(Generated::Block x).getChild(_)
|
||||
or
|
||||
i = any(Generated::BlockArgument x).getChild()
|
||||
or
|
||||
i = any(Generated::Call x).getReceiver()
|
||||
or
|
||||
i = any(Generated::Case x).getValue()
|
||||
or
|
||||
i = any(Generated::Class x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Conditional x).getCondition()
|
||||
or
|
||||
i = any(Generated::Conditional x).getConsequence()
|
||||
or
|
||||
i = any(Generated::Conditional x).getAlternative()
|
||||
or
|
||||
i = any(Generated::Do x).getChild(_)
|
||||
or
|
||||
i = any(Generated::DoBlock x).getChild(_)
|
||||
or
|
||||
i = any(Generated::ElementReference x).getChild(_)
|
||||
or
|
||||
i = any(Generated::ElementReference x).getObject()
|
||||
or
|
||||
i = any(Generated::Else x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Elsif x).getCondition()
|
||||
or
|
||||
i = any(Generated::EndBlock x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Ensure x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Exceptions x).getChild(_)
|
||||
or
|
||||
i = any(Generated::HashSplatArgument x).getChild()
|
||||
or
|
||||
i = any(Generated::If x).getCondition()
|
||||
or
|
||||
i = any(Generated::IfModifier x).getCondition()
|
||||
or
|
||||
i = any(Generated::IfModifier x).getBody()
|
||||
or
|
||||
i = any(Generated::In x).getChild()
|
||||
or
|
||||
i = any(Generated::Interpolation x).getChild()
|
||||
or
|
||||
i = any(Generated::KeywordParameter x).getValue()
|
||||
or
|
||||
i = any(Generated::Method x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Module x).getChild(_)
|
||||
or
|
||||
i = any(Generated::OperatorAssignment x).getRight()
|
||||
or
|
||||
i = any(Generated::OptionalParameter x).getValue()
|
||||
or
|
||||
i = any(Generated::Pair x).getKey()
|
||||
or
|
||||
i = any(Generated::Pair x).getValue()
|
||||
or
|
||||
i = any(Generated::ParenthesizedStatements x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Pattern x).getChild()
|
||||
or
|
||||
i = any(Generated::Program x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Range x).getChild(_)
|
||||
or
|
||||
i = any(Generated::RescueModifier x).getBody()
|
||||
or
|
||||
i = any(Generated::RescueModifier x).getHandler()
|
||||
or
|
||||
i = any(Generated::RightAssignmentList x).getChild(_)
|
||||
or
|
||||
i = any(Generated::ScopeResolution x).getScope()
|
||||
or
|
||||
i = any(Generated::SingletonClass x).getValue()
|
||||
or
|
||||
i = any(Generated::SingletonClass x).getChild(_)
|
||||
or
|
||||
i = any(Generated::SingletonMethod x).getChild(_)
|
||||
or
|
||||
i = any(Generated::SingletonMethod x).getObject()
|
||||
or
|
||||
i = any(Generated::SplatArgument x).getChild()
|
||||
or
|
||||
i = any(Generated::Superclass x).getChild()
|
||||
or
|
||||
i = any(Generated::Then x).getChild(_)
|
||||
or
|
||||
i = any(Generated::Unary x).getOperand()
|
||||
or
|
||||
i = any(Generated::Unless x).getCondition()
|
||||
or
|
||||
i = any(Generated::UnlessModifier x).getCondition()
|
||||
or
|
||||
i = any(Generated::UnlessModifier x).getBody()
|
||||
or
|
||||
i = any(Generated::Until x).getCondition()
|
||||
or
|
||||
i = any(Generated::UntilModifier x).getCondition()
|
||||
or
|
||||
i = any(Generated::UntilModifier x).getBody()
|
||||
or
|
||||
i = any(Generated::While x).getCondition()
|
||||
or
|
||||
i = any(Generated::WhileModifier x).getCondition()
|
||||
or
|
||||
i = any(Generated::WhileModifier x).getBody()
|
||||
}
|
||||
|
||||
private class IdentifierCallRange extends Call::Range, @token_identifier {
|
||||
final override Generated::Identifier generated;
|
||||
|
||||
IdentifierCallRange() { vcall(this) and not access(this, _) }
|
||||
|
||||
final override Expr getReceiver() { none() }
|
||||
|
||||
final override string getMethodName() { result = generated.getValue() }
|
||||
|
||||
final override ScopeResolution getMethodScopeResolution() { none() }
|
||||
|
||||
final override Expr getArgument(int n) { none() }
|
||||
|
||||
final override Block getBlock() { none() }
|
||||
}
|
||||
|
||||
private class RegularCallRange extends Call::Range, @call {
|
||||
final override Generated::Call generated;
|
||||
|
||||
final override Expr getReceiver() { result = generated.getReceiver() }
|
||||
|
||||
final override string getMethodName() {
|
||||
result = generated.getMethod().(Generated::Token).getValue() or
|
||||
result = this.getMethodScopeResolution().getName()
|
||||
}
|
||||
|
||||
final override ScopeResolution getMethodScopeResolution() { result = generated.getMethod() }
|
||||
|
||||
final override Expr getArgument(int n) { result = generated.getArguments().getChild(n) }
|
||||
|
||||
final override Block getBlock() { result = generated.getBlock() }
|
||||
}
|
||||
}
|
||||
|
||||
module YieldCall {
|
||||
class Range extends Call::Range, @yield {
|
||||
final override Generated::Yield generated;
|
||||
|
||||
final override Expr getReceiver() { none() }
|
||||
|
||||
final override string getMethodName() { result = "yield" }
|
||||
|
||||
final override ScopeResolution getMethodScopeResolution() { none() }
|
||||
|
||||
final override Expr getArgument(int n) { result = generated.getChild().getChild(n) }
|
||||
|
||||
final override Block getBlock() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
module BlockArgument {
|
||||
class Range extends Expr::Range, @block_argument {
|
||||
final override Generated::BlockArgument generated;
|
||||
|
||||
final Expr getExpr() { result = generated.getChild() }
|
||||
}
|
||||
}
|
||||
|
||||
module SplatArgument {
|
||||
class Range extends Expr::Range, @splat_argument {
|
||||
final override Generated::SplatArgument generated;
|
||||
|
||||
final Expr getExpr() { result = generated.getChild() }
|
||||
}
|
||||
}
|
||||
|
||||
module HashSplatArgument {
|
||||
class Range extends Expr::Range, @hash_splat_argument {
|
||||
final override Generated::HashSplatArgument generated;
|
||||
|
||||
final Expr getExpr() { result = generated.getChild() }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
private import codeql_ruby.AST
|
||||
private import codeql_ruby.ast.internal.TreeSitter
|
||||
private import codeql_ruby.ast.internal.Variable
|
||||
|
||||
module Expr {
|
||||
abstract class Range extends AstNode { }
|
||||
@@ -16,6 +17,8 @@ module IntegerLiteral {
|
||||
final override Generated::Integer generated;
|
||||
|
||||
final override string getValueText() { result = generated.getValue() }
|
||||
|
||||
final override string toString() { result = this.getValueText() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +27,8 @@ module NilLiteral {
|
||||
final override Generated::Nil generated;
|
||||
|
||||
final override string getValueText() { result = generated.getValue() }
|
||||
|
||||
final override string toString() { result = this.getValueText() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +40,8 @@ module BooleanLiteral {
|
||||
|
||||
final override string getValueText() { result = generated.getValue() }
|
||||
|
||||
final override string toString() { result = this.getValueText() }
|
||||
|
||||
predicate isTrue() { this instanceof @token_true }
|
||||
|
||||
predicate isFalse() { this instanceof @token_false }
|
||||
@@ -47,6 +54,16 @@ module RegexLiteral {
|
||||
final override Generated::Regex generated;
|
||||
|
||||
final override string getValueText() {
|
||||
forall(AstNode n | n = generated.getChild(_) | n instanceof Generated::Token) and
|
||||
result =
|
||||
concat(int i, string s |
|
||||
s = generated.getChild(i).(Generated::Token).getValue()
|
||||
|
|
||||
s order by i
|
||||
)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
result =
|
||||
concat(AstNode c, int i, string s |
|
||||
c = generated.getChild(i) and
|
||||
@@ -60,6 +77,91 @@ module RegexLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: expand this minimal placeholder.
|
||||
module StringLiteral {
|
||||
class Range extends Literal::Range, @string__ {
|
||||
final override Generated::String generated;
|
||||
|
||||
final override string getValueText() {
|
||||
strictcount(generated.getChild(_)) = 1 and
|
||||
result = generated.getChild(0).(Generated::Token).getValue()
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
result =
|
||||
concat(AstNode c, int i, string s |
|
||||
c = generated.getChild(i) and
|
||||
if c instanceof Generated::Token
|
||||
then s = c.(Generated::Token).getValue()
|
||||
else s = "#{...}"
|
||||
|
|
||||
s order by i
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: expand this minimal placeholder.
|
||||
module SymbolLiteral {
|
||||
abstract class Range extends Literal::Range { }
|
||||
|
||||
class SimpleSymbolRange extends SymbolLiteral::Range {
|
||||
final override Generated::SimpleSymbol generated;
|
||||
|
||||
// Tree-sitter gives us value text including the colon, which we skip.
|
||||
final override string getValueText() { result = generated.getValue().suffix(1) }
|
||||
|
||||
final override string toString() { result = generated.getValue() }
|
||||
}
|
||||
|
||||
abstract private class ComplexSymbolRange extends SymbolLiteral::Range {
|
||||
abstract Generated::AstNode getChild(int i);
|
||||
|
||||
final override string getValueText() {
|
||||
strictcount(this.getChild(_)) = 1 and
|
||||
result = this.getChild(0).(Generated::Token).getValue()
|
||||
}
|
||||
|
||||
private string summaryString() {
|
||||
result =
|
||||
concat(AstNode c, int i, string s |
|
||||
c = this.getChild(i) and
|
||||
if c instanceof Generated::Token
|
||||
then s = c.(Generated::Token).getValue()
|
||||
else s = "#{...}"
|
||||
|
|
||||
s order by i
|
||||
)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
if summaryString().regexpMatch("[a-zA-z_][a-zA-Z_0-9]*")
|
||||
then result = ":" + summaryString()
|
||||
else result = ":\"" + summaryString() + "\""
|
||||
}
|
||||
}
|
||||
|
||||
class DelimitedSymbolRange extends ComplexSymbolRange, @delimited_symbol {
|
||||
final override Generated::DelimitedSymbol generated;
|
||||
|
||||
final override Generated::AstNode getChild(int i) { result = generated.getChild(i) }
|
||||
}
|
||||
|
||||
class BareSymbolRange extends ComplexSymbolRange, @bare_symbol {
|
||||
final override Generated::BareSymbol generated;
|
||||
|
||||
final override Generated::AstNode getChild(int i) { result = generated.getChild(i) }
|
||||
}
|
||||
|
||||
class HashKeySymbolRange extends SymbolLiteral::Range, @token_hash_key_symbol {
|
||||
final override Generated::HashKeySymbol generated;
|
||||
|
||||
final override string getValueText() { result = generated.getValue() }
|
||||
|
||||
final override string toString() { result = ":" + this.getValueText() }
|
||||
}
|
||||
}
|
||||
|
||||
module ExprSequence {
|
||||
abstract class Range extends Expr::Range {
|
||||
abstract Expr getExpr(int n);
|
||||
@@ -89,3 +191,23 @@ module DoExpr {
|
||||
final override Expr getExpr(int n) { result = generated.getChild(n) }
|
||||
}
|
||||
}
|
||||
|
||||
module ScopeResolution {
|
||||
class Range extends Expr::Range, @scope_resolution {
|
||||
final override Generated::ScopeResolution generated;
|
||||
|
||||
final Expr getScope() { result = generated.getScope() }
|
||||
|
||||
final string getName() { result = generated.getName().(Generated::Token).getValue() }
|
||||
}
|
||||
}
|
||||
|
||||
module Pair {
|
||||
class Range extends Expr::Range, @pair {
|
||||
final override Generated::Pair generated;
|
||||
|
||||
final Expr getKey() { result = generated.getKey() }
|
||||
|
||||
final Expr getValue() { result = generated.getValue() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ module 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().(SymbolLiteral).getValueText() or
|
||||
result = generated.getName().(Generated::Setter).getName().getValue() + "="
|
||||
}
|
||||
}
|
||||
@@ -30,8 +29,7 @@ module SingletonMethod {
|
||||
|
||||
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().(SymbolLiteral).getValueText() or
|
||||
result = generated.getName().(Generated::Setter).getName().getValue() + "="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,6 +432,20 @@ module Generated {
|
||||
override string getAPrimaryQlClass() { result = "Constant" }
|
||||
}
|
||||
|
||||
class DelimitedSymbol extends @delimited_symbol, AstNode {
|
||||
override string getAPrimaryQlClass() { result = "DelimitedSymbol" }
|
||||
|
||||
override Location getLocation() { delimited_symbol_def(this, _, _, result) }
|
||||
|
||||
AstNode getChild(int i) { delimited_symbol_child(this, i, result) }
|
||||
|
||||
override AstNode getParent() { delimited_symbol_def(this, result, _, _) }
|
||||
|
||||
override int getParentIndex() { delimited_symbol_def(this, _, result, _) }
|
||||
|
||||
override AstNode getAFieldOrChild() { delimited_symbol_child(this, _, result) }
|
||||
}
|
||||
|
||||
class DestructuredLeftAssignment extends @destructured_left_assignment, AstNode {
|
||||
override string getAPrimaryQlClass() { result = "DestructuredLeftAssignment" }
|
||||
|
||||
@@ -658,6 +672,10 @@ module Generated {
|
||||
override AstNode getAFieldOrChild() { hash_child(this, _, result) }
|
||||
}
|
||||
|
||||
class HashKeySymbol extends @token_hash_key_symbol, Token {
|
||||
override string getAPrimaryQlClass() { result = "HashKeySymbol" }
|
||||
}
|
||||
|
||||
class HashSplatArgument extends @hash_splat_argument, AstNode {
|
||||
override string getAPrimaryQlClass() { result = "HashSplatArgument" }
|
||||
|
||||
@@ -1244,6 +1262,10 @@ module Generated {
|
||||
override AstNode getAFieldOrChild() { setter_def(this, _, _, result, _) }
|
||||
}
|
||||
|
||||
class SimpleSymbol extends @token_simple_symbol, Token {
|
||||
override string getAPrimaryQlClass() { result = "SimpleSymbol" }
|
||||
}
|
||||
|
||||
class SingletonClass extends @singleton_class, AstNode {
|
||||
override string getAPrimaryQlClass() { result = "SingletonClass" }
|
||||
|
||||
@@ -1379,20 +1401,6 @@ module Generated {
|
||||
override AstNode getAFieldOrChild() { superclass_def(this, _, _, result, _) }
|
||||
}
|
||||
|
||||
class Symbol extends @symbol, AstNode {
|
||||
override string getAPrimaryQlClass() { result = "Symbol" }
|
||||
|
||||
override Location getLocation() { symbol_def(this, _, _, result) }
|
||||
|
||||
AstNode getChild(int i) { symbol_child(this, i, result) }
|
||||
|
||||
override AstNode getParent() { symbol_def(this, result, _, _) }
|
||||
|
||||
override int getParentIndex() { symbol_def(this, _, result, _) }
|
||||
|
||||
override AstNode getAFieldOrChild() { symbol_child(this, _, result) }
|
||||
}
|
||||
|
||||
class SymbolArray extends @symbol_array, AstNode {
|
||||
override string getAPrimaryQlClass() { result = "SymbolArray" }
|
||||
|
||||
|
||||
@@ -334,13 +334,13 @@ abstract private class PostOrderTree extends ControlFlowTree {
|
||||
|
||||
private class LeftToRightPostOrderNodes =
|
||||
@argument_list or @array or @bare_string or @bare_symbol or @binary or @block_argument or
|
||||
@break or @call or @chained_string or @destructured_left_assignment or
|
||||
@break or @call or @chained_string or @delimited_symbol or @destructured_left_assignment or
|
||||
@destructured_parameter or @element_reference or @exception_variable or @hash or
|
||||
@hash_splat_argument or @heredoc_body or @interpolation or @left_assignment_list or @next or
|
||||
@operator_assignment or @pair or @parenthesized_statements or @range or @redo or @regex or
|
||||
@rest_assignment or @retry or @return or @right_assignment_list or @scope_resolution or
|
||||
@splat_argument or @splat_parameter or @string__ or @string_array or @subshell or
|
||||
@superclass or @symbol or @symbol_array or @unary;
|
||||
@token_simple_symbol or @splat_argument or @splat_parameter or @string__ or @string_array or
|
||||
@subshell or @superclass or @symbol_array or @token_hash_key_symbol or @unary;
|
||||
|
||||
private class LeftToRightPostOrderTree extends StandardPostOrderTree, LeftToRightPostOrderNodes {
|
||||
LeftToRightPostOrderTree() {
|
||||
|
||||
@@ -50,9 +50,9 @@ sourceLocationPrefix(
|
||||
|
||||
@underscore_lhs = @call | @element_reference | @scope_resolution | @token_false | @token_nil | @token_true | @underscore_variable
|
||||
|
||||
@underscore_method_name = @setter | @symbol | @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_operator
|
||||
@underscore_method_name = @delimited_symbol | @setter | @token_class_variable | @token_constant | @token_global_variable | @token_identifier | @token_instance_variable | @token_operator | @token_simple_symbol
|
||||
|
||||
@underscore_primary = @array | @begin | @break | @case__ | @chained_string | @class | @for | @hash | @if | @lambda | @method | @module | @next | @parenthesized_statements | @rational | @redo | @regex | @retry | @return | @singleton_class | @singleton_method | @string__ | @string_array | @subshell | @symbol | @symbol_array | @token_character | @token_complex | @token_float | @token_heredoc_beginning | @token_integer | @unary | @underscore_lhs | @unless | @until | @while | @yield
|
||||
@underscore_primary = @array | @begin | @break | @case__ | @chained_string | @class | @delimited_symbol | @for | @hash | @if | @lambda | @method | @module | @next | @parenthesized_statements | @rational | @redo | @regex | @retry | @return | @singleton_class | @singleton_method | @string__ | @string_array | @subshell | @symbol_array | @token_character | @token_complex | @token_float | @token_heredoc_beginning | @token_integer | @token_simple_symbol | @unary | @underscore_lhs | @unless | @until | @while | @yield
|
||||
|
||||
@underscore_statement = @alias | @assignment | @begin_block | @binary | @break | @call | @end_block | @if_modifier | @next | @operator_assignment | @rescue_modifier | @return | @unary | @undef | @underscore_arg | @unless_modifier | @until_modifier | @while_modifier | @yield
|
||||
|
||||
@@ -396,6 +396,23 @@ conditional_def(
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
@delimited_symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content
|
||||
|
||||
#keyset[delimited_symbol, index]
|
||||
delimited_symbol_child(
|
||||
int delimited_symbol: @delimited_symbol ref,
|
||||
int index: int ref,
|
||||
unique int child: @delimited_symbol_child_type ref
|
||||
);
|
||||
|
||||
#keyset[parent, parent_index]
|
||||
delimited_symbol_def(
|
||||
unique int id: @delimited_symbol,
|
||||
int parent: @ast_node_parent ref,
|
||||
int parent_index: int ref,
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
@destructured_left_assignment_child_type = @destructured_left_assignment | @rest_assignment | @underscore_lhs
|
||||
|
||||
#keyset[destructured_left_assignment, index]
|
||||
@@ -886,7 +903,7 @@ optional_parameter_def(
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
@pair_key_type = @string__ | @symbol | @underscore_arg
|
||||
@pair_key_type = @string__ | @token_hash_key_symbol | @underscore_arg
|
||||
|
||||
#keyset[parent, parent_index]
|
||||
pair_def(
|
||||
@@ -1241,23 +1258,6 @@ superclass_def(
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
@symbol_child_type = @interpolation | @token_escape_sequence | @token_string_content
|
||||
|
||||
#keyset[symbol, index]
|
||||
symbol_child(
|
||||
int symbol: @symbol ref,
|
||||
int index: int ref,
|
||||
unique int child: @symbol_child_type ref
|
||||
);
|
||||
|
||||
#keyset[parent, parent_index]
|
||||
symbol_def(
|
||||
unique int id: @symbol,
|
||||
int parent: @ast_node_parent ref,
|
||||
int parent_index: int ref,
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
#keyset[symbol_array, index]
|
||||
symbol_array_child(
|
||||
int symbol_array: @symbol_array ref,
|
||||
@@ -1461,23 +1461,25 @@ case @token.kind of
|
||||
| 8 = @token_false
|
||||
| 9 = @token_float
|
||||
| 10 = @token_global_variable
|
||||
| 11 = @token_heredoc_beginning
|
||||
| 12 = @token_heredoc_content
|
||||
| 13 = @token_heredoc_end
|
||||
| 14 = @token_identifier
|
||||
| 15 = @token_instance_variable
|
||||
| 16 = @token_integer
|
||||
| 17 = @token_nil
|
||||
| 18 = @token_operator
|
||||
| 19 = @token_self
|
||||
| 20 = @token_string_content
|
||||
| 21 = @token_super
|
||||
| 22 = @token_true
|
||||
| 23 = @token_uninterpreted
|
||||
| 11 = @token_hash_key_symbol
|
||||
| 12 = @token_heredoc_beginning
|
||||
| 13 = @token_heredoc_content
|
||||
| 14 = @token_heredoc_end
|
||||
| 15 = @token_identifier
|
||||
| 16 = @token_instance_variable
|
||||
| 17 = @token_integer
|
||||
| 18 = @token_nil
|
||||
| 19 = @token_operator
|
||||
| 20 = @token_self
|
||||
| 21 = @token_simple_symbol
|
||||
| 22 = @token_string_content
|
||||
| 23 = @token_super
|
||||
| 24 = @token_true
|
||||
| 25 = @token_uninterpreted
|
||||
;
|
||||
|
||||
|
||||
@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_parameters | @module | @next | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol | @symbol_array | @then | @token | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield
|
||||
@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @delimited_symbol | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_parameters | @module | @next | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol_array | @then | @token | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield
|
||||
|
||||
@ast_node_parent = @ast_node | @file
|
||||
|
||||
|
||||
10
ql/test/library-tests/ast/calls/arguments.expected
Normal file
10
ql/test/library-tests/ast/calls/arguments.expected
Normal file
@@ -0,0 +1,10 @@
|
||||
blockArguments
|
||||
| calls.rb:201:5:201:8 | &... | calls.rb:201:6:201:8 | call to bar |
|
||||
splatArguments
|
||||
| calls.rb:204:5:204:8 | *... | calls.rb:204:6:204:8 | call to bar |
|
||||
hashSplatArguments
|
||||
| calls.rb:207:5:207:9 | **... | calls.rb:207:7:207:9 | call to bar |
|
||||
keywordArguments
|
||||
| calls.rb:210:5:210:13 | Pair | calls.rb:210:5:210:8 | :blah | calls.rb:210:11:210:13 | call to bar |
|
||||
keywordArgumentsByKeyword
|
||||
| calls.rb:210:1:210:14 | call to foo | blah | calls.rb:210:11:210:13 | call to bar |
|
||||
15
ql/test/library-tests/ast/calls/arguments.ql
Normal file
15
ql/test/library-tests/ast/calls/arguments.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
import ruby
|
||||
|
||||
query predicate blockArguments(BlockArgument a, Expr e) { e = a.getExpr() }
|
||||
|
||||
query predicate splatArguments(SplatArgument a, Expr e) { e = a.getExpr() }
|
||||
|
||||
query predicate hashSplatArguments(HashSplatArgument a, Expr e) { e = a.getExpr() }
|
||||
|
||||
query predicate keywordArguments(Pair a, Expr key, Expr value) {
|
||||
exists(Call c | c.getAnArgument() = a and key = a.getKey() and value = a.getValue())
|
||||
}
|
||||
|
||||
query predicate keywordArgumentsByKeyword(Call c, string keyword, Expr value) {
|
||||
c.getKeywordArgument(keyword) = value
|
||||
}
|
||||
101
ql/test/library-tests/ast/calls/calls.expected
Normal file
101
ql/test/library-tests/ast/calls/calls.expected
Normal file
@@ -0,0 +1,101 @@
|
||||
callsWithNoReceiverArgumentsOrBlock
|
||||
| calls.rb:2:1:2:5 | call to foo | foo |
|
||||
| calls.rb:5:1:5:10 | call to bar | bar |
|
||||
| calls.rb:28:3:28:7 | call to yield | yield |
|
||||
| calls.rb:42:1:42:3 | call to foo | foo |
|
||||
| calls.rb:45:2:45:4 | call to foo | foo |
|
||||
| calls.rb:48:11:48:13 | call to foo | foo |
|
||||
| calls.rb:51:2:51:4 | call to foo | foo |
|
||||
| calls.rb:54:8:54:10 | call to foo | foo |
|
||||
| calls.rb:57:9:57:11 | call to bar | bar |
|
||||
| calls.rb:60:8:60:10 | call to foo | foo |
|
||||
| calls.rb:60:13:60:15 | call to bar | bar |
|
||||
| calls.rb:64:3:64:5 | call to foo | foo |
|
||||
| calls.rb:68:9:68:11 | call to foo | foo |
|
||||
| calls.rb:71:7:71:9 | call to foo | foo |
|
||||
| calls.rb:74:1:74:3 | call to foo | foo |
|
||||
| calls.rb:74:7:74:9 | call to bar | bar |
|
||||
| calls.rb:77:2:77:4 | call to foo | foo |
|
||||
| calls.rb:80:1:80:13 | call to foo | foo |
|
||||
| calls.rb:80:9:80:11 | call to bar | bar |
|
||||
| calls.rb:83:1:83:16 | call to foo | foo |
|
||||
| calls.rb:83:10:83:12 | call to bar | bar |
|
||||
| calls.rb:86:1:86:3 | call to foo | foo |
|
||||
| calls.rb:90:6:90:8 | call to foo | foo |
|
||||
| calls.rb:91:6:91:8 | call to bar | bar |
|
||||
| calls.rb:92:3:92:5 | call to baz | baz |
|
||||
| calls.rb:97:3:97:5 | call to foo | foo |
|
||||
| calls.rb:101:17:101:19 | call to foo | foo |
|
||||
| calls.rb:105:10:105:12 | call to foo | foo |
|
||||
| calls.rb:106:3:106:5 | call to bar | bar |
|
||||
| calls.rb:111:3:111:5 | call to foo | foo |
|
||||
| calls.rb:115:5:115:7 | call to foo | foo |
|
||||
| calls.rb:116:3:116:5 | call to bar | bar |
|
||||
| calls.rb:120:40:120:42 | call to foo | foo |
|
||||
| calls.rb:124:40:124:42 | call to foo | foo |
|
||||
| calls.rb:129:3:129:5 | call to foo | foo |
|
||||
| calls.rb:133:1:133:3 | call to foo | foo |
|
||||
| calls.rb:133:7:133:9 | call to bar | bar |
|
||||
| calls.rb:133:13:133:15 | call to baz | baz |
|
||||
| calls.rb:136:4:136:6 | call to foo | foo |
|
||||
| calls.rb:137:3:137:8 | call to wibble | wibble |
|
||||
| calls.rb:138:7:138:9 | call to bar | bar |
|
||||
| calls.rb:139:3:139:8 | call to wobble | wobble |
|
||||
| calls.rb:141:3:141:8 | call to wabble | wabble |
|
||||
| calls.rb:145:1:145:3 | call to bar | bar |
|
||||
| calls.rb:145:8:145:10 | call to foo | foo |
|
||||
| calls.rb:148:8:148:10 | call to foo | foo |
|
||||
| calls.rb:149:3:149:5 | call to bar | bar |
|
||||
| calls.rb:153:1:153:3 | call to bar | bar |
|
||||
| calls.rb:153:12:153:14 | call to foo | foo |
|
||||
| calls.rb:156:7:156:9 | call to foo | foo |
|
||||
| calls.rb:157:3:157:5 | call to bar | bar |
|
||||
| calls.rb:161:1:161:3 | call to bar | bar |
|
||||
| calls.rb:161:11:161:13 | call to foo | foo |
|
||||
| calls.rb:164:7:164:9 | call to foo | foo |
|
||||
| calls.rb:165:3:165:5 | call to bar | bar |
|
||||
| calls.rb:169:1:169:3 | call to bar | bar |
|
||||
| calls.rb:169:11:169:13 | call to foo | foo |
|
||||
| calls.rb:172:10:172:12 | call to bar | bar |
|
||||
| calls.rb:173:3:173:5 | call to baz | baz |
|
||||
| calls.rb:177:1:177:3 | call to foo | foo |
|
||||
| calls.rb:177:5:177:7 | call to bar | bar |
|
||||
| calls.rb:180:8:180:10 | call to bar | bar |
|
||||
| calls.rb:183:1:183:3 | call to foo | foo |
|
||||
| calls.rb:186:1:186:3 | call to foo | foo |
|
||||
| calls.rb:186:6:186:8 | call to bar | bar |
|
||||
| calls.rb:189:3:189:5 | call to foo | foo |
|
||||
| calls.rb:189:10:189:12 | call to bar | bar |
|
||||
| calls.rb:193:8:193:10 | call to foo | foo |
|
||||
| calls.rb:194:8:194:10 | call to bar | bar |
|
||||
| calls.rb:198:1:198:3 | call to foo | foo |
|
||||
| calls.rb:198:12:198:14 | call to bar | bar |
|
||||
| calls.rb:201:6:201:8 | call to bar | bar |
|
||||
| calls.rb:204:6:204:8 | call to bar | bar |
|
||||
| calls.rb:207:7:207:9 | call to bar | bar |
|
||||
| calls.rb:210:11:210:13 | call to bar | bar |
|
||||
callsWithScopeResolutionName
|
||||
| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:8 | ...::bar |
|
||||
callsWithArguments
|
||||
| calls.rb:11:1:11:11 | call to foo | foo | 0 | calls.rb:11:5:11:5 | 0 |
|
||||
| calls.rb:11:1:11:11 | call to foo | foo | 1 | calls.rb:11:8:11:8 | 1 |
|
||||
| calls.rb:11:1:11:11 | call to foo | foo | 2 | calls.rb:11:11:11:11 | 2 |
|
||||
| calls.rb:22:1:24:3 | call to bar | bar | 0 | calls.rb:22:9:22:13 | foo |
|
||||
| calls.rb:33:3:33:16 | call to yield | yield | 0 | calls.rb:33:9:33:11 | 100 |
|
||||
| calls.rb:33:3:33:16 | call to yield | yield | 1 | calls.rb:33:14:33:16 | 200 |
|
||||
| calls.rb:48:1:48:14 | call to some_func | some_func | 0 | calls.rb:48:11:48:13 | call to foo |
|
||||
| calls.rb:201:1:201:9 | call to foo | foo | 0 | calls.rb:201:5:201:8 | &... |
|
||||
| calls.rb:204:1:204:9 | call to foo | foo | 0 | calls.rb:204:5:204:8 | *... |
|
||||
| calls.rb:207:1:207:10 | call to foo | foo | 0 | calls.rb:207:5:207:9 | **... |
|
||||
| calls.rb:210:1:210:14 | call to foo | foo | 0 | calls.rb:210:5:210:13 | Pair |
|
||||
callsWithReceiver
|
||||
| calls.rb:8:1:8:7 | call to bar | calls.rb:8:1:8:3 | 123 |
|
||||
| calls.rb:22:1:24:3 | call to bar | calls.rb:22:1:22:3 | 123 |
|
||||
| calls.rb:86:1:86:9 | call to bar | calls.rb:86:1:86:3 | call to foo |
|
||||
callsWithBlock
|
||||
| calls.rb:14:1:14:17 | call to foo | calls.rb:14:5:14:17 | { ... } |
|
||||
| calls.rb:17:1:19:3 | call to foo | calls.rb:17:5:19:3 | do ... end |
|
||||
| calls.rb:22:1:24:3 | call to bar | calls.rb:22:16:24:3 | do ... end |
|
||||
yieldCalls
|
||||
| calls.rb:28:3:28:7 | call to yield |
|
||||
| calls.rb:33:3:33:16 | call to yield |
|
||||
24
ql/test/library-tests/ast/calls/calls.ql
Normal file
24
ql/test/library-tests/ast/calls/calls.ql
Normal file
@@ -0,0 +1,24 @@
|
||||
import ruby
|
||||
import codeql_ruby.ast.internal.TreeSitter
|
||||
|
||||
query predicate callsWithNoReceiverArgumentsOrBlock(Call c, string name) {
|
||||
name = c.getMethodName() and
|
||||
not exists(c.getReceiver()) and
|
||||
not exists(c.getAnArgument()) and
|
||||
not exists(c.getBlock())
|
||||
}
|
||||
|
||||
query predicate callsWithScopeResolutionName(Call c, ScopeResolution sr) {
|
||||
sr = c.getMethodScopeResolution()
|
||||
}
|
||||
|
||||
query predicate callsWithArguments(Call c, string name, int n, Expr argN) {
|
||||
name = c.getMethodName() and
|
||||
argN = c.getArgument(n)
|
||||
}
|
||||
|
||||
query predicate callsWithReceiver(Call c, Expr rcv) { rcv = c.getReceiver() }
|
||||
|
||||
query predicate callsWithBlock(Call c, Block b) { b = c.getBlock() }
|
||||
|
||||
query predicate yieldCalls(YieldCall c) { any() }
|
||||
210
ql/test/library-tests/ast/calls/calls.rb
Normal file
210
ql/test/library-tests/ast/calls/calls.rb
Normal file
@@ -0,0 +1,210 @@
|
||||
# call with no receiver, arguments, or block
|
||||
foo()
|
||||
|
||||
# call whose name is a scope resolution
|
||||
Foo::bar()
|
||||
|
||||
# call with a receiver, no arguments or block
|
||||
123.bar
|
||||
|
||||
# call with arguments
|
||||
foo 0, 1, 2
|
||||
|
||||
# call with curly brace block
|
||||
foo { |x| x + 1 }
|
||||
|
||||
# call with do block
|
||||
foo do |x|
|
||||
x + 1
|
||||
end
|
||||
|
||||
# call with receiver, arguments, and a block
|
||||
123.bar('foo') do |x|
|
||||
x + 1
|
||||
end
|
||||
|
||||
# a yield call
|
||||
def method_that_yields
|
||||
yield
|
||||
end
|
||||
|
||||
# a yield call with arguments
|
||||
def another_method_that_yields
|
||||
yield 100, 200
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Calls without parentheses or arguments are parsed by tree-sitter simply as
|
||||
# `identifier` nodes, so here we test that our AST library correctly represents
|
||||
# them as calls in all the following contexts.
|
||||
|
||||
# root level (child of program)
|
||||
foo
|
||||
|
||||
# in a parenthesized statement
|
||||
(foo)
|
||||
|
||||
# in an argument list
|
||||
some_func(foo)
|
||||
|
||||
# in an array
|
||||
[foo]
|
||||
|
||||
# RHS of an assignment
|
||||
var1 = foo
|
||||
|
||||
# RHS an operator assignment
|
||||
var1 += bar
|
||||
|
||||
# RHS assignment list
|
||||
var1 = foo, bar
|
||||
|
||||
# in a begin-end block
|
||||
begin
|
||||
foo
|
||||
end
|
||||
|
||||
# in a BEGIN block
|
||||
BEGIN { foo }
|
||||
|
||||
# in an END block
|
||||
END { foo }
|
||||
|
||||
# both operands of a binary operation
|
||||
foo + bar
|
||||
|
||||
# unary operand
|
||||
!foo
|
||||
|
||||
# in a curly brace block
|
||||
foo() { bar }
|
||||
|
||||
# in a do-end block
|
||||
foo() do bar end
|
||||
|
||||
# the receiver in a call can itself be a call
|
||||
foo.bar()
|
||||
|
||||
# the value for a case expr
|
||||
# and the when pattern and body
|
||||
case foo
|
||||
when bar
|
||||
baz
|
||||
end
|
||||
|
||||
# in a class definition
|
||||
class MyClass
|
||||
foo
|
||||
end
|
||||
|
||||
# in a superclass
|
||||
class MyClass < foo
|
||||
end
|
||||
|
||||
# in a singleton class value or body
|
||||
class << foo
|
||||
bar
|
||||
end
|
||||
|
||||
# in a method body
|
||||
def some_method
|
||||
foo
|
||||
end
|
||||
|
||||
# in a singleton method object or body
|
||||
def foo.some_method
|
||||
bar
|
||||
end
|
||||
|
||||
# in the default value for a keyword parameter
|
||||
def method_with_keyword_param(keyword: foo)
|
||||
end
|
||||
|
||||
# in the default value for an optional parameter
|
||||
def method_with_optional_param(param = foo)
|
||||
end
|
||||
|
||||
# in a module
|
||||
module SomeModule
|
||||
foo
|
||||
end
|
||||
|
||||
# ternary if: condition, consequence, and alternative can all be calls
|
||||
foo ? bar : baz
|
||||
|
||||
# if/elsif/else conditions and bodies
|
||||
if foo
|
||||
wibble
|
||||
elsif bar
|
||||
wobble
|
||||
else
|
||||
wabble
|
||||
end
|
||||
|
||||
# if-modifier condition/body
|
||||
bar if foo
|
||||
|
||||
# unless condition/body
|
||||
unless foo
|
||||
bar
|
||||
end
|
||||
|
||||
# unless-modifier condition/body
|
||||
bar unless foo
|
||||
|
||||
# while loop condition/body
|
||||
while foo do
|
||||
bar
|
||||
end
|
||||
|
||||
# while-modifier loop condition/body
|
||||
bar while foo
|
||||
|
||||
# until loop condition/body
|
||||
until foo do
|
||||
bar
|
||||
end
|
||||
|
||||
# until-modifier loop condition/body
|
||||
bar until foo
|
||||
|
||||
# the collection being iterated over in a for loop, and the body
|
||||
for x in bar
|
||||
baz
|
||||
end
|
||||
|
||||
# in an array indexing operation, both the object and the index can be calls
|
||||
foo[bar]
|
||||
|
||||
# interpolation
|
||||
"foo-#{bar}"
|
||||
|
||||
# the scope in a scope resolution
|
||||
foo::Bar
|
||||
|
||||
# in a range
|
||||
foo..bar
|
||||
|
||||
# the key/value in a hash pair
|
||||
{ foo => bar }
|
||||
|
||||
# rescue exceptions and ensure
|
||||
begin
|
||||
rescue foo
|
||||
ensure bar
|
||||
end
|
||||
|
||||
# rescue-modifier body and handler
|
||||
foo rescue bar
|
||||
|
||||
# block argument
|
||||
foo(&bar)
|
||||
|
||||
# splat argument
|
||||
foo(*bar)
|
||||
|
||||
# hash-splat argument
|
||||
foo(**bar)
|
||||
|
||||
# the value in a keyword argument
|
||||
foo(blah: bar)
|
||||
@@ -84,16 +84,16 @@ paramsInMethods
|
||||
| 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 | SimpleParameter |
|
||||
| params.rb:9:11:11:3 | \| ... \| | 1 | params.rb:9:20:9:24 | value | SimpleParameter |
|
||||
| params.rb:9:11:11:3 | do ... end | 0 | params.rb:9:15:9:17 | key | SimpleParameter |
|
||||
| params.rb:9:11:11:3 | do ... end | 1 | params.rb:9:20:9:24 | value | SimpleParameter |
|
||||
| params.rb:22:12:22:32 | { ... } | 0 | params.rb:22:15:22:20 | (..., ...) | TuplePatternParameter |
|
||||
| params.rb:34:12:35:3 | \| ... \| | 0 | params.rb:34:16:34:18 | val | SimpleParameter |
|
||||
| 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 | SimpleParameter |
|
||||
| params.rb:65:25:67:3 | \| ... \| | 1 | params.rb:65:35:65:42 | age | OptionalParameter |
|
||||
| params.rb:34:12:35:3 | do ... end | 0 | params.rb:34:16:34:18 | val | SimpleParameter |
|
||||
| params.rb:34:12:35:3 | do ... end | 1 | params.rb:34:21:34:26 | *splat | SplatParameter |
|
||||
| params.rb:34:12:35:3 | do ... end | 2 | params.rb:34:29:34:42 | **double_splat | HashSplatParameter |
|
||||
| params.rb:49:24:51:3 | do ... end | 0 | params.rb:49:28:49:30 | xx | KeywordParameter |
|
||||
| params.rb:49:24:51:3 | do ... end | 1 | params.rb:49:33:49:39 | yy | KeywordParameter |
|
||||
| params.rb:65:25:67:3 | do ... end | 0 | params.rb:65:29:65:32 | name | SimpleParameter |
|
||||
| params.rb:65:25:67:3 | do ... end | 1 | params.rb:65:35:65:42 | age | OptionalParameter |
|
||||
paramsInLambdas
|
||||
| params.rb:14:7:14:33 | -> { ... } | 0 | params.rb:14:11:14:13 | foo | SimpleParameter |
|
||||
| params.rb:14:7:14:33 | -> { ... } | 1 | params.rb:14:16:14:18 | bar | SimpleParameter |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -94,7 +94,7 @@ end
|
||||
|
||||
$global = 42
|
||||
|
||||
map1 = { 'a' => 'b', 'c': 'd', }
|
||||
map1 = { 'a' => 'b', 'c': 'd', e: 'f' }
|
||||
map2 = { **map1, 'x' => 'y', **map1}
|
||||
|
||||
|
||||
@@ -158,6 +158,8 @@ scriptfile = `cat "#{__FILE__}"`
|
||||
|
||||
symbol = :hello
|
||||
|
||||
delimited_symbol = :"goodbye-#{ 12 + 13 }"
|
||||
|
||||
x = true
|
||||
x = ! true
|
||||
x = - 42
|
||||
|
||||
Reference in New Issue
Block a user