Add and expand AST classes for literals

This commit is contained in:
Nick Rolfe
2021-02-12 17:36:28 +00:00
parent eee12eecc9
commit f56f81f555
15 changed files with 1858 additions and 334 deletions

View File

@@ -3,6 +3,7 @@ import ast.Call
import ast.Control
import ast.Constant
import ast.Expr
import ast.Literal
import ast.Method
import ast.Module
import ast.Parameter

View File

@@ -24,115 +24,7 @@ class Self extends Expr, @token_self {
final override string getAPrimaryQlClass() { result = "Self" }
}
/**
* A literal.
*
* This is the QL root class for all literals.
*/
class Literal extends Expr {
override Literal::Range range;
/** Gets the source text for this literal, if it is constant. */
final string getValueText() { result = range.getValueText() }
}
/**
* An integer literal.
* ```rb
* x = 123
* y = 0xff
* ```
*/
class IntegerLiteral extends Literal, @token_integer {
final override IntegerLiteral::Range range;
final override string getAPrimaryQlClass() { result = "IntegerLiteral" }
}
/** A `nil` literal. */
class NilLiteral extends Literal, @token_nil {
final override NilLiteral::Range range;
final override string getAPrimaryQlClass() { result = "NilLiteral" }
}
/**
* A Boolean literal.
* ```rb
* true
* false
* TRUE
* FALSE
* ```
*/
class BooleanLiteral extends Literal, BooleanLiteral::DbUnion {
final override BooleanLiteral::Range range;
final override string getAPrimaryQlClass() { result = "BooleanLiteral" }
/** Holds if the Boolean literal is `true` or `TRUE`. */
predicate isTrue() { range.isTrue() }
/** Holds if the Boolean literal is `false` or `FALSE`. */
predicate isFalse() { range.isFalse() }
}
// TODO: expand this. It's a minimal placeholder so we can test `=~` and `!~`.
class RegexLiteral extends Literal, @regex {
final override RegexLiteral::Range range;
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;
SymbolLiteral() {
not any(UndefStmt u).getAMethodName() = this and
not any(AliasStmt a).getNewName() = this and
not any(AliasStmt a).getOldName() = this
}
final override string getAPrimaryQlClass() { result = "SymbolLiteral" }
}
/**
* A method name literal. For example:
* ```rb
* - method_name # a normal name
* - + # an operator
* - :method_name # a symbol
* - :"eval_#{name}" # a complex symbol
* ```
*/
class MethodName extends Literal {
final override MethodName::Range range;
final override string getAPrimaryQlClass() { result = "MethodName" }
}
/** A sequence of expressions. */
class StmtSequence extends Expr {
@@ -322,3 +214,52 @@ class RescueModifierExpr extends Expr, @rescue_modifier {
*/
final Stmt getHandler() { result = range.getHandler() }
}
/**
* A concatenation of string literals.
*
* ```rb
* "foo" "bar" "baz"
* ```
*/
class StringConcatenation extends Expr, @chained_string {
final override StringConcatenation::Range range;
final override string getAPrimaryQlClass() { result = "StringConcatenation" }
/** Gets the `n`th string literal in this concatenation. */
final StringLiteral getString(int n) { result = range.getString(n) }
/** Gets a string literal in this concatenation. */
final StringLiteral getAString() { result = this.getString(_) }
/** Gets the number of string literals in this concatenation. */
final int getNumberOfStrings() { result = count(this.getString(_)) }
/**
* Gets the result of concatenating all the string literals, if and only if
* they do not contain any interpolations.
*
* For the following example, the result is `"foobar"`:
*
* ```rb
* "foo" 'bar'
* ```
*
* And for the following example, where one of the string literals includes
* an interpolation, there is no result:
*
* ```rb
* "foo" "bar#{ n }"
* ```
*/
final string getConcatenatedValueText() {
forall(StringLiteral c | c = this.getString(_) | exists(c.getValueText())) and
result =
concat(string valueText, int i |
valueText = this.getString(i).getValueText()
|
valueText order by i
)
}
}

View File

@@ -0,0 +1,410 @@
private import codeql_ruby.AST
private import internal.Literal
/**
* A literal.
*
* This is the QL root class for all literals.
*/
class Literal extends Expr {
override Literal::Range range;
/**
* Gets the source text for this literal, if this is a simple literal.
*
* For complex literals, such as arrays, hashes, and strings with
* interpolations, this predicate has no result.
*/
final string getValueText() { result = range.getValueText() }
}
/**
* A numeric literal, i.e. an integer, floating-point, rational, or complex
* value.
*
* ```rb
* 123
* 0xff
* 3.14159
* 1.0E2
* 7r
* 1i
* ```
*/
class NumericLiteral extends Literal {
override NumericLiteral::Range range;
}
/**
* An integer literal.
*
* ```rb
* 123
* 0xff
* ```
*/
class IntegerLiteral extends NumericLiteral, @token_integer {
final override IntegerLiteral::Range range;
final override string getAPrimaryQlClass() { result = "IntegerLiteral" }
}
/**
* A floating-point literal.
*
* ```rb
* 1.3
* 2.7e+5
* ```
*/
class FloatLiteral extends NumericLiteral, @token_float {
final override FloatLiteral::Range range;
final override string getAPrimaryQlClass() { result = "FloatLiteral" }
}
/**
* A rational literal.
*
* ```rb
* 123r
* ```
*/
class RationalLiteral extends NumericLiteral, @rational {
final override RationalLiteral::Range range;
final override string getAPrimaryQlClass() { result = "RationalLiteral" }
}
/**
* A complex literal.
*
* ```rb
* 1i
* ```
*/
class ComplexLiteral extends NumericLiteral, @token_complex {
final override ComplexLiteral::Range range;
final override string getAPrimaryQlClass() { result = "ComplexLiteral" }
}
/** A `nil` literal. */
class NilLiteral extends Literal, @token_nil {
final override NilLiteral::Range range;
final override string getAPrimaryQlClass() { result = "NilLiteral" }
}
/**
* A Boolean literal.
* ```rb
* true
* false
* TRUE
* FALSE
* ```
*/
class BooleanLiteral extends Literal, BooleanLiteral::DbUnion {
final override BooleanLiteral::Range range;
final override string getAPrimaryQlClass() { result = "BooleanLiteral" }
/** Holds if the Boolean literal is `true` or `TRUE`. */
predicate isTrue() { range.isTrue() }
/** Holds if the Boolean literal is `false` or `FALSE`. */
predicate isFalse() { range.isFalse() }
}
/**
* The base class for a component of a string: `StringTextComponent`,
* `StringEscapeSequenceComponent`, or `StringInterpolationComponent`.
*/
class StringComponent extends AstNode {
override StringComponent::Range range;
/**
* Gets the source text for this string component. Has no result if this is
* a `StringInterpolationComponent`.
*/
final string getValueText() { result = range.getValueText() }
}
/**
* A component of a string (or string-like) literal that is simply text.
*
* For example, the following string literals all contain `StringTextComponent`
* components whose `getValueText()` returns `"foo"`:
*
* ```rb
* 'foo'
* "#{ bar() }foo"
* "foo#{ bar() } baz"
* ```
*/
class StringTextComponent extends StringComponent, @token_string_content {
final override StringTextComponent::Range range;
final override string getAPrimaryQlClass() { result = "StringTextComponent" }
}
/**
* An escape sequence component of a string or string-like literal.
*/
class StringEscapeSequenceComponent extends StringComponent, @token_escape_sequence {
final override StringEscapeSequenceComponent::Range range;
final override string getAPrimaryQlClass() { result = "StringEscapeSequenceComponent" }
}
/**
* An interpolation expression component of a string or string-like literal.
*/
class StringInterpolationComponent extends StringComponent, StmtSequence, @interpolation {
final override StringInterpolationComponent::Range range;
final override string getAPrimaryQlClass() { result = "StringInterpolationComponent" }
}
/**
* A string, symbol, regex, or subshell literal.
*/
class StringlikeLiteral extends Literal {
override StringlikeLiteral::Range range;
/**
* Gets the `n`th component of this string or string-like literal. The result
* will be one of `StringTextComponent`, `StringInterpolationComponent`, and
* `StringEscapeSequenceComponent`.
*
* In the following example, the result for `n = 0` is the
* `StringTextComponent` for `foo_`, and the result for `n = 1` is the
* `StringInterpolationComponent` for `Time.now`.
*
* ```rb
* "foo_#{ Time.now }"
* ```
*/
final StringComponent getComponent(int n) { result = range.getComponent(n) }
/**
* Gets the number of components in this string or string-like literal.
*
* For the empty string `""`, the result is 0.
*
* For the string `"foo"`, the result is 1: there is a single
* `StringTextComponent`.
*
* For the following example, the result is 3: there is a
* `StringTextComponent` for the substring `"foo_"`; a
* `StringEscapeSequenceComponent` for the escaped quote; and a
* `StringInterpolationComponent` for the interpolation.
*
* ```rb
* "foo\"#{bar}"
* ```
*/
final int getNumberOfComponents() { result = count(this.getComponent(_)) }
}
/**
* A string literal.
*
* ```rb
* 'hello'
* "hello, #{name}"
* ```
*/
class StringLiteral extends StringlikeLiteral {
final override StringLiteral::Range range;
final override string getAPrimaryQlClass() { result = "StringLiteral" }
}
/**
* A regular expression literal.
*
* ```rb
* /[a-z]+/
* ```
*/
class RegexLiteral extends StringlikeLiteral, @regex {
final override RegexLiteral::Range range;
final override string getAPrimaryQlClass() { result = "RegexLiteral" }
/**
* Gets the regex flags as a string.
*
* ```rb
* /foo/ # => ""
* /foo/i # => "i"
* /foo/imxo # => "imxo"
*/
final string getFlagString() { result = range.getFlagString() }
/**
* Holds if the regex was specified using the `i` flag to indicate case
* insensitivity, as in the following example:
*
* ```rb
* /foo/i
* ```
*/
final predicate hasCaseInsensitiveFlag() { this.getFlagString().charAt(_) = "i" }
}
/**
* A symbol literal.
*
* ```rb
* :foo
* :"foo bar"
* :"foo bar #{baz}"
* ```
*/
class SymbolLiteral extends StringlikeLiteral {
final override SymbolLiteral::Range range;
SymbolLiteral() {
not any(UndefStmt u).getAMethodName() = this and
not any(AliasStmt a).getNewName() = this and
not any(AliasStmt a).getOldName() = this
}
final override string getAPrimaryQlClass() { result = "SymbolLiteral" }
}
/**
* A subshell literal.
*
* ```rb
* `ls -l`
* %x(/bin/sh foo.sh)
* ```
*/
class SubshellLiteral extends StringlikeLiteral, @subshell {
final override SubshellLiteral::Range range;
final override string getAPrimaryQlClass() { result = "SubshellLiteral" }
}
/**
* A character literal.
*
* ```rb
* ?a
* ?\u{61}
* ```
*/
class CharacterLiteral extends Literal, @token_character {
final override CharacterLiteral::Range range;
final override string getAPrimaryQlClass() { result = "CharacterLiteral" }
}
/**
* An array literal.
*
* ```rb
* [123, 'foo', bar()]
* %w(foo bar)
* %i(foo bar)
* ```
*/
class ArrayLiteral extends Literal {
final override ArrayLiteral::Range range;
final override string getAPrimaryQlClass() { result = "ArrayLiteral" }
/** Gets the `n`th element in this array literal. */
final Expr getElement(int n) { result = range.getElement(n) }
/** Gets an element in this array literal. */
final Expr getAnElement() { result = range.getElement(_) }
/** Gets the number of elements in this array literal. */
final int getNumberOfElements() { result = count(range.getElement(_)) }
}
/**
* A hash literal.
*
* ```rb
* { foo: 123, bar: 456 }
* ```
*/
class HashLiteral extends Literal, @hash {
final override HashLiteral::Range range;
final override string getAPrimaryQlClass() { result = "HashLiteral" }
/**
* Gets the `n`th element in this array literal.
*
* In the following example, the 0th element is a `Pair`, and the 1st element
* is a `HashSplatArgument`.
*
* ```rb
* { foo: 123, **bar }
* ```
*/
final Expr getElement(int n) { result = range.getElement(n) }
/** Gets an element in this array literal. */
final Expr getAnElement() { result = range.getElement(_) }
/** Gets a key-value `Pair` in this hash literal. */
final Pair getAKeyValuePair() { result = this.getAnElement() }
/** Gets the number of elements in this hash literal. */
final int getNumberOfElements() { result = count(range.getElement(_)) }
}
/**
* A range literal.
*
* ```rb
* (1..10)
* (1024...2048)
* ```
*/
class RangeLiteral extends Literal, @range {
final override RangeLiteral::Range range;
final override string getAPrimaryQlClass() { result = "RangeLiteral" }
/** Gets the begin expression of this range, if any. */
final Expr getBegin() { result = range.getBegin() }
/** Gets the end expression of this range, if any. */
final Expr getEnd() { result = range.getEnd() }
/**
* Holds if the range is inclusive of the end value, i.e. uses the `..`
* operator.
*/
final predicate isInclusive() { range.isInclusive() }
/**
* Holds if the range is exclusive of the end value, i.e. uses the `...`
* operator.
*/
final predicate isExclusive() { range.isExclusive() }
}
/**
* A method name literal. For example:
* ```rb
* method_name # a normal name
* + # an operator
* :method_name # a symbol
* :"eval_#{name}" # a complex symbol
* ```
*/
class MethodName extends Literal {
final override MethodName::Range range;
final override string getAPrimaryQlClass() { result = "MethodName" }
}

View File

@@ -31,37 +31,13 @@ module AstNode {
or
this = any(Generated::RestAssignment ra).getChild()
or
this instanceof Generated::SymbolArray
or
this instanceof Generated::Interpolation
or
this instanceof Generated::StringArray
or
this instanceof Generated::BareString
or
this instanceof Generated::Float
or
this instanceof Generated::Superclass
or
this instanceof Generated::Hash
or
this instanceof Generated::Array
or
this instanceof Generated::Complex
or
this instanceof Generated::Character
or
this instanceof Generated::HeredocBody
or
this instanceof Generated::HeredocBeginning
or
this instanceof Generated::HeredocEnd
or
this instanceof Generated::Range
or
this instanceof Generated::Rational
or
this instanceof Generated::Subshell
}
override string toString() { result = "AstNode" }

View File

@@ -1,4 +1,5 @@
private import codeql_ruby.AST
private import codeql_ruby.ast.internal.Literal
private import codeql_ruby.ast.internal.Statement
private import codeql_ruby.ast.internal.TreeSitter
@@ -14,194 +15,6 @@ module Self {
}
}
module Literal {
abstract class Range extends Expr::Range {
abstract string getValueText();
override string toString() { result = this.getValueText() }
}
}
module IntegerLiteral {
class Range extends Literal::Range, @token_integer {
final override Generated::Integer generated;
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = this.getValueText() }
}
}
module NilLiteral {
class Range extends Literal::Range, @token_nil {
final override Generated::Nil generated;
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = this.getValueText() }
}
}
module BooleanLiteral {
class DbUnion = @token_true or @token_false;
class Range extends Literal::Range, DbUnion {
final override Generated::Token generated;
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 }
}
}
// TODO: expand this. It's a minimal placeholder so we can test `=~` and `!~`.
module RegexLiteral {
class Range extends Literal::Range, @regex {
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(Generated::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 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(Generated::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(Generated::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 MethodName {
private class TokenTypes =
@setter or @token_class_variable or @token_constant or @token_global_variable or
@token_identifier or @token_instance_variable or @token_operator;
abstract class Range extends Literal::Range, @underscore_method_name {
Range() {
exists(Generated::Undef u | u.getChild(_) = generated)
or
exists(Generated::Alias a | a.getName() = generated or a.getAlias() = generated)
}
}
private class TokenMethodName extends MethodName::Range, TokenTypes {
final override Generated::UnderscoreMethodName generated;
final override string getValueText() {
result = generated.(Generated::Token).getValue()
or
result = generated.(Generated::Setter).getName().getValue() + "="
}
}
private class SimpleSymbolMethodName extends MethodName::Range, SymbolLiteral::SimpleSymbolRange,
@token_simple_symbol { }
private class DelimitedSymbolMethodName extends MethodName::Range,
SymbolLiteral::DelimitedSymbolRange, @delimited_symbol { }
}
module StmtSequence {
abstract class Range extends Expr::Range {
abstract Stmt getStmt(int n);
@@ -343,3 +156,13 @@ module Pair {
final override string toString() { result = "Pair" }
}
}
module StringConcatenation {
class Range extends Expr::Range, @chained_string {
final override Generated::ChainedString generated;
final StringLiteral::Range getString(int i) { result = generated.getChild(i) }
final override string toString() { result = "\"...\" \"...\"" }
}
}

View File

@@ -0,0 +1,373 @@
private import codeql_ruby.AST
private import codeql_ruby.ast.internal.AST
private import codeql_ruby.ast.internal.Expr
private import codeql_ruby.ast.internal.TreeSitter
module Literal {
abstract class Range extends Expr::Range {
abstract string getValueText();
override string toString() { result = this.getValueText() }
}
}
module NumericLiteral {
abstract class Range extends Literal::Range { }
}
module IntegerLiteral {
class Range extends NumericLiteral::Range, @token_integer {
final override Generated::Integer generated;
Range() { not any(Generated::Rational r).getChild() = this }
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = this.getValueText() }
}
}
module FloatLiteral {
class Range extends NumericLiteral::Range, @token_float {
final override Generated::Float generated;
Range() { not any(Generated::Rational r).getChild() = this }
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = this.getValueText() }
}
}
module RationalLiteral {
class Range extends NumericLiteral::Range, @rational {
final override Generated::Rational generated;
final override string getValueText() {
result = generated.getChild().(Generated::Token).getValue()
}
final override string toString() { result = this.getValueText() + "r" }
}
}
module ComplexLiteral {
class Range extends NumericLiteral::Range, @token_complex {
final override Generated::Complex generated;
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = this.getValueText() }
}
}
module NilLiteral {
class Range extends Literal::Range, @token_nil {
final override Generated::Nil generated;
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = this.getValueText() }
}
}
module BooleanLiteral {
class DbUnion = @token_true or @token_false;
class Range extends Literal::Range, DbUnion {
final override Generated::Token generated;
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 }
}
}
module StringComponent {
abstract class Range extends AstNode::Range {
abstract string getValueText();
}
}
module StringTextComponent {
class Range extends StringComponent::Range, @token_string_content {
final override Generated::StringContent generated;
final override string toString() { result = generated.getValue() }
final override string getValueText() { result = generated.getValue() }
}
}
module StringEscapeSequenceComponent {
class Range extends StringComponent::Range, @token_escape_sequence {
final override Generated::EscapeSequence generated;
final override string toString() { result = generated.getValue() }
final override string getValueText() { result = generated.getValue() }
}
}
module StringInterpolationComponent {
class Range extends StringComponent::Range, StmtSequence::Range, @interpolation {
final override Generated::Interpolation generated;
final override string toString() { result = "#{...}" }
final override Expr getStmt(int n) {
// TODO: fix grammar to properly handle a sequence of more than one expr,
// e.g. #{ foo; bar }
n = 0 and
result = generated.getChild()
}
final override string getValueText() { none() }
}
}
module StringlikeLiteral {
abstract class Range extends Literal::Range {
abstract StringComponent::Range getComponent(int i);
string getStartDelimiter() { result = "" }
string getEndDelimiter() { result = "" }
final predicate isSimple() { count(this.getComponent(_)) <= 1 }
override string getValueText() {
// 0 components should result in the empty string
// if there are any interpolations, there should be no result
// otherwise, concatenate all the components
forall(StringComponent c | c = this.getComponent(_) |
not c instanceof StringInterpolationComponent::Range
) and
result =
concat(StringComponent::Range c, int i |
c = this.getComponent(i)
|
c.getValueText() order by i
)
}
override string toString() {
result =
this.getStartDelimiter() +
concat(StringComponent::Range c, int i, string s |
c = this.getComponent(i) and
if c instanceof Generated::Token
then s = c.(Generated::Token).getValue()
else s = "#{...}"
|
s order by i
) + this.getEndDelimiter()
}
}
}
module StringLiteral {
abstract class Range extends StringlikeLiteral::Range {
final override string getStartDelimiter() { result = "\"" }
final override string getEndDelimiter() { result = "\"" }
}
private class RegularStringRange extends StringLiteral::Range, @string__ {
final override Generated::String generated;
final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) }
}
private class BareStringRange extends StringLiteral::Range, @bare_string {
final override Generated::BareString generated;
final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) }
}
}
module RegexLiteral {
class Range extends StringlikeLiteral::Range, @regex {
final override Generated::Regex generated;
final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) }
final override string getStartDelimiter() { result = "/" }
final override string getEndDelimiter() { result = "/" }
final string getFlagString() {
// For `/foo/i`, there should be an `/i` token in the database with `this`
// as its parents. Strip the delimiter, which can vary.
result =
max(Generated::Token t |
t.getParent() = this
|
t.getValue().suffix(1) order by t.getParentIndex()
)
}
}
}
module SymbolLiteral {
abstract class Range extends StringlikeLiteral::Range { }
class SimpleSymbolRange extends SymbolLiteral::Range {
final override Generated::SimpleSymbol generated;
final override StringComponent::Range getComponent(int i) { none() }
final override string getStartDelimiter() { result = ":" }
// 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 {
final override string getStartDelimiter() { result = ":\"" }
final override string getEndDelimiter() { result = "\"" }
}
class DelimitedSymbolRange extends ComplexSymbolRange, @delimited_symbol {
final override Generated::DelimitedSymbol generated;
final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) }
}
class BareSymbolRange extends ComplexSymbolRange, @bare_symbol {
final override Generated::BareSymbol generated;
final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) }
}
class HashKeySymbolRange extends SymbolLiteral::Range, @token_hash_key_symbol {
final override Generated::HashKeySymbol generated;
final override StringComponent::Range getComponent(int i) { none() }
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = ":" + this.getValueText() }
}
}
module SubshellLiteral {
class Range extends StringlikeLiteral::Range, @subshell {
final override Generated::Subshell generated;
final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) }
final override string getStartDelimiter() { result = "`" }
final override string getEndDelimiter() { result = "`" }
}
}
module CharacterLiteral {
class Range extends Literal::Range, @token_character {
final override Generated::Character generated;
final override string getValueText() { result = generated.getValue() }
final override string toString() { result = generated.getValue() }
}
}
module ArrayLiteral {
abstract class Range extends Literal::Range {
final override string getValueText() { none() }
abstract Expr getElement(int i);
}
private class RegularArrayRange extends ArrayLiteral::Range, @array {
final override Generated::Array generated;
final override Expr getElement(int i) { result = generated.getChild(i) }
final override string toString() { result = "[...]" }
}
private class StringArrayRange extends ArrayLiteral::Range, @string_array {
final override Generated::StringArray generated;
final override Expr getElement(int i) { result = generated.getChild(i) }
final override string toString() { result = "%w(...)" }
}
private class SymbolArrayRange extends ArrayLiteral::Range, @symbol_array {
final override Generated::SymbolArray generated;
final override Expr getElement(int i) { result = generated.getChild(i) }
final override string toString() { result = "%i(...)" }
}
}
module HashLiteral {
class Range extends Literal::Range, @hash {
final override Generated::Hash generated;
final override string getValueText() { none() }
final Expr getElement(int i) { result = generated.getChild(i) }
final override string toString() { result = "{...}" }
}
}
module RangeLiteral {
class Range extends Literal::Range, @range {
final override Generated::Range generated;
final override string getValueText() { none() }
final override string toString() { result = "_ " + generated.getOperator() + " _" }
final Expr getBegin() { result = generated.getBegin() }
final Expr getEnd() { result = generated.getEnd() }
final predicate isInclusive() { this instanceof @range_dotdot }
final predicate isExclusive() { this instanceof @range_dotdotdot }
}
}
module MethodName {
private class TokenTypes =
@setter or @token_class_variable or @token_constant or @token_global_variable or
@token_identifier or @token_instance_variable or @token_operator;
abstract class Range extends Literal::Range, @underscore_method_name {
Range() {
exists(Generated::Undef u | u.getChild(_) = generated)
or
exists(Generated::Alias a | a.getName() = generated or a.getAlias() = generated)
}
}
private class TokenMethodName extends MethodName::Range, TokenTypes {
final override Generated::UnderscoreMethodName generated;
final override string getValueText() {
result = generated.(Generated::Token).getValue()
or
result = generated.(Generated::Setter).getName().getValue() + "="
}
}
private class SimpleSymbolMethodName extends MethodName::Range, SymbolLiteral::SimpleSymbolRange,
@token_simple_symbol { }
private class DelimitedSymbolMethodName extends MethodName::Range,
SymbolLiteral::DelimitedSymbolRange, @delimited_symbol { }
}

View File

@@ -1083,15 +1083,25 @@ module Generated {
class Range extends @range, AstNode {
override string getAPrimaryQlClass() { result = "Range" }
override Location getLocation() { range_def(this, _, _, result) }
override Location getLocation() { range_def(this, _, _, _, result) }
UnderscoreArg getChild(int i) { range_child(this, i, result) }
UnderscoreArg getBegin() { range_begin(this, result) }
override AstNode getParent() { range_def(this, result, _, _) }
UnderscoreArg getEnd() { range_end(this, result) }
override int getParentIndex() { range_def(this, _, result, _) }
string getOperator() {
exists(int value | range_def(this, _, _, value, _) |
result = ".." and value = 0
or
result = "..." and value = 1
)
}
override AstNode getAFieldOrChild() { range_child(this, _, result) }
override AstNode getParent() { range_def(this, result, _, _, _) }
override int getParentIndex() { range_def(this, _, result, _, _) }
override AstNode getAFieldOrChild() { range_begin(this, result) or range_end(this, result) }
}
class Rational extends @rational, AstNode {

View File

@@ -264,7 +264,9 @@ private module Cached {
or
i = any(Generated::Program x).getChild(_)
or
i = any(Generated::Range x).getChild(_)
i = any(Generated::Range x).getBegin()
or
i = any(Generated::Range x).getEnd()
or
i = any(Generated::RescueModifier x).getBody()
or