Ruby: Rework getConstantValue implementation

This commit is contained in:
Tom Hvitved
2022-03-18 11:32:46 +01:00
parent 80fb021e32
commit 99ddfb489f
7 changed files with 1025 additions and 818 deletions

View File

@@ -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" }
}

View File

@@ -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. */

View File

@@ -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 + " _"
)
}
}
/**

View File

@@ -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

View File

@@ -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() }
}

View File

@@ -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. */

View File

@@ -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 |