mirror of
https://github.com/github/codeql.git
synced 2026-05-01 03:35:13 +02:00
Ruby: Rework getConstantValue implementation
This commit is contained in:
@@ -240,8 +240,6 @@ class ConstantReadAccess extends ConstantAccess {
|
||||
result = lookupConst(resolveConstantReadAccess(this.getScopeExpr()), this.getName())
|
||||
}
|
||||
|
||||
final override ConstantValue getConstantValue() { result = this.getValue().getConstantValue() }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ConstantReadAccess" }
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ private import codeql.ruby.AST
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.ast.Constant
|
||||
private import internal.AST
|
||||
private import internal.Constant
|
||||
private import internal.Expr
|
||||
private import internal.TreeSitter
|
||||
|
||||
@@ -19,9 +20,7 @@ class Expr extends Stmt, TExpr {
|
||||
deprecated string getValueText() { result = this.getConstantValue().toString() }
|
||||
|
||||
/** Gets the constant value of this expression, if any. */
|
||||
ConstantValue getConstantValue() {
|
||||
forex(CfgNodes::ExprCfgNode n | n = this.getAControlFlowNode() | result = n.getConstantValue())
|
||||
}
|
||||
ConstantValue getConstantValue() { result = getConstantValueExpr(this) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Use `SelfVariableAccess` instead. */
|
||||
|
||||
@@ -39,10 +39,10 @@ class NumericLiteral extends Literal, TNumericLiteral { }
|
||||
*/
|
||||
class IntegerLiteral extends NumericLiteral instanceof IntegerLiteralImpl {
|
||||
/** Gets the numerical value of this integer literal. */
|
||||
final int getValue() { result = super.getValueImpl() }
|
||||
final int getValue() { result = super.getValue() }
|
||||
|
||||
final override ConstantValue::ConstantIntegerValue getConstantValue() {
|
||||
result.isInt(this.getValue())
|
||||
result = NumericLiteral.super.getConstantValue()
|
||||
}
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "IntegerLiteral" }
|
||||
@@ -56,17 +56,11 @@ class IntegerLiteral extends NumericLiteral instanceof IntegerLiteralImpl {
|
||||
* 2.7e+5
|
||||
* ```
|
||||
*/
|
||||
class FloatLiteral extends NumericLiteral, TFloatLiteral {
|
||||
private Ruby::Float g;
|
||||
|
||||
FloatLiteral() { this = TFloatLiteral(g) }
|
||||
|
||||
class FloatLiteral extends NumericLiteral instanceof FloatLiteralImpl {
|
||||
final override ConstantValue::ConstantFloatValue getConstantValue() {
|
||||
result.isFloat(parseFloat(g))
|
||||
result = NumericLiteral.super.getConstantValue()
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "FloatLiteral" }
|
||||
}
|
||||
|
||||
@@ -77,20 +71,11 @@ class FloatLiteral extends NumericLiteral, TFloatLiteral {
|
||||
* 123r
|
||||
* ```
|
||||
*/
|
||||
class RationalLiteral extends NumericLiteral, TRationalLiteral {
|
||||
private Ruby::Rational g;
|
||||
|
||||
RationalLiteral() { this = TRationalLiteral(g) }
|
||||
|
||||
class RationalLiteral extends NumericLiteral instanceof RationalLiteralImpl {
|
||||
final override ConstantValue::ConstantRationalValue getConstantValue() {
|
||||
exists(int numerator, int denominator |
|
||||
isRationalValue(g, numerator, denominator) and
|
||||
result.isRational(numerator, denominator)
|
||||
)
|
||||
result = NumericLiteral.super.getConstantValue()
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getChild().(Ruby::Token).getValue() + "r" }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "RationalLiteral" }
|
||||
}
|
||||
|
||||
@@ -101,29 +86,17 @@ class RationalLiteral extends NumericLiteral, TRationalLiteral {
|
||||
* 1i
|
||||
* ```
|
||||
*/
|
||||
class ComplexLiteral extends NumericLiteral, TComplexLiteral {
|
||||
private Ruby::Complex g;
|
||||
|
||||
ComplexLiteral() { this = TComplexLiteral(g) }
|
||||
|
||||
class ComplexLiteral extends NumericLiteral instanceof ComplexLiteralImpl {
|
||||
final override ConstantValue::ConstantComplexValue getConstantValue() {
|
||||
result.isComplex(0, getComplexValue(g))
|
||||
result = NumericLiteral.super.getConstantValue()
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ComplexLiteral" }
|
||||
}
|
||||
|
||||
/** A `nil` literal. */
|
||||
class NilLiteral extends Literal, TNilLiteral {
|
||||
private Ruby::Nil g;
|
||||
|
||||
NilLiteral() { this = TNilLiteral(g) }
|
||||
|
||||
final override ConstantValue::ConstantNilValue getConstantValue() { any() }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
class NilLiteral extends Literal instanceof NilLiteralImpl {
|
||||
final override ConstantValue::ConstantNilValue getConstantValue() { result = TNil() }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "NilLiteral" }
|
||||
}
|
||||
@@ -137,62 +110,53 @@ class NilLiteral extends Literal, TNilLiteral {
|
||||
* FALSE
|
||||
* ```
|
||||
*/
|
||||
class BooleanLiteral extends Literal, TBooleanLiteral {
|
||||
class BooleanLiteral extends Literal instanceof BooleanLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "BooleanLiteral" }
|
||||
|
||||
/** Holds if the Boolean literal is `true` or `TRUE`. */
|
||||
predicate isTrue() { none() }
|
||||
final predicate isTrue() { this.getValue() = true }
|
||||
|
||||
/** Holds if the Boolean literal is `false` or `FALSE`. */
|
||||
predicate isFalse() { none() }
|
||||
final predicate isFalse() { this.getValue() = false }
|
||||
|
||||
/** Gets the value of this Boolean literal. */
|
||||
boolean getValue() {
|
||||
this.isTrue() and result = true
|
||||
or
|
||||
this.isFalse() and result = false
|
||||
}
|
||||
boolean getValue() { result = super.getValue() }
|
||||
|
||||
final override ConstantValue::ConstantBooleanValue getConstantValue() {
|
||||
result.isBoolean(this.getValue())
|
||||
result = Literal.super.getConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `__ENCODING__` literal.
|
||||
*/
|
||||
class EncodingLiteral extends Literal, TEncoding {
|
||||
class EncodingLiteral extends Literal instanceof EncodingLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "EncodingLiteral" }
|
||||
|
||||
final override string toString() { result = "__ENCODING__" }
|
||||
|
||||
// TODO: return the encoding defined by a magic encoding: comment, if any.
|
||||
override ConstantValue::ConstantStringValue getConstantValue() { result.isString("UTF-8") }
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result = Literal.super.getConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `__LINE__` literal.
|
||||
*/
|
||||
class LineLiteral extends Literal, TLine {
|
||||
class LineLiteral extends Literal instanceof LineLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "LineLiteral" }
|
||||
|
||||
final override string toString() { result = "__LINE__" }
|
||||
|
||||
final override ConstantValue::ConstantIntegerValue getConstantValue() {
|
||||
result.isInt(this.getLocation().getStartLine())
|
||||
result = Literal.super.getConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `__FILE__` literal.
|
||||
*/
|
||||
class FileLiteral extends Literal, TFile {
|
||||
class FileLiteral extends Literal instanceof FileLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "FileLiteral" }
|
||||
|
||||
final override string toString() { result = "__FILE__" }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(this.getLocation().getFile().getAbsolutePath())
|
||||
result = Literal.super.getConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +164,7 @@ class FileLiteral extends Literal, TFile {
|
||||
* The base class for a component of a string: `StringTextComponent`,
|
||||
* `StringEscapeSequenceComponent`, or `StringInterpolationComponent`.
|
||||
*/
|
||||
class StringComponent extends AstNode, TStringComponent {
|
||||
class StringComponent extends AstNode instanceof StringComponentImpl {
|
||||
/**
|
||||
* DEPRECATED: Use `getConstantValue` instead.
|
||||
*
|
||||
@@ -210,7 +174,7 @@ class StringComponent extends AstNode, TStringComponent {
|
||||
deprecated string getValueText() { result = this.getConstantValue().toString() }
|
||||
|
||||
/** Gets the constant value of this string component, if any. */
|
||||
ConstantValue::ConstantStringValue getConstantValue() { none() }
|
||||
ConstantValue::ConstantStringValue getConstantValue() { result = TString(super.getValue()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,100 +189,38 @@ class StringComponent extends AstNode, TStringComponent {
|
||||
* "foo#{ bar() } baz"
|
||||
* ```
|
||||
*/
|
||||
class StringTextComponent extends StringComponent, TStringTextComponentNonRegexp {
|
||||
class StringTextComponent extends StringComponent instanceof StringTextComponentImpl {
|
||||
final override string getAPrimaryQlClass() { result = "StringTextComponent" }
|
||||
|
||||
/** Gets the text of this component as it appears in the source code. */
|
||||
string getRawText() { none() }
|
||||
}
|
||||
|
||||
private class StringTextComponentStringOrHeredocContent extends StringTextComponent,
|
||||
TStringTextComponentNonRegexpStringOrHeredocContent {
|
||||
private Ruby::Token g;
|
||||
|
||||
StringTextComponentStringOrHeredocContent() {
|
||||
this = TStringTextComponentNonRegexpStringOrHeredocContent(g)
|
||||
}
|
||||
|
||||
final override string toString() { result = this.getRawText() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(this.getUnescapedText())
|
||||
}
|
||||
|
||||
final override string getRawText() { result = g.getValue() }
|
||||
|
||||
final private string getUnescapedText() { result = unescapeTextComponent(this.getRawText()) }
|
||||
}
|
||||
|
||||
private class StringTextComponentSimpleSymbol extends StringTextComponent,
|
||||
TStringTextComponentNonRegexpSimpleSymbol {
|
||||
private Ruby::SimpleSymbol g;
|
||||
|
||||
StringTextComponentSimpleSymbol() { this = TStringTextComponentNonRegexpSimpleSymbol(g) }
|
||||
|
||||
final override string toString() { result = getSimpleSymbolValue(g) }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(getSimpleSymbolValue(g))
|
||||
}
|
||||
|
||||
final override string getRawText() { result = getSimpleSymbolValue(g) }
|
||||
}
|
||||
|
||||
private class StringTextComponentHashKeySymbol extends StringTextComponent,
|
||||
TStringTextComponentNonRegexpHashKeySymbol {
|
||||
private Ruby::HashKeySymbol g;
|
||||
|
||||
StringTextComponentHashKeySymbol() { this = TStringTextComponentNonRegexpHashKeySymbol(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(g.getValue())
|
||||
}
|
||||
|
||||
final override string getRawText() { result = g.getValue() }
|
||||
final string getRawText() { result = super.getRawTextImpl() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An escape sequence component of a string or string-like literal.
|
||||
*/
|
||||
class StringEscapeSequenceComponent extends StringComponent, TStringEscapeSequenceComponentNonRegexp {
|
||||
private Ruby::EscapeSequence g;
|
||||
|
||||
StringEscapeSequenceComponent() { this = TStringEscapeSequenceComponentNonRegexp(g) }
|
||||
|
||||
final override string toString() { result = this.getRawText() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(this.getUnescapedText())
|
||||
}
|
||||
|
||||
class StringEscapeSequenceComponent extends StringComponent instanceof StringEscapeSequenceComponentImpl {
|
||||
final override string getAPrimaryQlClass() { result = "StringEscapeSequenceComponent" }
|
||||
|
||||
/** Gets the text of this component as it appears in the source code. */
|
||||
final string getRawText() { result = g.getValue() }
|
||||
|
||||
final private string getUnescapedText() { result = unescapeEscapeSequence(this.getRawText()) }
|
||||
final string getRawText() { result = super.getRawTextImpl() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An interpolation expression component of a string or string-like literal.
|
||||
*/
|
||||
class StringInterpolationComponent extends StringComponent, StmtSequence,
|
||||
TStringInterpolationComponentNonRegexp {
|
||||
class StringInterpolationComponent extends StringComponent, StmtSequence instanceof StringInterpolationComponentImpl {
|
||||
private Ruby::Interpolation g;
|
||||
|
||||
StringInterpolationComponent() { this = TStringInterpolationComponentNonRegexp(g) }
|
||||
|
||||
final override string toString() { result = "#{...}" }
|
||||
|
||||
final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
|
||||
|
||||
deprecated final override string getValueText() { none() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() { none() }
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result = StmtSequence.super.getConstantValue()
|
||||
}
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "StringInterpolationComponent" }
|
||||
}
|
||||
@@ -326,17 +228,7 @@ class StringInterpolationComponent extends StringComponent, StmtSequence,
|
||||
/**
|
||||
* The base class for a component of a regular expression literal.
|
||||
*/
|
||||
class RegExpComponent extends AstNode, TRegExpComponent {
|
||||
/**
|
||||
* DEPRECATED: Use `getConstantValue` instead.
|
||||
*
|
||||
* Gets the source text for this regex component, if any.
|
||||
*/
|
||||
deprecated string getValueText() { result = this.getConstantValue().toString() }
|
||||
|
||||
/** Gets the constant value of this regex component, if any. */
|
||||
ConstantValue::ConstantStringValue getConstantValue() { none() }
|
||||
}
|
||||
class RegExpComponent extends StringComponent instanceof RegExpComponentImpl { }
|
||||
|
||||
/**
|
||||
* A component of a regex literal that is simply text.
|
||||
@@ -350,53 +242,32 @@ class RegExpComponent extends AstNode, TRegExpComponent {
|
||||
* "foo#{ bar() } baz"
|
||||
* ```
|
||||
*/
|
||||
class RegExpTextComponent extends RegExpComponent, TStringTextComponentRegexp {
|
||||
private Ruby::Token g;
|
||||
|
||||
RegExpTextComponent() { this = TStringTextComponentRegexp(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(getRegExpTextComponentValue(this))
|
||||
}
|
||||
|
||||
class RegExpTextComponent extends RegExpComponent instanceof RegExpTextComponentImpl {
|
||||
final override string getAPrimaryQlClass() { result = "RegExpTextComponent" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An escape sequence component of a regex literal.
|
||||
*/
|
||||
class RegExpEscapeSequenceComponent extends RegExpComponent, TStringEscapeSequenceComponentRegexp {
|
||||
private Ruby::EscapeSequence g;
|
||||
|
||||
RegExpEscapeSequenceComponent() { this = TStringEscapeSequenceComponentRegexp(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(getRegExpEscapeSequenceComponentValue(this))
|
||||
}
|
||||
|
||||
class RegExpEscapeSequenceComponent extends RegExpComponent instanceof RegExpEscapeSequenceComponentImpl {
|
||||
final override string getAPrimaryQlClass() { result = "RegExpEscapeSequenceComponent" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An interpolation expression component of a regex literal.
|
||||
*/
|
||||
class RegExpInterpolationComponent extends RegExpComponent, StmtSequence,
|
||||
TStringInterpolationComponentRegexp {
|
||||
class RegExpInterpolationComponent extends RegExpComponent, StmtSequence instanceof RegExpComponentImpl {
|
||||
private Ruby::Interpolation g;
|
||||
|
||||
RegExpInterpolationComponent() { this = TStringInterpolationComponentRegexp(g) }
|
||||
|
||||
final override string toString() { result = "#{...}" }
|
||||
|
||||
final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) }
|
||||
|
||||
deprecated final override string getValueText() { none() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() { none() }
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result = StmtSequence.super.getConstantValue()
|
||||
}
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "RegExpInterpolationComponent" }
|
||||
}
|
||||
@@ -404,7 +275,7 @@ class RegExpInterpolationComponent extends RegExpComponent, StmtSequence,
|
||||
/**
|
||||
* A string, symbol, regexp, or subshell literal.
|
||||
*/
|
||||
class StringlikeLiteral extends Literal, TStringlikeLiteral {
|
||||
class StringlikeLiteral extends Literal instanceof StringlikeLiteralImpl {
|
||||
/**
|
||||
* Gets the `n`th component of this string or string-like literal. The result
|
||||
* will be one of `StringTextComponent`, `StringInterpolationComponent`, and
|
||||
@@ -418,7 +289,7 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral {
|
||||
* "foo_#{ Time.now }"
|
||||
* ```
|
||||
*/
|
||||
StringComponent getComponent(int n) { none() }
|
||||
final StringComponent getComponent(int n) { result = super.getComponentImpl(n) }
|
||||
|
||||
/**
|
||||
* Gets the number of components in this string or string-like literal.
|
||||
@@ -439,78 +310,8 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral {
|
||||
*/
|
||||
final int getNumberOfComponents() { result = count(this.getComponent(_)) }
|
||||
|
||||
private string getStartDelimiter() {
|
||||
this instanceof TStringLiteral and
|
||||
result = "\""
|
||||
or
|
||||
this instanceof TRegExpLiteral and
|
||||
result = "/"
|
||||
or
|
||||
this instanceof TSimpleSymbolLiteral and
|
||||
result = ":"
|
||||
or
|
||||
this instanceof TComplexSymbolLiteral and
|
||||
result = ":\""
|
||||
or
|
||||
this instanceof THashKeySymbolLiteral and
|
||||
result = ""
|
||||
or
|
||||
this instanceof TSubshellLiteral and
|
||||
result = "`"
|
||||
or
|
||||
this instanceof THereDoc and
|
||||
result = ""
|
||||
}
|
||||
|
||||
private string getEndDelimiter() {
|
||||
this instanceof TStringLiteral and
|
||||
result = "\""
|
||||
or
|
||||
this instanceof TRegExpLiteral and
|
||||
result = "/"
|
||||
or
|
||||
this instanceof TSimpleSymbolLiteral and
|
||||
result = ""
|
||||
or
|
||||
this instanceof TComplexSymbolLiteral and
|
||||
result = "\""
|
||||
or
|
||||
this instanceof THashKeySymbolLiteral and
|
||||
result = ""
|
||||
or
|
||||
this instanceof TSubshellLiteral and
|
||||
result = "`"
|
||||
or
|
||||
this instanceof THereDoc and
|
||||
result = ""
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
exists(string full, string summary |
|
||||
full =
|
||||
concat(StringComponent c, int i, string s |
|
||||
c = this.getComponent(i) and
|
||||
(
|
||||
s = toGenerated(c).(Ruby::Token).getValue()
|
||||
or
|
||||
not toGenerated(c) instanceof Ruby::Token and
|
||||
s = "#{...}"
|
||||
)
|
||||
|
|
||||
s order by i
|
||||
) and
|
||||
(
|
||||
// summary should be 32 chars max (incl. ellipsis)
|
||||
full.length() > 32 and summary = full.substring(0, 29) + "..."
|
||||
or
|
||||
full.length() <= 32 and summary = full
|
||||
) and
|
||||
result = this.getStartDelimiter() + summary + this.getEndDelimiter()
|
||||
)
|
||||
}
|
||||
|
||||
final override AstNode getAChild(string pred) {
|
||||
result = super.getAChild(pred)
|
||||
result = Literal.super.getAChild(pred)
|
||||
or
|
||||
pred = "getComponent" and result = this.getComponent(_)
|
||||
}
|
||||
@@ -524,7 +325,7 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral {
|
||||
* "hello, #{name}"
|
||||
* ```
|
||||
*/
|
||||
class StringLiteral extends StringlikeLiteral, TStringLiteral {
|
||||
class StringLiteral extends StringlikeLiteral instanceof StringLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "StringLiteral" }
|
||||
}
|
||||
|
||||
@@ -535,15 +336,13 @@ class StringLiteral extends StringlikeLiteral, TStringLiteral {
|
||||
* /[a-z]+/
|
||||
* ```
|
||||
*/
|
||||
class RegExpLiteral extends StringlikeLiteral, TRegExpLiteral {
|
||||
class RegExpLiteral extends StringlikeLiteral instanceof RegExpLiteralImpl {
|
||||
private Ruby::Regex g;
|
||||
|
||||
RegExpLiteral() { this = TRegExpLiteral(g) }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "RegExpLiteral" }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
|
||||
/**
|
||||
* Gets the regexp flags as a string.
|
||||
*
|
||||
@@ -606,10 +405,14 @@ class RegExpLiteral extends StringlikeLiteral, TRegExpLiteral {
|
||||
* :"foo bar #{baz}"
|
||||
* ```
|
||||
*/
|
||||
class SymbolLiteral extends StringlikeLiteral, TSymbolLiteral {
|
||||
class SymbolLiteral extends StringlikeLiteral instanceof SymbolLiteralImpl {
|
||||
final override string getAPrimaryQlClass() {
|
||||
not this instanceof MethodName and result = "SymbolLiteral"
|
||||
}
|
||||
|
||||
final override ConstantValue::ConstantSymbolValue getConstantValue() {
|
||||
result = StringlikeLiteral.super.getConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -620,18 +423,12 @@ class SymbolLiteral extends StringlikeLiteral, TSymbolLiteral {
|
||||
* %x(/bin/sh foo.sh)
|
||||
* ```
|
||||
*/
|
||||
class SubshellLiteral extends StringlikeLiteral, TSubshellLiteral {
|
||||
class SubshellLiteral extends StringlikeLiteral instanceof SubshellLiteralImpl {
|
||||
private Ruby::Subshell g;
|
||||
|
||||
SubshellLiteral() { this = TSubshellLiteral(g) }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "SubshellLiteral" }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
private class RequiredCharacterConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = any(Ruby::Character c).getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -642,18 +439,12 @@ private class RequiredCharacterConstantValue extends RequiredConstantValue {
|
||||
* ?\u{61}
|
||||
* ```
|
||||
*/
|
||||
class CharacterLiteral extends Literal, TCharacterLiteral {
|
||||
private Ruby::Character g;
|
||||
|
||||
CharacterLiteral() { this = TCharacterLiteral(g) }
|
||||
class CharacterLiteral extends Literal instanceof CharacterLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "CharacterLiteral" }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(g.getValue())
|
||||
result = Literal.super.getConstantValue()
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "CharacterLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,7 +456,7 @@ class CharacterLiteral extends Literal, TCharacterLiteral {
|
||||
* SQL
|
||||
* ```
|
||||
*/
|
||||
class HereDoc extends StringlikeLiteral, THereDoc {
|
||||
class HereDoc extends StringlikeLiteral instanceof HereDocImpl {
|
||||
private Ruby::HeredocBeginning g;
|
||||
|
||||
HereDoc() { this = THereDoc(g) }
|
||||
@@ -713,12 +504,6 @@ class HereDoc extends StringlikeLiteral, THereDoc {
|
||||
result = ["-", "~"]
|
||||
)
|
||||
}
|
||||
|
||||
final override StringComponent getComponent(int n) {
|
||||
toGenerated(result) = getHereDocBody(g).getChild(n)
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -784,8 +569,6 @@ class HashLiteral extends Literal instanceof HashLiteralImpl {
|
||||
/** Gets the number of elements in this hash literal. */
|
||||
final int getNumberOfElements() { result = super.getNumberOfElementsImpl() }
|
||||
|
||||
final override string toString() { result = "{...}" }
|
||||
|
||||
final override AstNode getAChild(string pred) {
|
||||
result = Literal.super.getAChild(pred)
|
||||
or
|
||||
@@ -801,44 +584,34 @@ class HashLiteral extends Literal instanceof HashLiteralImpl {
|
||||
* (1024...2048)
|
||||
* ```
|
||||
*/
|
||||
class RangeLiteral extends Literal, TRangeLiteral {
|
||||
class RangeLiteral extends Literal instanceof RangeLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "RangeLiteral" }
|
||||
|
||||
/** Gets the begin expression of this range, if any. */
|
||||
Expr getBegin() { none() }
|
||||
final Expr getBegin() { result = super.getBeginImpl() }
|
||||
|
||||
/** Gets the end expression of this range, if any. */
|
||||
Expr getEnd() { none() }
|
||||
final Expr getEnd() { result = super.getEndImpl() }
|
||||
|
||||
/**
|
||||
* Holds if the range is inclusive of the end value, i.e. uses the `..`
|
||||
* operator.
|
||||
*/
|
||||
predicate isInclusive() { none() }
|
||||
final predicate isInclusive() { super.isInclusiveImpl() }
|
||||
|
||||
/**
|
||||
* Holds if the range is exclusive of the end value, i.e. uses the `...`
|
||||
* operator.
|
||||
*/
|
||||
predicate isExclusive() { none() }
|
||||
final predicate isExclusive() { super.isExclusiveImpl() }
|
||||
|
||||
final override AstNode getAChild(string pred) {
|
||||
result = super.getAChild(pred)
|
||||
result = Literal.super.getAChild(pred)
|
||||
or
|
||||
pred = "getBegin" and result = this.getBegin()
|
||||
or
|
||||
pred = "getEnd" and result = this.getEnd()
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
exists(string op |
|
||||
this.isInclusive() and op = ".."
|
||||
or
|
||||
this.isExclusive() and op = "..."
|
||||
|
|
||||
result = "_ " + op + " _"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,36 +1,446 @@
|
||||
cached
|
||||
newtype TConstantValue =
|
||||
TInt(int i) { any(RequiredConstantValue x).requiredInt(i) } or
|
||||
TFloat(float f) { any(RequiredConstantValue x).requiredFloat(f) } or
|
||||
TRational(int numerator, int denominator) {
|
||||
any(RequiredConstantValue x).requiredRational(numerator, denominator)
|
||||
} or
|
||||
TComplex(float real, float imaginary) {
|
||||
any(RequiredConstantValue x).requiredComplex(real, imaginary)
|
||||
} or
|
||||
TString(string s) { any(RequiredConstantValue x).requiredString(s) } or
|
||||
TSymbol(string s) { any(RequiredConstantValue x).requiredSymbol(s) } or
|
||||
TBoolean(boolean b) { b in [false, true] } or
|
||||
TNil()
|
||||
|
||||
private newtype TRequiredConstantValue = MkRequiredConstantValue()
|
||||
private import codeql.ruby.AST
|
||||
private import codeql.ruby.ast.internal.Literal
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
private import codeql.ruby.dataflow.SSA
|
||||
private import ExprNodes
|
||||
|
||||
/**
|
||||
* A class that exists for QL technical reasons only (the IPA type used
|
||||
* to represent constant values needs to be bounded).
|
||||
* Provides an implementation of constant propagation for control-flow graph
|
||||
* (CFG) nodes and expressions (AST nodes).
|
||||
*
|
||||
* The end result are two predicates
|
||||
* ```ql
|
||||
* ConstantValue getConstantValue(ExprCfgNode n);
|
||||
* ConstantValue getConstantValueExpr(Expr e);
|
||||
* ```
|
||||
*
|
||||
* It would be natural to define those predicates recursively. However, because
|
||||
* of how `newtype`s work, this results in bad performance as a result of
|
||||
* unnecessary recursion through the constructors of `TConstantValue`. Instead,
|
||||
* we define a set of predicats for each possible `ConstantValue` type, and each
|
||||
* set of predicates need to replicate logic, e.g., how a constant may be propagated
|
||||
* from an assignment to a variable read.
|
||||
*
|
||||
* For each `ConstantValue` type `T`, we define three predicates:
|
||||
* ```ql
|
||||
* predicate isT(ExprCfgNode n, T v);
|
||||
* predicate isTExprNoCfg(Expr e, T v);
|
||||
* predicate isTExpr(Expr e, T v);
|
||||
* ```
|
||||
*
|
||||
* `isT` and `isTExpr` rely on the CFG to determine the constant value of a CFG
|
||||
* node and expression, respectively, whereas `isTExprNoCfg` is able to determine
|
||||
* the constant value of an expression without relying on the CFG. This means that
|
||||
* even if the CFG is not available (dead code), we may still be able to infer a
|
||||
* constant value in some cases.
|
||||
*/
|
||||
class RequiredConstantValue extends MkRequiredConstantValue {
|
||||
string toString() { none() }
|
||||
private module Propagation {
|
||||
private ExprCfgNode getSource(VariableReadAccessCfgNode read) {
|
||||
exists(Ssa::WriteDefinition def |
|
||||
def.assigns(result) and
|
||||
read = def.getARead()
|
||||
)
|
||||
}
|
||||
|
||||
predicate requiredInt(int i) { none() }
|
||||
predicate isInt(ExprCfgNode e, int i) {
|
||||
isIntExprNoCfg(e.getExpr(), i)
|
||||
or
|
||||
isIntExpr(e.getExpr().(ConstantReadAccess).getValue(), i)
|
||||
or
|
||||
isInt(getSource(e), i)
|
||||
or
|
||||
e =
|
||||
any(UnaryOperationCfgNode unop |
|
||||
unop.getOperator() = "-" and
|
||||
isInt(unop.getOperand(), -i)
|
||||
or
|
||||
unop.getOperator() = "+" and
|
||||
isInt(unop.getOperand(), i)
|
||||
)
|
||||
or
|
||||
exists(BinaryOperationCfgNode binop, string op, int left, int right |
|
||||
e = binop and
|
||||
isInt(binop.getLeftOperand(), left) and
|
||||
isInt(binop.getRightOperand(), right) and
|
||||
op = binop.getOperator()
|
||||
|
|
||||
op = "+" and
|
||||
i = left + right
|
||||
or
|
||||
op = "-" and
|
||||
i = left - right
|
||||
or
|
||||
op = "*" and
|
||||
i = left * right
|
||||
or
|
||||
op = "/" and
|
||||
i = left / right
|
||||
)
|
||||
}
|
||||
|
||||
predicate requiredFloat(float f) { none() }
|
||||
private predicate isIntExprNoCfg(Expr e, int i) {
|
||||
i = e.(IntegerLiteralImpl).getValue()
|
||||
or
|
||||
i = e.(LineLiteralImpl).getValue()
|
||||
or
|
||||
isIntExprNoCfg(e.(ConstantReadAccess).getValue(), i)
|
||||
}
|
||||
|
||||
predicate requiredRational(int numerator, int denominator) { none() }
|
||||
predicate isIntExpr(Expr e, int i) {
|
||||
isIntExprNoCfg(e, i)
|
||||
or
|
||||
isIntExpr(e.(ConstantReadAccess).getValue(), i)
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isInt(n, i))
|
||||
}
|
||||
|
||||
predicate requiredComplex(float real, float imaginary) { none() }
|
||||
predicate isFloat(ExprCfgNode e, float f) {
|
||||
isFloatExprNoCfg(e.getExpr(), f)
|
||||
or
|
||||
isFloatExpr(e.getExpr().(ConstantReadAccess).getValue(), f)
|
||||
or
|
||||
isFloat(getSource(e), f)
|
||||
or
|
||||
e =
|
||||
any(UnaryOperationCfgNode unop |
|
||||
unop.getOperator() = "-" and
|
||||
isFloat(unop.getOperand(), -f)
|
||||
or
|
||||
unop.getOperator() = "+" and
|
||||
isFloat(unop.getOperand(), f)
|
||||
)
|
||||
or
|
||||
exists(BinaryOperationCfgNode binop, string op, float left, float right |
|
||||
e = binop and
|
||||
op = binop.getOperator() and
|
||||
exists(ExprCfgNode l, ExprCfgNode r |
|
||||
l = binop.getLeftOperand() and
|
||||
r = binop.getRightOperand()
|
||||
|
|
||||
isFloat(l, left) and isFloat(r, right)
|
||||
or
|
||||
isInt(l, left) and isFloat(r, right)
|
||||
or
|
||||
isFloat(l, left) and isInt(r, right)
|
||||
)
|
||||
|
|
||||
op = "+" and
|
||||
f = left + right
|
||||
or
|
||||
op = "-" and
|
||||
f = left - right
|
||||
or
|
||||
op = "*" and
|
||||
f = left * right
|
||||
or
|
||||
op = "/" and
|
||||
f = left / right
|
||||
)
|
||||
}
|
||||
|
||||
predicate requiredString(string s) { none() }
|
||||
private predicate isFloatExprNoCfg(Expr e, float f) {
|
||||
f = e.(FloatLiteralImpl).getValue()
|
||||
or
|
||||
isFloatExprNoCfg(e.(ConstantReadAccess).getValue(), f)
|
||||
}
|
||||
|
||||
predicate requiredSymbol(string s) { none() }
|
||||
predicate isFloatExpr(Expr e, float f) {
|
||||
isFloatExprNoCfg(e, f)
|
||||
or
|
||||
isFloatExpr(e.(ConstantReadAccess).getValue(), f)
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isFloat(n, f))
|
||||
}
|
||||
|
||||
predicate isRational(ExprCfgNode e, int numerator, int denominator) {
|
||||
isRationalExprNoCfg(e.getExpr(), numerator, denominator)
|
||||
or
|
||||
isRationalExpr(e.getExpr().(ConstantReadAccess).getValue(), numerator, denominator)
|
||||
or
|
||||
isRational(getSource(e), numerator, denominator)
|
||||
}
|
||||
|
||||
private predicate isRationalExprNoCfg(Expr e, int numerator, int denominator) {
|
||||
e.(RationalLiteralImpl).hasValue(numerator, denominator)
|
||||
or
|
||||
isRationalExprNoCfg(e.(ConstantReadAccess).getValue(), numerator, denominator)
|
||||
}
|
||||
|
||||
predicate isRationalExpr(Expr e, int numerator, int denominator) {
|
||||
isRationalExprNoCfg(e, numerator, denominator)
|
||||
or
|
||||
isRationalExpr(e.(ConstantReadAccess).getValue(), numerator, denominator)
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isRational(n, numerator, denominator))
|
||||
}
|
||||
|
||||
predicate isComplex(ExprCfgNode e, float real, float imaginary) {
|
||||
isComplexExprNoCfg(e.getExpr(), real, imaginary)
|
||||
or
|
||||
isComplexExpr(e.getExpr().(ConstantReadAccess).getValue(), real, imaginary)
|
||||
or
|
||||
isComplex(getSource(e), real, imaginary)
|
||||
}
|
||||
|
||||
private predicate isComplexExprNoCfg(Expr e, float real, float imaginary) {
|
||||
e.(ComplexLiteralImpl).hasValue(real, imaginary)
|
||||
or
|
||||
isComplexExprNoCfg(e.(ConstantReadAccess).getValue(), real, imaginary)
|
||||
}
|
||||
|
||||
predicate isComplexExpr(Expr e, float real, float imaginary) {
|
||||
isComplexExprNoCfg(e, real, imaginary)
|
||||
or
|
||||
isComplexExpr(e.(ConstantReadAccess).getValue(), real, imaginary)
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isComplex(n, real, imaginary))
|
||||
}
|
||||
|
||||
private class StringlikeLiteralWithInterpolationCfgNode extends StringlikeLiteralCfgNode {
|
||||
StringlikeLiteralWithInterpolationCfgNode() {
|
||||
this.getAComponent() =
|
||||
any(StringComponentCfgNode c |
|
||||
c instanceof StringInterpolationComponentCfgNode or
|
||||
c instanceof RegExpInterpolationComponentCfgNode
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string getComponentValue(int i) {
|
||||
this.getComponent(i) =
|
||||
any(StringComponentCfgNode c |
|
||||
isString(c, result)
|
||||
or
|
||||
result = c.getNode().(StringComponentImpl).getValue()
|
||||
)
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string getValue() {
|
||||
result =
|
||||
strictconcat(int i | exists(this.getComponent(i)) | this.getComponentValue(i) order by i)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
string getSymbolValue() {
|
||||
result = this.getValue() and
|
||||
this.getExpr() instanceof SymbolLiteral
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
string getNonSymbolValue() {
|
||||
result = this.getValue() and
|
||||
not this.getExpr() instanceof SymbolLiteral
|
||||
}
|
||||
}
|
||||
|
||||
predicate isString(ExprCfgNode e, string s) {
|
||||
isStringExprNoCfg(e.getExpr(), s)
|
||||
or
|
||||
isStringExpr(e.getExpr().(ConstantReadAccess).getValue(), s)
|
||||
or
|
||||
isString(getSource(e), s)
|
||||
or
|
||||
exists(BinaryOperationCfgNode binop, string left, string right |
|
||||
e = binop and
|
||||
isString(binop.getLeftOperand(), left) and
|
||||
isString(binop.getRightOperand(), right) and
|
||||
binop.getOperator() = "+" and
|
||||
left.length() + right.length() <= 1000 and
|
||||
s = left + right
|
||||
)
|
||||
or
|
||||
s = e.(StringlikeLiteralWithInterpolationCfgNode).getNonSymbolValue()
|
||||
or
|
||||
// If last statement in the interpolation is a constant or local variable read,
|
||||
// we attempt to look up its string value.
|
||||
// If there's a result, we return that as the string value of the interpolation.
|
||||
exists(ExprCfgNode last | last = e.(StringInterpolationComponentCfgNode).getLastStmt() |
|
||||
isInt(last, any(int i | s = i.toString())) or
|
||||
isFloat(last, any(float f | s = f.toString())) or
|
||||
isString(last, s)
|
||||
)
|
||||
or
|
||||
// If last statement in the interpolation is a constant or local variable read,
|
||||
// attempt to look up its definition and return the definition's `getConstantValue()`.
|
||||
exists(ExprCfgNode last | last = e.(RegExpInterpolationComponentCfgNode).getLastStmt() |
|
||||
isInt(last, any(int i | s = i.toString())) or
|
||||
isFloat(last, any(float f | s = f.toString())) or
|
||||
isString(last, s)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isStringExprNoCfg(Expr e, string s) {
|
||||
s = e.(StringlikeLiteralImpl).getStringValue() and
|
||||
not e instanceof SymbolLiteral
|
||||
or
|
||||
s = e.(EncodingLiteralImpl).getValue()
|
||||
or
|
||||
s = e.(FileLiteralImpl).getValue()
|
||||
or
|
||||
s = e.(TokenMethodName).getValue()
|
||||
or
|
||||
s = e.(CharacterLiteralImpl).getValue()
|
||||
or
|
||||
isStringExprNoCfg(e.(ConstantReadAccess).getValue(), s)
|
||||
}
|
||||
|
||||
predicate isStringExpr(Expr e, string s) {
|
||||
isStringExprNoCfg(e, s)
|
||||
or
|
||||
isStringExpr(e.(ConstantReadAccess).getValue(), s)
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isString(n, s))
|
||||
}
|
||||
|
||||
predicate isSymbol(ExprCfgNode e, string s) {
|
||||
isSymbolExprNoCfg(e.getExpr(), s)
|
||||
or
|
||||
isSymbolExpr(e.getExpr().(ConstantReadAccess).getValue(), s)
|
||||
or
|
||||
isSymbol(getSource(e), s)
|
||||
or
|
||||
s = e.(StringlikeLiteralWithInterpolationCfgNode).getSymbolValue()
|
||||
}
|
||||
|
||||
private predicate isSymbolExprNoCfg(Expr e, string s) {
|
||||
s = e.(StringlikeLiteralImpl).getStringValue() and
|
||||
e instanceof SymbolLiteral
|
||||
or
|
||||
isSymbolExprNoCfg(e.(ConstantReadAccess).getValue(), s)
|
||||
}
|
||||
|
||||
predicate isSymbolExpr(Expr e, string s) {
|
||||
isSymbolExprNoCfg(e, s)
|
||||
or
|
||||
isSymbolExpr(e.(ConstantReadAccess).getValue(), s)
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isSymbol(n, s))
|
||||
}
|
||||
|
||||
predicate isBoolean(ExprCfgNode e, boolean b) {
|
||||
isBooleanExprNoCfg(e.getExpr(), b)
|
||||
or
|
||||
isBooleanExpr(e.getExpr().(ConstantReadAccess).getValue(), b)
|
||||
or
|
||||
isBoolean(getSource(e), b)
|
||||
}
|
||||
|
||||
private predicate isBooleanExprNoCfg(Expr e, boolean b) {
|
||||
b = e.(BooleanLiteralImpl).getValue()
|
||||
or
|
||||
isBooleanExprNoCfg(e.(ConstantReadAccess).getValue(), b)
|
||||
}
|
||||
|
||||
predicate isBooleanExpr(Expr e, boolean b) {
|
||||
isBooleanExprNoCfg(e, b)
|
||||
or
|
||||
isBooleanExpr(e.(ConstantReadAccess).getValue(), b)
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isBoolean(n, b))
|
||||
}
|
||||
|
||||
predicate isNil(ExprCfgNode e) {
|
||||
isNilExprNoCfg(e.getExpr())
|
||||
or
|
||||
isNilExpr(e.getExpr().(ConstantReadAccess).getValue())
|
||||
or
|
||||
isNil(getSource(e))
|
||||
}
|
||||
|
||||
private predicate isNilExprNoCfg(Expr e) {
|
||||
e instanceof NilLiteralImpl
|
||||
or
|
||||
isNilExprNoCfg(e.(ConstantReadAccess).getValue())
|
||||
}
|
||||
|
||||
predicate isNilExpr(Expr e) {
|
||||
isNilExprNoCfg(e)
|
||||
or
|
||||
isNilExpr(e.(ConstantReadAccess).getValue())
|
||||
or
|
||||
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isNil(n))
|
||||
}
|
||||
}
|
||||
|
||||
private import Propagation
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TConstantValue =
|
||||
TInt(int i) { isInt(_, i) or isIntExpr(_, i) } or
|
||||
TFloat(float f) { isFloat(_, f) or isFloatExpr(_, f) } or
|
||||
TRational(int numerator, int denominator) {
|
||||
isRational(_, numerator, denominator) or
|
||||
isRationalExpr(_, numerator, denominator)
|
||||
} or
|
||||
TComplex(float real, float imaginary) {
|
||||
isComplex(_, real, imaginary) or
|
||||
isComplexExpr(_, real, imaginary)
|
||||
} or
|
||||
TString(string s) {
|
||||
isString(_, s)
|
||||
or
|
||||
isStringExpr(_, s)
|
||||
or
|
||||
s = any(StringComponentImpl c).getValue()
|
||||
} or
|
||||
TSymbol(string s) { isString(_, s) or isSymbolExpr(_, s) } or
|
||||
TBoolean(boolean b) { b in [false, true] } or
|
||||
TNil()
|
||||
|
||||
cached
|
||||
ConstantValue getConstantValue(ExprCfgNode n) {
|
||||
result.isInt(any(int i | isInt(n, i)))
|
||||
or
|
||||
result.isFloat(any(float f | isFloat(n, f)))
|
||||
or
|
||||
exists(int numerator, int denominator |
|
||||
isRational(n, numerator, denominator) and
|
||||
result = TRational(numerator, denominator)
|
||||
)
|
||||
or
|
||||
exists(float real, float imaginary |
|
||||
isComplex(n, real, imaginary) and
|
||||
result = TComplex(real, imaginary)
|
||||
)
|
||||
or
|
||||
result.isString(any(string s | isString(n, s)))
|
||||
or
|
||||
result.isSymbol(any(string s | isSymbol(n, s)))
|
||||
or
|
||||
result.isBoolean(any(boolean b | isBoolean(n, b)))
|
||||
or
|
||||
result.isNil() and
|
||||
isNil(n)
|
||||
}
|
||||
|
||||
cached
|
||||
ConstantValue getConstantValueExpr(Expr e) {
|
||||
result.isInt(any(int i | isIntExpr(e, i)))
|
||||
or
|
||||
result.isFloat(any(float f | isFloatExpr(e, f)))
|
||||
or
|
||||
exists(int numerator, int denominator |
|
||||
isRationalExpr(e, numerator, denominator) and
|
||||
result = TRational(numerator, denominator)
|
||||
)
|
||||
or
|
||||
exists(float real, float imaginary |
|
||||
isComplexExpr(e, real, imaginary) and
|
||||
result = TComplex(real, imaginary)
|
||||
)
|
||||
or
|
||||
result.isString(any(string s | isStringExpr(e, s)))
|
||||
or
|
||||
result.isSymbol(any(string s | isSymbolExpr(e, s)))
|
||||
or
|
||||
result.isBoolean(any(boolean b | isBooleanExpr(e, b)))
|
||||
or
|
||||
result.isNil() and
|
||||
isNilExpr(e)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
@@ -2,6 +2,7 @@ private import codeql.ruby.AST
|
||||
private import AST
|
||||
private import Constant
|
||||
private import TreeSitter
|
||||
private import codeql.ruby.ast.internal.Scope
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
private import codeql.NumberUtils
|
||||
|
||||
@@ -23,12 +24,8 @@ int parseInteger(Ruby::Integer i) {
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredIntegerLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) { i = any(IntegerLiteral il).getValue() }
|
||||
}
|
||||
|
||||
abstract class IntegerLiteralImpl extends Expr, TIntegerLiteral {
|
||||
abstract int getValueImpl();
|
||||
abstract int getValue();
|
||||
}
|
||||
|
||||
class IntegerLiteralReal extends IntegerLiteralImpl, TIntegerLiteralReal {
|
||||
@@ -36,7 +33,7 @@ class IntegerLiteralReal extends IntegerLiteralImpl, TIntegerLiteralReal {
|
||||
|
||||
IntegerLiteralReal() { this = TIntegerLiteralReal(g) }
|
||||
|
||||
final override int getValueImpl() { result = parseInteger(g) }
|
||||
final override int getValue() { result = parseInteger(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
@@ -46,7 +43,7 @@ class IntegerLiteralSynth extends IntegerLiteralImpl, TIntegerLiteralSynth {
|
||||
|
||||
IntegerLiteralSynth() { this = TIntegerLiteralSynth(_, _, value) }
|
||||
|
||||
final override int getValueImpl() { result = value }
|
||||
final override int getValue() { result = value }
|
||||
|
||||
final override string toString() { result = value.toString() }
|
||||
}
|
||||
@@ -54,8 +51,14 @@ class IntegerLiteralSynth extends IntegerLiteralImpl, TIntegerLiteralSynth {
|
||||
// TODO: implement properly
|
||||
float parseFloat(Ruby::Float f) { result = f.getValue().toFloat() }
|
||||
|
||||
private class RequiredFloatConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredFloat(float f) { f = parseFloat(_) }
|
||||
class FloatLiteralImpl extends Expr, TFloatLiteral {
|
||||
private Ruby::Float g;
|
||||
|
||||
FloatLiteralImpl() { this = TFloatLiteral(g) }
|
||||
|
||||
final float getValue() { result = parseFloat(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
predicate isRationalValue(Ruby::Rational r, int numerator, int denominator) {
|
||||
@@ -72,10 +75,16 @@ predicate isRationalValue(Ruby::Rational r, int numerator, int denominator) {
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRationalConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredRational(int numerator, int denominator) {
|
||||
isRationalValue(_, numerator, denominator)
|
||||
class RationalLiteralImpl extends Expr, TRationalLiteral {
|
||||
private Ruby::Rational g;
|
||||
|
||||
RationalLiteralImpl() { this = TRationalLiteral(g) }
|
||||
|
||||
final predicate hasValue(int numerator, int denominator) {
|
||||
isRationalValue(g, numerator, denominator)
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getChild().(Ruby::Token).getValue() + "r" }
|
||||
}
|
||||
|
||||
float getComplexValue(Ruby::Complex c) {
|
||||
@@ -85,245 +94,79 @@ float getComplexValue(Ruby::Complex c) {
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredComplexConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredComplex(float real, float imaginary) {
|
||||
real = 0 and
|
||||
imaginary = getComplexValue(_)
|
||||
class ComplexLiteralImpl extends Expr, TComplexLiteral {
|
||||
private Ruby::Complex g;
|
||||
|
||||
ComplexLiteralImpl() { this = TComplexLiteral(g) }
|
||||
|
||||
final predicate hasValue(float real, float imaginary) {
|
||||
real = 0 and imaginary = getComplexValue(g)
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
class TrueLiteral extends BooleanLiteral, TTrueLiteral {
|
||||
class NilLiteralImpl extends Expr, TNilLiteral {
|
||||
private Ruby::Nil g;
|
||||
|
||||
NilLiteralImpl() { this = TNilLiteral(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
abstract class BooleanLiteralImpl extends Expr, TBooleanLiteral {
|
||||
abstract boolean getValue();
|
||||
}
|
||||
|
||||
class TrueLiteral extends BooleanLiteralImpl, TTrueLiteral {
|
||||
private Ruby::True g;
|
||||
|
||||
TrueLiteral() { this = TTrueLiteral(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override predicate isTrue() { any() }
|
||||
final override boolean getValue() { result = true }
|
||||
}
|
||||
|
||||
class FalseLiteral extends BooleanLiteral, TFalseLiteral {
|
||||
class FalseLiteral extends BooleanLiteralImpl, TFalseLiteral {
|
||||
private Ruby::False g;
|
||||
|
||||
FalseLiteral() { this = TFalseLiteral(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override predicate isFalse() { any() }
|
||||
final override boolean getValue() { result = false }
|
||||
}
|
||||
|
||||
private class RequiredEncodingLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = "UTF-8" }
|
||||
class EncodingLiteralImpl extends Expr, TEncoding {
|
||||
private Ruby::Encoding g;
|
||||
|
||||
EncodingLiteralImpl() { this = TEncoding(g) }
|
||||
|
||||
// TODO: return the encoding defined by a magic encoding: comment, if any.
|
||||
final string getValue() { result = "UTF-8" }
|
||||
|
||||
final override string toString() { result = "__ENCODING__" }
|
||||
}
|
||||
|
||||
private class RequiredLineLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) { i = any(LineLiteral ll).getLocation().getStartLine() }
|
||||
class LineLiteralImpl extends Expr, TLine {
|
||||
private Ruby::Line g;
|
||||
|
||||
LineLiteralImpl() { this = TLine(g) }
|
||||
|
||||
final int getValue() { result = this.getLocation().getStartLine() }
|
||||
|
||||
final override string toString() { result = "__LINE__" }
|
||||
}
|
||||
|
||||
private class RequiredFileLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s = any(FileLiteral fl).getLocation().getFile().getAbsolutePath()
|
||||
}
|
||||
}
|
||||
class FileLiteralImpl extends Expr, TFile {
|
||||
private Ruby::File g;
|
||||
|
||||
private class RequiredStringTextComponentNonRegexpStringOrHeredocContentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s =
|
||||
unescapeTextComponent(any(Ruby::Token t |
|
||||
exists(TStringTextComponentNonRegexpStringOrHeredocContent(t))
|
||||
).getValue())
|
||||
}
|
||||
}
|
||||
FileLiteralImpl() { this = TFile(g) }
|
||||
|
||||
private class RequiredStringTextComponentNonRegexpSimpleSymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getSimpleSymbolValue(_) }
|
||||
}
|
||||
final string getValue() { result = this.getLocation().getFile().getAbsolutePath() }
|
||||
|
||||
private class RequiredStringTextComponentNonRegexpHashKeySymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = any(Ruby::HashKeySymbol h).getValue() }
|
||||
}
|
||||
|
||||
private class RequiredStringEscapeSequenceComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s =
|
||||
unescapeEscapeSequence(any(Ruby::Token t | exists(TStringEscapeSequenceComponentNonRegexp(t)))
|
||||
.getValue())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string represented by the escape sequence in `escaped`. For example:
|
||||
*
|
||||
* ```
|
||||
* \\ => \
|
||||
* \141 => a
|
||||
* \u0078 => x
|
||||
* ```
|
||||
*/
|
||||
bindingset[escaped]
|
||||
string unescapeEscapeSequence(string escaped) {
|
||||
result = unescapeKnownEscapeSequence(escaped)
|
||||
or
|
||||
// Any other character following a backslash is just that character.
|
||||
not exists(unescapeKnownEscapeSequence(escaped)) and
|
||||
result = escaped.suffix(1)
|
||||
}
|
||||
|
||||
bindingset[escaped]
|
||||
private string unescapeKnownEscapeSequence(string escaped) {
|
||||
escaped = "\\\\" and result = "\\"
|
||||
or
|
||||
escaped = "\\'" and result = "'"
|
||||
or
|
||||
escaped = "\\\"" and result = "\""
|
||||
or
|
||||
escaped = "\\a" and result = 7.toUnicode()
|
||||
or
|
||||
escaped = "\\b" and result = 8.toUnicode()
|
||||
or
|
||||
escaped = "\\t" and result = "\t"
|
||||
or
|
||||
escaped = "\\n" and result = "\n"
|
||||
or
|
||||
escaped = "\\v" and result = 11.toUnicode()
|
||||
or
|
||||
escaped = "\\f" and result = 12.toUnicode()
|
||||
or
|
||||
escaped = "\\r" and result = "\r"
|
||||
or
|
||||
escaped = "\\e" and result = 27.toUnicode()
|
||||
or
|
||||
escaped = "\\s" and result = " "
|
||||
or
|
||||
escaped = ["\\c?", "\\C-?"] and result = 127.toUnicode()
|
||||
or
|
||||
result = parseOctalInt(escaped.regexpCapture("\\\\([0-7]{1,3})", 1)).toUnicode()
|
||||
or
|
||||
result = parseHexInt(escaped.regexpCapture("\\\\x([0-9a-fA-F]{1,2})", 1)).toUnicode()
|
||||
or
|
||||
result = parseHexInt(escaped.regexpCapture("\\\\u([0-9a-fA-F]{4})", 1)).toUnicode()
|
||||
or
|
||||
result = parseHexInt(escaped.regexpCapture("\\\\u\\{([0-9a-fA-F]{1,6})\\}", 1)).toUnicode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result of unescaping a string text component by replacing `\\` and
|
||||
* `\'` with `\` and `'`, respectively.
|
||||
*
|
||||
* ```rb
|
||||
* 'foo\\bar \'baz\'' # foo\bar 'baz'
|
||||
* ```
|
||||
*/
|
||||
bindingset[text]
|
||||
string unescapeTextComponent(string text) { result = text.regexpReplaceAll("\\\\(['\\\\])", "$1") }
|
||||
|
||||
class TRegExpComponent =
|
||||
TStringTextComponentRegexp or TStringEscapeSequenceComponentRegexp or
|
||||
TStringInterpolationComponentRegexp;
|
||||
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
string getRegExpTextComponentValue(RegExpTextComponent c) {
|
||||
exists(Ruby::Token t |
|
||||
c = TStringTextComponentRegexp(t) and
|
||||
not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = t.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRegExpTextComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getRegExpTextComponentValue(_) }
|
||||
}
|
||||
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
string getRegExpEscapeSequenceComponentValue(RegExpEscapeSequenceComponent c) {
|
||||
exists(Ruby::EscapeSequence e |
|
||||
c = TStringEscapeSequenceComponentRegexp(e) and
|
||||
not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = e.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRegExpEscapeSequenceComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getRegExpEscapeSequenceComponentValue(_) }
|
||||
}
|
||||
|
||||
class RegularStringLiteral extends StringLiteral, TRegularStringLiteral {
|
||||
private Ruby::String g;
|
||||
|
||||
RegularStringLiteral() { this = TRegularStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
class BareStringLiteral extends StringLiteral, TBareStringLiteral {
|
||||
private Ruby::BareString g;
|
||||
|
||||
BareStringLiteral() { this = TBareStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
// Tree-sitter gives us value text including the colon, which we skip.
|
||||
string getSimpleSymbolValue(Ruby::SimpleSymbol ss) { result = ss.getValue().suffix(1) }
|
||||
|
||||
private class RequiredSimpleSymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredSymbol(string s) { s = getSimpleSymbolValue(_) }
|
||||
}
|
||||
|
||||
private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral {
|
||||
private Ruby::SimpleSymbol g;
|
||||
|
||||
SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) }
|
||||
|
||||
final override ConstantValue::ConstantSymbolValue getConstantValue() {
|
||||
result.isSymbol(getSimpleSymbolValue(g))
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override StringComponent getComponent(int n) { n = 0 and toGenerated(result) = g }
|
||||
}
|
||||
|
||||
class ComplexSymbolLiteral extends SymbolLiteral, TComplexSymbolLiteral { }
|
||||
|
||||
class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral {
|
||||
private Ruby::DelimitedSymbol g;
|
||||
|
||||
DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral {
|
||||
private Ruby::BareSymbol g;
|
||||
|
||||
BareSymbolLiteral() { this = TBareSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
private class RequiredHashKeySymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredSymbol(string s) { s = any(Ruby::HashKeySymbol h).getValue() }
|
||||
}
|
||||
|
||||
private class HashKeySymbolLiteral extends SymbolLiteral, THashKeySymbolLiteral {
|
||||
private Ruby::HashKeySymbol g;
|
||||
|
||||
HashKeySymbolLiteral() { this = THashKeySymbolLiteral(g) }
|
||||
|
||||
final override ConstantValue::ConstantSymbolValue getConstantValue() {
|
||||
result.isSymbol(g.getValue())
|
||||
}
|
||||
|
||||
final override string toString() { result = ":" + g.getValue() }
|
||||
|
||||
final override StringComponent getComponent(int n) { n = 0 and toGenerated(result) = g }
|
||||
}
|
||||
|
||||
private class RequiredCharacterConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = any(Ruby::Character c).getValue() }
|
||||
final override string toString() { result = "__FILE__" }
|
||||
}
|
||||
|
||||
abstract class ArrayLiteralImpl extends Literal, TArrayLiteral {
|
||||
@@ -374,34 +217,56 @@ class HashLiteralImpl extends Literal, THashLiteral {
|
||||
HashLiteralImpl() { this = THashLiteral(g) }
|
||||
|
||||
final int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
|
||||
final override string toString() { result = "{...}" }
|
||||
}
|
||||
|
||||
class RangeLiteralReal extends RangeLiteral, TRangeLiteralReal {
|
||||
abstract class RangeLiteralImpl extends Literal, TRangeLiteral {
|
||||
abstract Expr getBeginImpl();
|
||||
|
||||
abstract Expr getEndImpl();
|
||||
|
||||
abstract predicate isInclusiveImpl();
|
||||
|
||||
abstract predicate isExclusiveImpl();
|
||||
|
||||
final override string toString() {
|
||||
exists(string op |
|
||||
this.isInclusiveImpl() and op = ".."
|
||||
or
|
||||
this.isExclusiveImpl() and op = "..."
|
||||
|
|
||||
result = "_ " + op + " _"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class RangeLiteralReal extends RangeLiteralImpl, TRangeLiteralReal {
|
||||
private Ruby::Range g;
|
||||
|
||||
RangeLiteralReal() { this = TRangeLiteralReal(g) }
|
||||
|
||||
final override Expr getBegin() { toGenerated(result) = g.getBegin() }
|
||||
final override Expr getBeginImpl() { toGenerated(result) = g.getBegin() }
|
||||
|
||||
final override Expr getEnd() { toGenerated(result) = g.getEnd() }
|
||||
final override Expr getEndImpl() { toGenerated(result) = g.getEnd() }
|
||||
|
||||
final override predicate isInclusive() { g instanceof @ruby_range_dotdot }
|
||||
final override predicate isInclusiveImpl() { g instanceof @ruby_range_dotdot }
|
||||
|
||||
final override predicate isExclusive() { g instanceof @ruby_range_dotdotdot }
|
||||
final override predicate isExclusiveImpl() { g instanceof @ruby_range_dotdotdot }
|
||||
}
|
||||
|
||||
class RangeLiteralSynth extends RangeLiteral, TRangeLiteralSynth {
|
||||
class RangeLiteralSynth extends RangeLiteralImpl, TRangeLiteralSynth {
|
||||
private boolean inclusive;
|
||||
|
||||
RangeLiteralSynth() { this = TRangeLiteralSynth(_, _, inclusive) }
|
||||
|
||||
final override Expr getBegin() { result = TIntegerLiteralSynth(this, 0, _) }
|
||||
final override Expr getBeginImpl() { result = TIntegerLiteralSynth(this, 0, _) }
|
||||
|
||||
final override Expr getEnd() { result = TIntegerLiteralSynth(this, 1, _) }
|
||||
final override Expr getEndImpl() { result = TIntegerLiteralSynth(this, 1, _) }
|
||||
|
||||
final override predicate isInclusive() { inclusive = true }
|
||||
final override predicate isInclusiveImpl() { inclusive = true }
|
||||
|
||||
final override predicate isExclusive() { inclusive = false }
|
||||
final override predicate isExclusiveImpl() { inclusive = false }
|
||||
}
|
||||
|
||||
private string getMethodName(MethodName::Token t) {
|
||||
@@ -410,18 +275,387 @@ private string getMethodName(MethodName::Token t) {
|
||||
result = t.(Ruby::Setter).getName().getValue() + "="
|
||||
}
|
||||
|
||||
private class RequiredMethodNameConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getMethodName(_) }
|
||||
}
|
||||
|
||||
class TokenMethodName extends MethodName, TTokenMethodName {
|
||||
class TokenMethodName extends Expr, TTokenMethodName {
|
||||
private MethodName::Token g;
|
||||
|
||||
TokenMethodName() { this = TTokenMethodName(g) }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(getMethodName(g))
|
||||
}
|
||||
final string getValue() { result = getMethodName(g) }
|
||||
|
||||
final override string toString() { result = getMethodName(g) }
|
||||
}
|
||||
|
||||
abstract class StringComponentImpl extends AstNode, TStringComponent {
|
||||
abstract string getValue();
|
||||
}
|
||||
|
||||
abstract class StringTextComponentImpl extends StringComponentImpl, TStringTextComponentNonRegexp {
|
||||
abstract string getRawTextImpl();
|
||||
|
||||
final override string toString() { result = this.getRawTextImpl() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result of unescaping a string text component by replacing `\\` and
|
||||
* `\'` with `\` and `'`, respectively.
|
||||
*
|
||||
* ```rb
|
||||
* 'foo\\bar \'baz\'' # foo\bar 'baz'
|
||||
* ```
|
||||
*/
|
||||
bindingset[text]
|
||||
private string unescapeTextComponent(string text) {
|
||||
result = text.regexpReplaceAll("\\\\(['\\\\])", "$1")
|
||||
}
|
||||
|
||||
class StringTextComponentStringOrHeredocContent extends StringTextComponentImpl,
|
||||
TStringTextComponentNonRegexpStringOrHeredocContent {
|
||||
private Ruby::Token g;
|
||||
|
||||
StringTextComponentStringOrHeredocContent() {
|
||||
this = TStringTextComponentNonRegexpStringOrHeredocContent(g)
|
||||
}
|
||||
|
||||
final override string getValue() { result = this.getUnescapedText() }
|
||||
|
||||
final override string getRawTextImpl() { result = g.getValue() }
|
||||
|
||||
final private string getUnescapedText() { result = unescapeTextComponent(g.getValue()) }
|
||||
}
|
||||
|
||||
private class StringTextComponentSimpleSymbol extends StringTextComponentImpl,
|
||||
TStringTextComponentNonRegexpSimpleSymbol {
|
||||
private Ruby::SimpleSymbol g;
|
||||
|
||||
StringTextComponentSimpleSymbol() { this = TStringTextComponentNonRegexpSimpleSymbol(g) }
|
||||
|
||||
// Tree-sitter gives us value text including the colon, which we skip.
|
||||
private string getSimpleSymbolValue() { result = g.getValue().suffix(1) }
|
||||
|
||||
final override string getValue() { result = this.getSimpleSymbolValue() }
|
||||
|
||||
final override string getRawTextImpl() { result = this.getSimpleSymbolValue() }
|
||||
}
|
||||
|
||||
private class StringTextComponentHashKeySymbol extends StringTextComponentImpl,
|
||||
TStringTextComponentNonRegexpHashKeySymbol {
|
||||
private Ruby::HashKeySymbol g;
|
||||
|
||||
StringTextComponentHashKeySymbol() { this = TStringTextComponentNonRegexpHashKeySymbol(g) }
|
||||
|
||||
final override string getValue() { result = g.getValue() }
|
||||
|
||||
final override string getRawTextImpl() { result = g.getValue() }
|
||||
}
|
||||
|
||||
bindingset[escaped]
|
||||
private string unescapeKnownEscapeSequence(string escaped) {
|
||||
escaped = "\\\\" and result = "\\"
|
||||
or
|
||||
escaped = "\\'" and result = "'"
|
||||
or
|
||||
escaped = "\\\"" and result = "\""
|
||||
or
|
||||
escaped = "\\a" and result = 7.toUnicode()
|
||||
or
|
||||
escaped = "\\b" and result = 8.toUnicode()
|
||||
or
|
||||
escaped = "\\t" and result = "\t"
|
||||
or
|
||||
escaped = "\\n" and result = "\n"
|
||||
or
|
||||
escaped = "\\v" and result = 11.toUnicode()
|
||||
or
|
||||
escaped = "\\f" and result = 12.toUnicode()
|
||||
or
|
||||
escaped = "\\r" and result = "\r"
|
||||
or
|
||||
escaped = "\\e" and result = 27.toUnicode()
|
||||
or
|
||||
escaped = "\\s" and result = " "
|
||||
or
|
||||
escaped = ["\\c?", "\\C-?"] and result = 127.toUnicode()
|
||||
or
|
||||
result = parseOctalInt(escaped.regexpCapture("\\\\([0-7]{1,3})", 1)).toUnicode()
|
||||
or
|
||||
result = parseHexInt(escaped.regexpCapture("\\\\x([0-9a-fA-F]{1,2})", 1)).toUnicode()
|
||||
or
|
||||
result = parseHexInt(escaped.regexpCapture("\\\\u([0-9a-fA-F]{4})", 1)).toUnicode()
|
||||
or
|
||||
result = parseHexInt(escaped.regexpCapture("\\\\u\\{([0-9a-fA-F]{1,6})\\}", 1)).toUnicode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string represented by the escape sequence in `escaped`. For example:
|
||||
*
|
||||
* ```
|
||||
* \\ => \
|
||||
* \141 => a
|
||||
* \u0078 => x
|
||||
* ```
|
||||
*/
|
||||
bindingset[escaped]
|
||||
private string unescapeEscapeSequence(string escaped) {
|
||||
result = unescapeKnownEscapeSequence(escaped)
|
||||
or
|
||||
// Any other character following a backslash is just that character.
|
||||
not exists(unescapeKnownEscapeSequence(escaped)) and
|
||||
result = escaped.suffix(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* An escape sequence component of a string or string-like literal.
|
||||
*/
|
||||
class StringEscapeSequenceComponentImpl extends StringComponentImpl,
|
||||
TStringEscapeSequenceComponentNonRegexp {
|
||||
private Ruby::EscapeSequence g;
|
||||
|
||||
StringEscapeSequenceComponentImpl() { this = TStringEscapeSequenceComponentNonRegexp(g) }
|
||||
|
||||
final override string getValue() { result = this.getUnescapedText() }
|
||||
|
||||
final string getRawTextImpl() { result = g.getValue() }
|
||||
|
||||
final private string getUnescapedText() { result = unescapeEscapeSequence(g.getValue()) }
|
||||
|
||||
final override string toString() { result = this.getRawTextImpl() }
|
||||
}
|
||||
|
||||
class StringInterpolationComponentImpl extends StringComponentImpl,
|
||||
TStringInterpolationComponentNonRegexp {
|
||||
private Ruby::Interpolation g;
|
||||
|
||||
StringInterpolationComponentImpl() { this = TStringInterpolationComponentNonRegexp(g) }
|
||||
|
||||
// requires the CFG
|
||||
final override string getValue() { none() }
|
||||
|
||||
final override string toString() { result = "#{...}" }
|
||||
}
|
||||
|
||||
private class TRegExpComponent =
|
||||
TStringTextComponentRegexp or TStringEscapeSequenceComponentRegexp or
|
||||
TStringInterpolationComponentRegexp;
|
||||
|
||||
abstract class RegExpComponentImpl extends StringComponentImpl, TRegExpComponent { }
|
||||
|
||||
class RegExpTextComponentImpl extends RegExpComponentImpl, TStringTextComponentRegexp {
|
||||
private Ruby::Token g;
|
||||
|
||||
RegExpTextComponentImpl() { this = TStringTextComponentRegexp(g) }
|
||||
|
||||
final override string getValue() {
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
not this.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = g.getValue()
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
class RegExpEscapeSequenceComponentImpl extends RegExpComponentImpl,
|
||||
TStringEscapeSequenceComponentRegexp {
|
||||
private Ruby::EscapeSequence g;
|
||||
|
||||
RegExpEscapeSequenceComponentImpl() { this = TStringEscapeSequenceComponentRegexp(g) }
|
||||
|
||||
final override string getValue() {
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
not this.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = g.getValue()
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
class RegExpInterpolationComponentImpl extends RegExpComponentImpl,
|
||||
TStringInterpolationComponentRegexp {
|
||||
private Ruby::Interpolation g;
|
||||
|
||||
RegExpInterpolationComponentImpl() { this = TStringInterpolationComponentRegexp(g) }
|
||||
|
||||
// requires the CFG
|
||||
final override string getValue() { none() }
|
||||
|
||||
final override string toString() { result = "#{...}" }
|
||||
}
|
||||
|
||||
abstract class StringlikeLiteralImpl extends Expr, TStringlikeLiteral {
|
||||
abstract StringComponentImpl getComponentImpl(int n);
|
||||
|
||||
private string getStartDelimiter() {
|
||||
this instanceof TStringLiteral and
|
||||
result = "\""
|
||||
or
|
||||
this instanceof TRegExpLiteral and
|
||||
result = "/"
|
||||
or
|
||||
this instanceof TSimpleSymbolLiteral and
|
||||
result = ":"
|
||||
or
|
||||
this instanceof TComplexSymbolLiteral and
|
||||
result = ":\""
|
||||
or
|
||||
this instanceof THashKeySymbolLiteral and
|
||||
result = ""
|
||||
or
|
||||
this instanceof TSubshellLiteral and
|
||||
result = "`"
|
||||
or
|
||||
this instanceof THereDoc and
|
||||
result = ""
|
||||
}
|
||||
|
||||
private string getEndDelimiter() {
|
||||
this instanceof TStringLiteral and
|
||||
result = "\""
|
||||
or
|
||||
this instanceof TRegExpLiteral and
|
||||
result = "/"
|
||||
or
|
||||
this instanceof TSimpleSymbolLiteral and
|
||||
result = ""
|
||||
or
|
||||
this instanceof TComplexSymbolLiteral and
|
||||
result = "\""
|
||||
or
|
||||
this instanceof THashKeySymbolLiteral and
|
||||
result = ""
|
||||
or
|
||||
this instanceof TSubshellLiteral and
|
||||
result = "`"
|
||||
or
|
||||
this instanceof THereDoc and
|
||||
result = ""
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
exists(string full, string summary |
|
||||
full =
|
||||
concat(StringComponent c, int i, string s |
|
||||
c = this.getComponentImpl(i) and
|
||||
(
|
||||
s = toGenerated(c).(Ruby::Token).getValue()
|
||||
or
|
||||
not toGenerated(c) instanceof Ruby::Token and
|
||||
s = "#{...}"
|
||||
)
|
||||
|
|
||||
s order by i
|
||||
) and
|
||||
(
|
||||
// summary should be 32 chars max (incl. ellipsis)
|
||||
full.length() > 32 and summary = full.substring(0, 29) + "..."
|
||||
or
|
||||
full.length() <= 32 and summary = full
|
||||
) and
|
||||
result = this.getStartDelimiter() + summary + this.getEndDelimiter()
|
||||
)
|
||||
}
|
||||
|
||||
// 0 components results in the empty string
|
||||
// if all interpolations have a known string value, we will get a result
|
||||
language[monotonicAggregates]
|
||||
final string getStringValue() {
|
||||
result =
|
||||
concat(StringComponentImpl c, int i | c = this.getComponentImpl(i) | c.getValue() order by i)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class StringLiteralImpl extends StringlikeLiteralImpl, TStringLiteral { }
|
||||
|
||||
class RegularStringLiteral extends StringLiteralImpl, TRegularStringLiteral {
|
||||
private Ruby::String g;
|
||||
|
||||
RegularStringLiteral() { this = TRegularStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
class BareStringLiteral extends StringLiteralImpl, TBareStringLiteral {
|
||||
private Ruby::BareString g;
|
||||
|
||||
BareStringLiteral() { this = TBareStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
abstract class SymbolLiteralImpl extends StringlikeLiteralImpl, TSymbolLiteral { }
|
||||
|
||||
class SimpleSymbolLiteral extends SymbolLiteralImpl, TSimpleSymbolLiteral {
|
||||
private Ruby::SimpleSymbol g;
|
||||
|
||||
SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int n) { n = 0 and toGenerated(result) = g }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
abstract class ComplexSymbolLiteral extends SymbolLiteralImpl, TComplexSymbolLiteral { }
|
||||
|
||||
class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral {
|
||||
private Ruby::DelimitedSymbol g;
|
||||
|
||||
DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral {
|
||||
private Ruby::BareSymbol g;
|
||||
|
||||
BareSymbolLiteral() { this = TBareSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
private class HashKeySymbolLiteral extends SymbolLiteralImpl, THashKeySymbolLiteral {
|
||||
private Ruby::HashKeySymbol g;
|
||||
|
||||
HashKeySymbolLiteral() { this = THashKeySymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int n) { n = 0 and toGenerated(result) = g }
|
||||
|
||||
final override string toString() { result = ":" + g.getValue() }
|
||||
}
|
||||
|
||||
class RegExpLiteralImpl extends StringlikeLiteralImpl, TRegExpLiteral {
|
||||
private Ruby::Regex g;
|
||||
|
||||
RegExpLiteralImpl() { this = TRegExpLiteral(g) }
|
||||
|
||||
final override RegExpComponentImpl getComponentImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
class SubshellLiteralImpl extends StringlikeLiteralImpl, TSubshellLiteral {
|
||||
private Ruby::Subshell g;
|
||||
|
||||
SubshellLiteralImpl() { this = TSubshellLiteral(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
class CharacterLiteralImpl extends Expr, TCharacterLiteral {
|
||||
private Ruby::Character g;
|
||||
|
||||
CharacterLiteralImpl() { this = TCharacterLiteral(g) }
|
||||
|
||||
final string getValue() { result = g.getValue() }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
class HereDocImpl extends StringlikeLiteralImpl, THereDoc {
|
||||
private Ruby::HeredocBeginning g;
|
||||
|
||||
HereDocImpl() { this = THereDoc(g) }
|
||||
|
||||
final override StringComponent getComponentImpl(int n) {
|
||||
toGenerated(result) = getHereDocBody(g).getChild(n)
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ private import codeql.ruby.AST
|
||||
private import codeql.ruby.controlflow.BasicBlocks
|
||||
private import codeql.ruby.dataflow.SSA
|
||||
private import codeql.ruby.ast.internal.Constant
|
||||
private import codeql.ruby.ast.internal.Literal
|
||||
private import ControlFlowGraph
|
||||
private import internal.ControlFlowGraphImpl
|
||||
private import internal.Splitting
|
||||
@@ -101,13 +102,6 @@ class ExprCfgNode extends AstCfgNode {
|
||||
/** Gets the underlying expression. */
|
||||
Expr getExpr() { result = e }
|
||||
|
||||
private ExprCfgNode getSource() {
|
||||
exists(Ssa::WriteDefinition def |
|
||||
def.assigns(result) and
|
||||
this = def.getARead()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getConstantValue` instead.
|
||||
*
|
||||
@@ -116,8 +110,7 @@ class ExprCfgNode extends AstCfgNode {
|
||||
deprecated string getValueText() { result = this.getConstantValue().toString() }
|
||||
|
||||
/** Gets the constant value of this expression, if any. */
|
||||
cached
|
||||
ConstantValue getConstantValue() { result = this.getSource().getConstantValue() }
|
||||
ConstantValue getConstantValue() { result = getConstantValue(this) }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a return-like statement. */
|
||||
@@ -142,11 +135,8 @@ class StringComponentCfgNode extends AstCfgNode {
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `RegExpComponent` AST expression. */
|
||||
class RegExpComponentCfgNode extends AstCfgNode {
|
||||
RegExpComponentCfgNode() { this.getNode() instanceof RegExpComponent }
|
||||
|
||||
/** Gets the constant value of this regex component. */
|
||||
ConstantValue getConstantValue() { result = this.getNode().(RegExpComponent).getConstantValue() }
|
||||
class RegExpComponentCfgNode extends StringComponentCfgNode {
|
||||
RegExpComponentCfgNode() { e instanceof RegExpComponent }
|
||||
}
|
||||
|
||||
private AstNode desugar(AstNode n) {
|
||||
@@ -232,8 +222,6 @@ module ExprNodes {
|
||||
override LiteralChildMapping e;
|
||||
|
||||
override Literal getExpr() { result = super.getExpr() }
|
||||
|
||||
override ConstantValue getConstantValue() { result = e.getConstantValue() }
|
||||
}
|
||||
|
||||
private class AssignExprChildMapping extends ExprChildMapping, AssignExpr {
|
||||
@@ -263,31 +251,13 @@ module ExprNodes {
|
||||
|
||||
override Operation getExpr() { result = super.getExpr() }
|
||||
|
||||
/** Gets the operator of this operation. */
|
||||
string getOperator() { result = this.getExpr().getOperator() }
|
||||
|
||||
/** Gets an operand of this operation. */
|
||||
final ExprCfgNode getAnOperand() { e.hasCfgChild(e.getAnOperand(), this, result) }
|
||||
}
|
||||
|
||||
private predicate unaryConstFold(UnaryOperationCfgNode unop, string op, ConstantValue value) {
|
||||
value = unop.getOperand().getConstantValue() and
|
||||
op = unop.getExpr().getOperator()
|
||||
}
|
||||
|
||||
private class RequiredUnaryConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) {
|
||||
exists(ConstantValue value |
|
||||
unaryConstFold(_, "-", value) and
|
||||
i = -value.getInt()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate requiredFloat(float f) {
|
||||
exists(ConstantValue value |
|
||||
unaryConstFold(_, "-", value) and
|
||||
f = -value.getFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `UnaryOperation` AST expression. */
|
||||
class UnaryOperationCfgNode extends OperationCfgNode {
|
||||
private UnaryOperation uo;
|
||||
@@ -298,93 +268,6 @@ module ExprNodes {
|
||||
|
||||
/** Gets the operand of this unary operation. */
|
||||
final ExprCfgNode getOperand() { e.hasCfgChild(uo.getOperand(), this, result) }
|
||||
|
||||
final override ConstantValue getConstantValue() {
|
||||
// TODO: Implement support for complex numbers and rational numbers
|
||||
exists(string op, ConstantValue value | unaryConstFold(this, op, value) |
|
||||
op = "+" and
|
||||
result = value
|
||||
or
|
||||
op = "-" and
|
||||
(
|
||||
result.isInt(-value.getInt())
|
||||
or
|
||||
result.isFloat(-value.getFloat())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate binaryConstFold(
|
||||
BinaryOperationCfgNode binop, string op, ConstantValue left, ConstantValue right
|
||||
) {
|
||||
left = binop.getLeftOperand().getConstantValue() and
|
||||
right = binop.getRightOperand().getConstantValue() and
|
||||
op = binop.getExpr().getOperator()
|
||||
}
|
||||
|
||||
private class RequiredBinaryConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) {
|
||||
exists(string op, ConstantValue left, ConstantValue right |
|
||||
binaryConstFold(_, op, left, right)
|
||||
|
|
||||
op = "+" and
|
||||
i = left.getInt() + right.getInt()
|
||||
or
|
||||
op = "-" and
|
||||
i = left.getInt() - right.getInt()
|
||||
or
|
||||
op = "*" and
|
||||
i = left.getInt() * right.getInt()
|
||||
or
|
||||
op = "/" and
|
||||
i = left.getInt() / right.getInt()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate requiredFloat(float f) {
|
||||
exists(string op, ConstantValue left, ConstantValue right |
|
||||
binaryConstFold(_, op, left, right)
|
||||
|
|
||||
op = "+" and
|
||||
f =
|
||||
[
|
||||
left.getFloat() + right.getFloat(), left.getInt() + right.getFloat(),
|
||||
left.getFloat() + right.getInt()
|
||||
]
|
||||
or
|
||||
op = "-" and
|
||||
f =
|
||||
[
|
||||
left.getFloat() - right.getFloat(), left.getInt() - right.getFloat(),
|
||||
left.getFloat() - right.getInt()
|
||||
]
|
||||
or
|
||||
op = "*" and
|
||||
f =
|
||||
[
|
||||
left.getFloat() * right.getFloat(), left.getInt() * right.getFloat(),
|
||||
left.getFloat() * right.getInt()
|
||||
]
|
||||
or
|
||||
op = "/" and
|
||||
f =
|
||||
[
|
||||
left.getFloat() / right.getFloat(), left.getInt() / right.getFloat(),
|
||||
left.getFloat() / right.getInt()
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
override predicate requiredString(string s) {
|
||||
exists(string op, ConstantValue left, ConstantValue right |
|
||||
binaryConstFold(_, op, left, right)
|
||||
|
|
||||
op = "+" and
|
||||
s = left.getString() + right.getString() and
|
||||
s.length() <= 10000
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `BinaryOperation` AST expression. */
|
||||
@@ -400,59 +283,6 @@ module ExprNodes {
|
||||
|
||||
/** Gets the right operand of this binary operation. */
|
||||
final ExprCfgNode getRightOperand() { e.hasCfgChild(bo.getRightOperand(), this, result) }
|
||||
|
||||
final override ConstantValue getConstantValue() {
|
||||
// TODO: Implement support for complex numbers and rational numbers
|
||||
exists(string op, ConstantValue left, ConstantValue right |
|
||||
binaryConstFold(this, op, left, right)
|
||||
|
|
||||
op = "+" and
|
||||
(
|
||||
result.isInt(left.getInt() + right.getInt())
|
||||
or
|
||||
result
|
||||
.isFloat([
|
||||
left.getFloat() + right.getFloat(), left.getInt() + right.getFloat(),
|
||||
left.getFloat() + right.getInt()
|
||||
])
|
||||
or
|
||||
result.isString(left.getString() + right.getString())
|
||||
)
|
||||
or
|
||||
op = "-" and
|
||||
(
|
||||
result.isInt(left.getInt() - right.getInt())
|
||||
or
|
||||
result
|
||||
.isFloat([
|
||||
left.getFloat() - right.getFloat(), left.getInt() - right.getFloat(),
|
||||
left.getFloat() - right.getInt()
|
||||
])
|
||||
)
|
||||
or
|
||||
op = "*" and
|
||||
(
|
||||
result.isInt(left.getInt() * right.getInt())
|
||||
or
|
||||
result
|
||||
.isFloat([
|
||||
left.getFloat() * right.getFloat(), left.getInt() * right.getFloat(),
|
||||
left.getFloat() * right.getInt()
|
||||
])
|
||||
)
|
||||
or
|
||||
op = "/" and
|
||||
(
|
||||
result.isInt(left.getInt() / right.getInt())
|
||||
or
|
||||
result
|
||||
.isFloat([
|
||||
left.getFloat() / right.getFloat(), left.getInt() / right.getFloat(),
|
||||
left.getFloat() / right.getInt()
|
||||
])
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockArgumentChildMapping extends ExprChildMapping, BlockArgument {
|
||||
@@ -722,8 +552,6 @@ module ExprNodes {
|
||||
|
||||
/** Gets the scope expression. */
|
||||
final ExprCfgNode getScopeExpr() { e.hasCfgChild(e.getScopeExpr(), this, result) }
|
||||
|
||||
override ConstantValue getConstantValue() { result = this.getExpr().getConstantValue() }
|
||||
}
|
||||
|
||||
private class StmtSequenceChildMapping extends ExprChildMapping, StmtSequence {
|
||||
@@ -819,11 +647,8 @@ module ExprNodes {
|
||||
class StringInterpolationComponentCfgNode extends StringComponentCfgNode, StmtSequenceCfgNode {
|
||||
StringInterpolationComponentCfgNode() { this.getNode() instanceof StringInterpolationComponent }
|
||||
|
||||
// If last statement in the interpolation is a constant or local variable read,
|
||||
// we attempt to look up its string value.
|
||||
// If there's a result, we return that as the string value of the interpolation.
|
||||
final override ConstantValue getConstantValue() {
|
||||
result = this.getLastStmt().getConstantValue()
|
||||
result = StmtSequenceCfgNode.super.getConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,10 +656,8 @@ module ExprNodes {
|
||||
class RegExpInterpolationComponentCfgNode extends RegExpComponentCfgNode, StmtSequenceCfgNode {
|
||||
RegExpInterpolationComponentCfgNode() { this.getNode() instanceof RegExpInterpolationComponent }
|
||||
|
||||
// If last statement in the interpolation is a constant or local variable read,
|
||||
// attempt to look up its definition and return the definition's `getConstantValue()`.
|
||||
final override ConstantValue getConstantValue() {
|
||||
result = this.getLastStmt().getConstantValue()
|
||||
result = StmtSequenceCfgNode.super.getConstantValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,56 +665,17 @@ module ExprNodes {
|
||||
override predicate relevantChild(AstNode n) { n = this.getComponent(_) }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string getStringComponentCfgNodeValue(StringComponentCfgNode c) {
|
||||
result = c.getConstantValue().toString()
|
||||
}
|
||||
|
||||
// 0 components results in the empty string
|
||||
// if all interpolations have a known string value, we will get a result
|
||||
language[monotonicAggregates]
|
||||
private string getStringlikeLiteralCfgNodeValue(StringlikeLiteralCfgNode n) {
|
||||
result =
|
||||
concat(StringComponentCfgNode c, int i |
|
||||
c = n.getComponent(i)
|
||||
|
|
||||
getStringComponentCfgNodeValue(c) order by i
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredStringlikeLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
exists(StringlikeLiteralCfgNode n |
|
||||
s = getStringlikeLiteralCfgNodeValue(n) and
|
||||
not n.getExpr() instanceof SymbolLiteral
|
||||
)
|
||||
}
|
||||
|
||||
override predicate requiredSymbol(string s) {
|
||||
exists(StringlikeLiteralCfgNode n |
|
||||
s = getStringlikeLiteralCfgNodeValue(n) and
|
||||
n.getExpr() instanceof SymbolLiteral
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `StringlikeLiteral` AST expression. */
|
||||
class StringlikeLiteralCfgNode extends ExprCfgNode {
|
||||
override StringlikeLiteralChildMapping e;
|
||||
|
||||
final override StringlikeLiteral getExpr() { result = super.getExpr() }
|
||||
override StringlikeLiteral getExpr() { result = super.getExpr() }
|
||||
|
||||
/** Gets the `n`th component of this `StringlikeLiteral` */
|
||||
StringComponentCfgNode getComponent(int n) { e.hasCfgChild(e.getComponent(n), this, result) }
|
||||
|
||||
/** Gets a component of this `StringlikeLiteral` */
|
||||
StringComponentCfgNode getAComponent() { result = this.getComponent(_) }
|
||||
|
||||
final override ConstantValue getConstantValue() {
|
||||
if this.getExpr() instanceof SymbolLiteral
|
||||
then result.isSymbol(getStringlikeLiteralCfgNodeValue(this))
|
||||
else result.isString(getStringlikeLiteralCfgNodeValue(this))
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `StringLiteral` AST expression. */
|
||||
@@ -901,40 +685,15 @@ module ExprNodes {
|
||||
final override StringLiteral getExpr() { result = super.getExpr() }
|
||||
}
|
||||
|
||||
private class RegExpLiteralChildMapping extends ExprChildMapping, RegExpLiteral {
|
||||
override predicate relevantChild(AstNode n) { n = this.getComponent(_) }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string getRegExpComponentCfgNodeValue(RegExpComponentCfgNode c) {
|
||||
result = c.getConstantValue().toString()
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private string getRegExpLiteralCfgNodeValue(RegExpLiteralCfgNode n) {
|
||||
result =
|
||||
concat(RegExpComponentCfgNode c, int i |
|
||||
c = n.getComponent(i)
|
||||
|
|
||||
getRegExpComponentCfgNodeValue(c) order by i
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRexExpLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getRegExpLiteralCfgNodeValue(_) }
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `RegExpLiteral` AST expression. */
|
||||
class RegExpLiteralCfgNode extends ExprCfgNode {
|
||||
override RegExpLiteralChildMapping e;
|
||||
class RegExpLiteralCfgNode extends StringlikeLiteralCfgNode {
|
||||
RegExpLiteralCfgNode() { e instanceof RegExpLiteral }
|
||||
|
||||
RegExpComponentCfgNode getComponent(int n) { e.hasCfgChild(e.getComponent(n), this, result) }
|
||||
final override RegExpComponentCfgNode getComponent(int n) { result = super.getComponent(n) }
|
||||
|
||||
final override RegExpComponentCfgNode getAComponent() { result = super.getAComponent() }
|
||||
|
||||
final override RegExpLiteral getExpr() { result = super.getExpr() }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(getRegExpLiteralCfgNodeValue(this))
|
||||
}
|
||||
}
|
||||
|
||||
/** A control-flow node that wraps a `ComparisonOperation` AST expression. */
|
||||
|
||||
@@ -83,6 +83,7 @@ exprValue
|
||||
| constants/constants.rb:20:14:20:19 | "Vera" | Vera |
|
||||
| constants/constants.rb:20:22:20:28 | "Chuck" | Chuck |
|
||||
| constants/constants.rb:20:31:20:36 | "Dave" | Dave |
|
||||
| constants/constants.rb:23:15:23:27 | #{...} | Helloconst_aconst_b |
|
||||
| constants/constants.rb:23:18:23:25 | GREETING | Helloconst_aconst_b |
|
||||
| constants/constants.rb:28:11:28:15 | "foo" | foo |
|
||||
| constants/constants.rb:32:16:32:17 | 42 | 42 |
|
||||
@@ -379,6 +380,7 @@ exprValue
|
||||
| escape_sequences/escapes.rb:38:1:38:6 | "\\C-?" | C-? |
|
||||
| escape_sequences/escapes.rb:43:5:43:9 | "\\\\." | \\. |
|
||||
| escape_sequences/escapes.rb:44:1:44:6 | "#{...}" | \\. |
|
||||
| escape_sequences/escapes.rb:44:2:44:5 | #{...} | \\. |
|
||||
| escape_sequences/escapes.rb:44:4:44:4 | a | \\. |
|
||||
| escape_sequences/escapes.rb:48:1:48:4 | /\\n/ | \\n |
|
||||
| escape_sequences/escapes.rb:49:1:49:4 | /\\p/ | \\p |
|
||||
@@ -386,7 +388,9 @@ exprValue
|
||||
| escape_sequences/escapes.rb:53:5:53:9 | "\\\\." | \\. |
|
||||
| escape_sequences/escapes.rb:54:5:54:8 | /\\./ | \\. |
|
||||
| escape_sequences/escapes.rb:55:1:55:10 | /#{...}#{...}/ | \\.\\. |
|
||||
| escape_sequences/escapes.rb:55:2:55:5 | #{...} | \\. |
|
||||
| escape_sequences/escapes.rb:55:4:55:4 | a | \\. |
|
||||
| escape_sequences/escapes.rb:55:6:55:9 | #{...} | \\. |
|
||||
| escape_sequences/escapes.rb:55:8:55:8 | b | \\. |
|
||||
| escape_sequences/escapes.rb:58:4:58:9 | "foo \\n" | foo\\n |
|
||||
| escape_sequences/escapes.rb:58:11:58:13 | "bar" | bar |
|
||||
@@ -488,10 +492,12 @@ exprValue
|
||||
| literals/literals.rb:69:1:69:12 | "foo\\ bar" | foo\\ bar |
|
||||
| literals/literals.rb:70:1:70:12 | "foo\\ bar" | foo bar |
|
||||
| literals/literals.rb:71:1:71:20 | "2 + 2 = #{...}" | 2 + 2 = 4 |
|
||||
| literals/literals.rb:71:10:71:19 | #{...} | 4 |
|
||||
| literals/literals.rb:71:13:71:13 | 2 | 2 |
|
||||
| literals/literals.rb:71:13:71:17 | ... + ... | 4 |
|
||||
| literals/literals.rb:71:17:71:17 | 2 | 2 |
|
||||
| literals/literals.rb:72:1:72:22 | "3 + 4 = #{...}" | 3 + 4 = 7 |
|
||||
| literals/literals.rb:72:12:72:21 | #{...} | 7 |
|
||||
| literals/literals.rb:72:15:72:15 | 3 | 3 |
|
||||
| literals/literals.rb:72:15:72:19 | ... + ... | 7 |
|
||||
| literals/literals.rb:72:19:72:19 | 4 | 4 |
|
||||
@@ -505,24 +511,30 @@ exprValue
|
||||
| literals/literals.rb:76:15:76:19 | "baz" | baz |
|
||||
| literals/literals.rb:77:1:77:5 | "foo" | foo |
|
||||
| literals/literals.rb:77:7:77:21 | "bar#{...}" | bar1 |
|
||||
| literals/literals.rb:77:11:77:20 | #{...} | 1 |
|
||||
| literals/literals.rb:77:14:77:14 | 1 | 1 |
|
||||
| literals/literals.rb:77:14:77:18 | ... * ... | 1 |
|
||||
| literals/literals.rb:77:18:77:18 | 1 | 1 |
|
||||
| literals/literals.rb:77:23:77:27 | "baz" | baz |
|
||||
| literals/literals.rb:78:1:78:35 | "foo #{...} qux" | foo bar 5 baz qux |
|
||||
| literals/literals.rb:78:6:78:30 | #{...} | bar 5 baz |
|
||||
| literals/literals.rb:78:9:78:28 | "bar #{...} baz" | bar 5 baz |
|
||||
| literals/literals.rb:78:14:78:23 | #{...} | 5 |
|
||||
| literals/literals.rb:78:17:78:17 | 2 | 2 |
|
||||
| literals/literals.rb:78:17:78:21 | ... + ... | 5 |
|
||||
| literals/literals.rb:78:21:78:21 | 3 | 3 |
|
||||
| literals/literals.rb:79:1:79:22 | "foo #{...}" | foo 10 |
|
||||
| literals/literals.rb:79:6:79:21 | #{...} | 10 |
|
||||
| literals/literals.rb:79:17:79:17 | 1 | 1 |
|
||||
| literals/literals.rb:79:17:79:19 | ... + ... | 10 |
|
||||
| literals/literals.rb:79:19:79:19 | 9 | 9 |
|
||||
| literals/literals.rb:80:7:80:11 | "bar" | bar |
|
||||
| literals/literals.rb:81:7:81:11 | "bar" | bar |
|
||||
| literals/literals.rb:82:1:82:14 | "foo #{...}" | foo bar |
|
||||
| literals/literals.rb:82:6:82:13 | #{...} | bar |
|
||||
| literals/literals.rb:82:9:82:11 | bar | bar |
|
||||
| literals/literals.rb:83:1:83:14 | "foo #{...}" | foo bar |
|
||||
| literals/literals.rb:83:6:83:13 | #{...} | bar |
|
||||
| literals/literals.rb:83:9:83:11 | BAR | bar |
|
||||
| literals/literals.rb:86:1:86:2 | ?x | ?x |
|
||||
| literals/literals.rb:87:1:87:3 | ?\\n | ?\\n |
|
||||
@@ -542,10 +554,13 @@ exprValue
|
||||
| literals/literals.rb:102:1:102:10 | :"wibble" | :wibble |
|
||||
| literals/literals.rb:103:1:103:17 | :"wibble wobble" | :wibble wobble |
|
||||
| literals/literals.rb:104:1:104:30 | :"foo_#{...}_#{...}_#{...}" | :foo_4_bar_bar |
|
||||
| literals/literals.rb:104:7:104:15 | #{...} | 4 |
|
||||
| literals/literals.rb:104:10:104:10 | 2 | 2 |
|
||||
| literals/literals.rb:104:10:104:14 | ... + ... | 4 |
|
||||
| literals/literals.rb:104:14:104:14 | 2 | 2 |
|
||||
| literals/literals.rb:104:17:104:22 | #{...} | bar |
|
||||
| literals/literals.rb:104:19:104:21 | bar | bar |
|
||||
| literals/literals.rb:104:24:104:29 | #{...} | bar |
|
||||
| literals/literals.rb:104:26:104:28 | BAR | bar |
|
||||
| literals/literals.rb:105:1:105:30 | :"foo_#{ 2 + 2}_#{bar}_#{BAR}" | :foo_#{ 2 + 2}_#{bar}_#{BAR} |
|
||||
| literals/literals.rb:106:1:106:18 | :"foo_#{ 3 - 2 }" | :foo_#{ 3 - 2 } |
|
||||
@@ -568,12 +583,15 @@ exprValue
|
||||
| literals/literals.rb:117:12:117:14 | "baz" | baz |
|
||||
| literals/literals.rb:118:4:118:6 | "foo" | foo |
|
||||
| literals/literals.rb:118:8:118:16 | "bar#{...}" | bar2 |
|
||||
| literals/literals.rb:118:11:118:16 | #{...} | 2 |
|
||||
| literals/literals.rb:118:13:118:13 | 1 | 1 |
|
||||
| literals/literals.rb:118:13:118:15 | ... + ... | 2 |
|
||||
| literals/literals.rb:118:15:118:15 | 1 | 1 |
|
||||
| literals/literals.rb:118:18:118:23 | "#{...}" | bar |
|
||||
| literals/literals.rb:118:18:118:23 | #{...} | bar |
|
||||
| literals/literals.rb:118:20:118:22 | bar | bar |
|
||||
| literals/literals.rb:118:25:118:30 | "#{...}" | bar |
|
||||
| literals/literals.rb:118:25:118:30 | #{...} | bar |
|
||||
| literals/literals.rb:118:27:118:29 | BAR | bar |
|
||||
| literals/literals.rb:118:32:118:34 | "baz" | baz |
|
||||
| literals/literals.rb:119:4:119:6 | "foo" | foo |
|
||||
@@ -589,11 +607,14 @@ exprValue
|
||||
| literals/literals.rb:124:12:124:14 | :"baz" | :baz |
|
||||
| literals/literals.rb:125:4:125:6 | :"foo" | :foo |
|
||||
| literals/literals.rb:125:8:125:20 | :"bar#{...}" | :bar6 |
|
||||
| literals/literals.rb:125:11:125:20 | #{...} | 6 |
|
||||
| literals/literals.rb:125:14:125:14 | 2 | 2 |
|
||||
| literals/literals.rb:125:14:125:18 | ... + ... | 6 |
|
||||
| literals/literals.rb:125:18:125:18 | 4 | 4 |
|
||||
| literals/literals.rb:125:22:125:27 | #{...} | bar |
|
||||
| literals/literals.rb:125:22:125:27 | :"#{...}" | :bar |
|
||||
| literals/literals.rb:125:24:125:26 | bar | bar |
|
||||
| literals/literals.rb:125:29:125:34 | #{...} | bar |
|
||||
| literals/literals.rb:125:29:125:34 | :"#{...}" | :bar |
|
||||
| literals/literals.rb:125:31:125:33 | BAR | bar |
|
||||
| literals/literals.rb:125:36:125:38 | :"baz" | :baz |
|
||||
@@ -630,12 +651,16 @@ exprValue
|
||||
| literals/literals.rb:143:1:143:7 | `ls -l` | ls -l |
|
||||
| literals/literals.rb:144:1:144:9 | `ls -l` | ls -l |
|
||||
| literals/literals.rb:145:1:145:32 | `du -d #{...} #{...} #{...}` | du -d 2 bar bar |
|
||||
| literals/literals.rb:145:8:145:17 | #{...} | 2 |
|
||||
| literals/literals.rb:145:11:145:11 | 1 | 1 |
|
||||
| literals/literals.rb:145:11:145:15 | ... + ... | 2 |
|
||||
| literals/literals.rb:145:15:145:15 | 1 | 1 |
|
||||
| literals/literals.rb:145:19:145:24 | #{...} | bar |
|
||||
| literals/literals.rb:145:21:145:23 | bar | bar |
|
||||
| literals/literals.rb:145:26:145:31 | #{...} | bar |
|
||||
| literals/literals.rb:145:28:145:30 | BAR | bar |
|
||||
| literals/literals.rb:146:1:146:20 | `du -d #{...}` | du -d 1 |
|
||||
| literals/literals.rb:146:10:146:19 | #{...} | 1 |
|
||||
| literals/literals.rb:146:13:146:13 | 5 | 5 |
|
||||
| literals/literals.rb:146:13:146:17 | ... - ... | 1 |
|
||||
| literals/literals.rb:146:17:146:17 | 4 | 4 |
|
||||
@@ -644,20 +669,26 @@ exprValue
|
||||
| literals/literals.rb:151:1:151:6 | /foo/ | foo |
|
||||
| literals/literals.rb:152:1:152:13 | /foo+\\sbar\\S/ | foo+\\sbar\\S |
|
||||
| literals/literals.rb:153:1:153:30 | /foo#{...}bar#{...}#{...}/ | foo2barbarbar |
|
||||
| literals/literals.rb:153:5:153:14 | #{...} | 2 |
|
||||
| literals/literals.rb:153:8:153:8 | 1 | 1 |
|
||||
| literals/literals.rb:153:8:153:12 | ... + ... | 2 |
|
||||
| literals/literals.rb:153:12:153:12 | 1 | 1 |
|
||||
| literals/literals.rb:153:18:153:23 | #{...} | bar |
|
||||
| literals/literals.rb:153:20:153:22 | bar | bar |
|
||||
| literals/literals.rb:153:24:153:29 | #{...} | bar |
|
||||
| literals/literals.rb:153:26:153:28 | BAR | bar |
|
||||
| literals/literals.rb:155:1:155:4 | // | |
|
||||
| literals/literals.rb:156:1:156:7 | /foo/ | foo |
|
||||
| literals/literals.rb:157:1:157:8 | /foo/ | foo |
|
||||
| literals/literals.rb:158:1:158:15 | /foo+\\sbar\\S/ | foo+\\sbar\\S |
|
||||
| literals/literals.rb:159:1:159:32 | /foo#{...}bar#{...}#{...}/ | foo2barbarbar |
|
||||
| literals/literals.rb:159:7:159:16 | #{...} | 2 |
|
||||
| literals/literals.rb:159:10:159:10 | 1 | 1 |
|
||||
| literals/literals.rb:159:10:159:14 | ... + ... | 2 |
|
||||
| literals/literals.rb:159:14:159:14 | 1 | 1 |
|
||||
| literals/literals.rb:159:20:159:25 | #{...} | bar |
|
||||
| literals/literals.rb:159:22:159:24 | bar | bar |
|
||||
| literals/literals.rb:159:26:159:31 | #{...} | bar |
|
||||
| literals/literals.rb:159:28:159:30 | BAR | bar |
|
||||
| literals/literals.rb:163:1:163:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | abcdefghijklmnopqrstuvwxyzabcdef |
|
||||
| literals/literals.rb:164:1:164:35 | "foobarfoobarfoobarfoobarfooba..." | foobarfoobarfoobarfoobarfoobarfoo |
|
||||
@@ -681,6 +712,7 @@ exprValue
|
||||
| misc/misc.rb:3:24:3:25 | [] | [] |
|
||||
| misc/misc.rb:3:28:3:30 | []= | []= |
|
||||
| misc/misc.rb:4:7:4:19 | :"foo_#{...}" | :foo_bar |
|
||||
| misc/misc.rb:4:13:4:18 | #{...} | bar |
|
||||
| misc/misc.rb:4:15:4:17 | bar | bar |
|
||||
| misc/misc.rb:5:7:5:9 | nil | nil |
|
||||
| misc/misc.rb:5:12:5:15 | true | true |
|
||||
@@ -694,6 +726,7 @@ exprValue
|
||||
| misc/misc.rb:9:7:9:11 | super | super |
|
||||
| misc/misc.rb:9:13:9:16 | self | self |
|
||||
| misc/misc.rb:10:7:10:17 | :"\\n#{...}" | :\nbar |
|
||||
| misc/misc.rb:10:11:10:16 | #{...} | bar |
|
||||
| misc/misc.rb:10:13:10:15 | bar | bar |
|
||||
| misc/misc.rb:10:19:10:24 | :"foo" | :foo |
|
||||
| modules/classes.rb:11:28:11:31 | :baz | :baz |
|
||||
@@ -716,6 +749,7 @@ exprValue
|
||||
| modules/modules.rb:55:8:55:30 | "module Foo::Bar again" | module Foo::Bar again |
|
||||
| modules/modules.rb:56:17:56:17 | 4 | 4 |
|
||||
| modules/toplevel.rb:1:6:1:12 | "world" | world |
|
||||
| modules/toplevel.rb:3:12:3:16 | "!!!" | !!! |
|
||||
| modules/toplevel.rb:5:14:5:20 | "hello" | hello |
|
||||
| operations/operations.rb:3:5:3:5 | 0 | 0 |
|
||||
| operations/operations.rb:4:5:4:5 | 0 | 0 |
|
||||
|
||||
Reference in New Issue
Block a user