mirror of
https://github.com/github/codeql.git
synced 2026-02-23 02:13:41 +01:00
Add AST classes and tests for method calls
This commit is contained in:
173
ql/src/codeql_ruby/ast/Call.qll
Normal file
173
ql/src/codeql_ruby/ast/Call.qll
Normal file
@@ -0,0 +1,173 @@
|
||||
private import codeql_ruby.AST
|
||||
private import internal.Call
|
||||
|
||||
/**
|
||||
* A method 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 the 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 method name if it is a `ScopeResolution`.
|
||||
*/
|
||||
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 = 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" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
208
ql/src/codeql_ruby/ast/internal/Call.qll
Normal file
208
ql/src/codeql_ruby/ast/internal/Call.qll
Normal file
@@ -0,0 +1,208 @@
|
||||
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) {
|
||||
exists(Generated::ArgumentList x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Array x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Assignment x | x.getRight() = i)
|
||||
or
|
||||
exists(Generated::Begin x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::BeginBlock x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Binary x | x.getLeft() = i or x.getRight() = i)
|
||||
or
|
||||
exists(Generated::Block x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::BlockArgument x | x.getChild() = i)
|
||||
or
|
||||
exists(Generated::Call x | x.getReceiver() = i)
|
||||
or
|
||||
exists(Generated::Case x | x.getValue() = i)
|
||||
or
|
||||
exists(Generated::Class x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Conditional x |
|
||||
x.getCondition() = i or x.getConsequence() = i or x.getAlternative() = i
|
||||
)
|
||||
or
|
||||
exists(Generated::Do x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::DoBlock x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::ElementReference x | x.getChild(_) = i or x.getObject() = i)
|
||||
or
|
||||
exists(Generated::Else x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Elsif x | x.getCondition() = i)
|
||||
or
|
||||
exists(Generated::EndBlock x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Ensure x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Exceptions x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::HashSplatArgument x | x.getChild() = i)
|
||||
or
|
||||
exists(Generated::If x | x.getCondition() = i)
|
||||
or
|
||||
exists(Generated::IfModifier x | x.getCondition() = i or x.getBody() = i)
|
||||
or
|
||||
exists(Generated::In x | x.getChild() = i)
|
||||
or
|
||||
exists(Generated::Interpolation x | x.getChild() = i)
|
||||
or
|
||||
exists(Generated::KeywordParameter x | x.getValue() = i)
|
||||
or
|
||||
exists(Generated::Method x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Module x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::OperatorAssignment x | x.getRight() = i)
|
||||
or
|
||||
exists(Generated::OptionalParameter x | x.getValue() = i)
|
||||
or
|
||||
exists(Generated::Pair x | x.getKey() = i or x.getValue() = i)
|
||||
or
|
||||
exists(Generated::ParenthesizedStatements x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Pattern x | x.getChild() = i)
|
||||
or
|
||||
exists(Generated::Program x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Range x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::RescueModifier x | x.getBody() = i or x.getHandler() = i)
|
||||
or
|
||||
exists(Generated::RightAssignmentList x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::ScopeResolution x | x.getScope() = i)
|
||||
or
|
||||
exists(Generated::SingletonClass x | x.getValue() = i or x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::SingletonMethod x | x.getChild(_) = i or x.getObject() = i)
|
||||
or
|
||||
exists(Generated::SplatArgument x | x.getChild() = i)
|
||||
or
|
||||
exists(Generated::Superclass x | x.getChild() = i)
|
||||
or
|
||||
exists(Generated::Then x | x.getChild(_) = i)
|
||||
or
|
||||
exists(Generated::Unary x | x.getOperand() = i)
|
||||
or
|
||||
exists(Generated::Unless x | x.getCondition() = i)
|
||||
or
|
||||
exists(Generated::UnlessModifier x | x.getCondition() = i or x.getBody() = i)
|
||||
or
|
||||
exists(Generated::Until x | x.getCondition() = i)
|
||||
or
|
||||
exists(Generated::UntilModifier x | x.getCondition() = i or x.getBody() = i)
|
||||
or
|
||||
exists(Generated::While x | x.getCondition() = i)
|
||||
or
|
||||
exists(Generated::WhileModifier x | x.getCondition() = i or x.getBody() = i)
|
||||
}
|
||||
|
||||
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 = 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 = getValueText() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +27,8 @@ module NilLiteral {
|
||||
final override Generated::Nil generated;
|
||||
|
||||
final override string getValueText() { result = generated.getValue() }
|
||||
|
||||
final override string toString() { result = getValueText() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +40,8 @@ module BooleanLiteral {
|
||||
|
||||
final override string getValueText() { result = generated.getValue() }
|
||||
|
||||
final override string toString() { result = 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,109 @@ module RegexLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: expand this minimal placeholder.
|
||||
module StringLiteral {
|
||||
class Range extends Literal::Range, @string__ {
|
||||
final override Generated::String generated;
|
||||
|
||||
final override string getValueText() {
|
||||
count(generated.getChild(_)) = 1 and
|
||||
generated.getChild(0) instanceof Generated::Token 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() }
|
||||
}
|
||||
|
||||
class DelimitedSymbolRange extends SymbolLiteral::Range, @delimited_symbol {
|
||||
final override Generated::DelimitedSymbol generated;
|
||||
|
||||
final override string getValueText() {
|
||||
count(generated.getChild(_)) = 1 and
|
||||
generated.getChild(0) instanceof Generated::Token and
|
||||
result = generated.getChild(0).(Generated::Token).getValue()
|
||||
}
|
||||
|
||||
private string summaryString() {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
if summaryString().regexpMatch("[a-zA-z_][a-zA-Z_0-9]*")
|
||||
then result = ":" + summaryString()
|
||||
else result = ":\"" + summaryString() + "\""
|
||||
}
|
||||
}
|
||||
|
||||
class BareSymbolRange extends SymbolLiteral::Range, @bare_symbol {
|
||||
final override Generated::BareSymbol generated;
|
||||
|
||||
final override string getValueText() {
|
||||
count(generated.getChild(_)) = 1 and
|
||||
generated.getChild(0) instanceof Generated::Token and
|
||||
result = generated.getChild(0).(Generated::Token).getValue()
|
||||
}
|
||||
|
||||
private string summaryString() {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
if summaryString().regexpMatch("[a-zA-z_][a-zA-Z_0-9]*")
|
||||
then result = ":" + summaryString()
|
||||
else result = ":\"" + summaryString() + "\""
|
||||
}
|
||||
}
|
||||
|
||||
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 = ":" + getValueText() }
|
||||
}
|
||||
}
|
||||
|
||||
module ExprSequence {
|
||||
abstract class Range extends Expr::Range {
|
||||
abstract Expr getExpr(int n);
|
||||
@@ -89,3 +209,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" }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user