Update AST library

This commit is contained in:
Arthur Baars
2021-11-22 15:57:49 +01:00
parent ec0bd24b64
commit 078a2aa03b
8 changed files with 706 additions and 39 deletions

View File

@@ -348,6 +348,9 @@ class CaseExpr extends ControlExpr, TCaseExpr {
*/
final Expr getABranch() { result = this.getBranch(_) }
/** Gets the `n`th `when` branch of this case expression. */
final WhenExpr getWhenBranch(int n) { result = this.getBranch(n) }
/** Gets a `when` branch of this case expression. */
final WhenExpr getAWhenBranch() { result = this.getABranch() }
@@ -367,6 +370,10 @@ class CaseExpr extends ControlExpr, TCaseExpr {
pred = "getValue" and result = this.getValue()
or
pred = "getBranch" and result = this.getBranch(_)
or
pred = "getWhenBranch" and result = this.getWhenBranch(_)
or
pred = "getElseBranch" and result = this.getElseBranch()
}
}
@@ -422,6 +429,204 @@ class WhenExpr extends Expr, TWhenExpr {
}
}
/**
* A `case` statement used for pattern matching. For example:
* ```rb
* config = {db: {user: 'admin', password: 'abc123'}}
* case config
* in db: {user:} # matches subhash and puts matched value in variable user
* puts "Connect with user '#{user}'"
* in connection: {username: } unless username == 'admin'
* puts "Connect with user '#{username}'"
* else
* puts "Unrecognized structure of config"
* end
* ```
*/
class CaseMatch extends ControlExpr, TCaseMatch {
private Ruby::CaseMatch g;
CaseMatch() { this = TCaseMatch(g) }
final override string getAPrimaryQlClass() { result = "CaseMatch" }
/**
* Gets the expression being matched. For example, `foo` in the following example.
* ```rb
* case foo
* in 0
* puts 'zero'
* in 1
* puts 'one'
* end
* ```
*/
final Expr getValue() { toGenerated(result) = g.getValue() }
/**
* Gets the `n`th branch of this case expression, either an `InClause` or a
* `StmtSequence`.
*/
final Expr getBranch(int n) {
toGenerated(result) = g.getClauses(n)
or
n = count(g.getClauses(_)) and toGenerated(result) = g.getElse()
}
/**
* Gets a branch of this case expression, either an `InClause` or an
* `StmtSequence`.
*/
final Expr getABranch() { result = this.getBranch(_) }
/** Gets the `n`th `in` clause of this case expression. */
final InClause getInClause(int n) { result = this.getBranch(n) }
/** Gets an `in` clause of this case expression. */
final InClause getAnInClause() { result = this.getABranch() }
/** Gets the `else` branch of this case expression, if any. */
final StmtSequence getElseBranch() { result = this.getABranch() }
/**
* Gets the number of branches of this case expression.
*/
final int getNumberOfBranches() { result = count(this.getBranch(_)) }
final override string toString() { result = "case ... in" }
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getValue" and result = this.getValue()
or
pred = "getBranch" and result = this.getBranch(_)
or
pred = "getInClause" and result = this.getInClause(_)
or
pred = "getElseBranch" and result = this.getElseBranch()
}
}
/**
* An `in` clause of a `case` expression.
* ```rb
* case foo
* in [ a ] then a
* end
* ```
*/
class InClause extends Expr, TInClause {
private Ruby::InClause g;
InClause() { this = TInClause(g) }
final override string getAPrimaryQlClass() { result = "InClause" }
/** Gets the body of this case-in expression. */
final Stmt getBody() { toGenerated(result) = g.getBody() }
/**
* Gets the pattern in this case-in expression. In the
* following example, the pattern is `Point{ x:, y: }`.
* ```rb
* case foo
* in Point{ x:, y: }
* x + y
* end
* ```
*/
final CasePattern getPattern() { toGenerated(result) = g.getPattern() }
/**
* Gets the pattern guard in this case-in expression. In the
* following example, the pattern guard is `x > 10`.
* ```rb
* case foo
* in [ x ] if x > 10 then ...
* end
* ```
*/
final PatternGuard getPatternGuard() { toGenerated(result) = g.getGuard() }
final override string toString() { result = "in ... then ..." }
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getBody" and result = this.getBody()
or
pred = "getPattern" and result = this.getPattern()
or
pred = "getGuard" and result = this.getPatternGuard()
}
}
/**
* A guard used in pattern matching. For example:
* ```rb
* in pattern if guard
* in pattern unless guard
* ```
*/
class PatternGuard extends AstNode, TPatternGuard {
/**
* Gets the condition expression. For example, the result is `foo` in the
* following:
* ```rb
* if foo
* unless foo
* ```
*/
Expr getCondition() { none() }
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getCondition" and result = this.getCondition()
}
}
/**
* An `if` pattern guard. For example:
* ```rb
* case foo
* in [ bar] if bar > 10
* end
* ```
*/
class IfGuard extends PatternGuard, TIfGuard {
private Ruby::IfGuard g;
IfGuard() { this = TIfGuard(g) }
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
final override string getAPrimaryQlClass() { result = "IfGuard" }
final override string toString() { result = "if ..." }
}
/**
* An `unless` pattern guard. For example:
* ```rb
* case foo
* in [ bar] unless bar > 10
* end
* ```
*/
class UnlessGuard extends PatternGuard, TUnlessGuard {
private Ruby::UnlessGuard g;
UnlessGuard() { this = TUnlessGuard(g) }
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
final override string getAPrimaryQlClass() { result = "UnlessGuard" }
final override string toString() { result = "unless ..." }
}
/**
* A loop. That is, a `for` loop, a `while` or `until` loop, or their
* expression-modifier variants.

View File

@@ -223,6 +223,40 @@ private class FalseLiteral extends BooleanLiteral, TFalseLiteral {
final override predicate isFalse() { any() }
}
/**
* An `__ENCODING__` literal.
*/
class EncodingLiteral extends Literal, TEncoding {
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 string getValueText() { result = "UTF-8" }
}
/**
* A `__LINE__` literal.
*/
class LineLiteral extends Literal, TLine {
final override string getAPrimaryQlClass() { result = "LineLiteral" }
final override string toString() { result = "__LINE__" }
override string getValueText() { result = this.getLocation().getStartLine().toString() }
}
/**
* A `__FILE__` literal.
*/
class FileLiteral extends Literal, TFile {
final override string getAPrimaryQlClass() { result = "FileLiteral" }
final override string toString() { result = "__FILE__" }
override string getValueText() { result = this.getLocation().getFile().getAbsolutePath() }
}
/**
* The base class for a component of a string: `StringTextComponent`,
* `StringEscapeSequenceComponent`, or `StringInterpolationComponent`.

View File

@@ -131,6 +131,23 @@ class HashSplatParameter extends NamedParameter, THashSplatParameter {
final override string getName() { result = g.getName().getValue() }
}
/**
* A `nil` hash splat (`**nil`) indicating that there are no keyword parameters or keyword patterns.
* For example:
* ```rb
* def foo(bar, **nil)
* case bar
* in { x:, **nil } then puts x
* end
* end
* ```
*/
class HashSplatNilParameter extends Parameter, THashSplatNilParameter {
final override string getAPrimaryQlClass() { result = "HashSplatNilParameter" }
final override string toString() { result = "**nil" }
}
/**
* A keyword parameter, including a default value if the parameter is optional.
* For example, in the following example, `foo` is a keyword parameter with a

View File

@@ -97,3 +97,322 @@ class TuplePattern extends Pattern, TTuplePattern {
override AstNode getAChild(string pred) { pred = "getElement" and result = this.getElement(_) }
}
private class TPatternNode =
TArrayPattern or TFindPattern or THashPattern or TAlternativePattern or TAsPattern or
TVariableReferencePattern;
private class TPattern =
TPatternNode or TLiteral or TLambda or TConstantAccess or TLocalVariableAccess or
TUnaryArithmeticOperation;
/**
* A pattern used in a `case-in` expression. For example
* ```rb
* case expr
* in [ x ] then ...
* in Point(a:, b:) then ...
* in Integer => x then ...
* end
* ```
*/
class CasePattern extends AstNode, TPattern {
CasePattern() { casePattern(toGenerated(this)) }
}
/**
* An array pattern, for example:
* ```rb
* in []
* in ["first", Integer => x, "last"]
* in ["a", Integer => x, *]
* in ["a", Integer => x, ]
* in [1, 2, *x, 7, 8]
* in [*init, 7, 8]
* in List["a", Integer => x, *tail]
* ```
*/
class ArrayPattern extends CasePattern, TArrayPattern {
private Ruby::ArrayPattern g;
ArrayPattern() { this = TArrayPattern(g) }
/** Gets the class this pattern matches objects against, if any. */
ConstantReadAccess getClass() { toGenerated(result) = g.getClass() }
/**
* Gets the `n`th element of this list pattern's prefix, i.e. the elements `1, ^two, 3`
* in the following examples:
* ```
* in [ 1, ^two, 3 ]
* in [ 1, ^two, 3, ]
* in [ 1, ^two, 3, *, 4 , 5]
* in [ 1, ^two, 3, *more]
* ```
*/
CasePattern getPrefixElement(int n) {
toGenerated(result) = g.getChild(n) and
(
n < this.restIndex()
or
not exists(restIndex())
)
}
/**
* Gets the `n`th element of this list pattern's suffix, i.e. the elements `4, 5`
* in the following examples:
* ```
* in [ *, 4, 5 ]
* in [ 1, 2, 3, *middle, 4 , 5]
* ```
*/
CasePattern getSuffixElement(int n) { toGenerated(result) = g.getChild(n + this.restIndex() + 1) }
/**
* Gets the variable of the rest token, if any. For example `middle` in `the following array pattern.
* ```rb
* [ 1, 2, 3, *middle, 4 , 5]
* ```
*/
LocalVariableWriteAccess getRestVariable() {
toGenerated(result) = g.getChild(restIndex()).(Ruby::SplatParameter).getName()
}
/**
* Holds if this pattern permits any unmatched remaining elements, i.e. the pattern does not have a trailing `,`
* and does not contain a rest token (`*` or `*name`) either.
*/
predicate allowUnmatchedElements() { not exists(this.restIndex()) }
private int restIndex() { g.getChild(result) instanceof Ruby::SplatParameter }
final override string getAPrimaryQlClass() { result = "ArrayPattern" }
final override string toString() { result = "[ ..., * ]" }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getClass" and result = this.getClass()
or
pred = "getPrefixElement" and result = this.getPrefixElement(_)
or
pred = "getSuffixElement" and result = this.getSuffixElement(_)
or
pred = "getRestVariable" and result = this.getRestVariable()
}
}
/**
* A find pattern, for example:
* ```rb
* in [*, "a", Integer => x, *]
* in List[*init, "a", Integer => x, *tail]
* in List[*, "a", Integer => x, *]
* ```
*/
class FindPattern extends CasePattern, TFindPattern {
private Ruby::FindPattern g;
FindPattern() { this = TFindPattern(g) }
/** Gets the class this pattern matches objects against, if any. */
ConstantReadAccess getClass() { toGenerated(result) = g.getClass() }
/** Gets the `n`th element of this list pattern. */
CasePattern getElement(int n) { toGenerated(result) = g.getChild(n + 1) }
/** Gets an element of this list pattern. */
CasePattern getAnElement() { result = this.getElement(_) }
/**
* Gets the variable for the prefix of this list pattern, if any. For example `init` in:
* ```rb
* in List[*init, "a", Integer => x, *tail]
* ```
*/
LocalVariableWriteAccess getPrefix() {
toGenerated(result) = g.getChild(0).(Ruby::SplatParameter).getName()
}
/**
* Gets the variable for the suffix of this list pattern, if any. For example `tail` in:
* ```rb
* in List[*init, "a", Integer => x, *tail]
* ```
*/
LocalVariableWriteAccess getSuffix() {
toGenerated(result) = max(int i | | g.getChild(i) order by i).(Ruby::SplatParameter).getName()
}
final override string getAPrimaryQlClass() { result = "FindPattern" }
final override string toString() { result = "[ *,...,* ]" }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getClass" and result = this.getClass()
or
pred = "getElement" and result = this.getElement(_)
or
pred = "getPrefix" and result = this.getPrefix()
or
pred = "getSuffix" and result = this.getSuffix()
}
}
/**
* A hash pattern, for example:
* ```rb
* in {}
* in { a: 1 }
* in { a: 1, **rest }
* in { a: 1, **nil }
* in Node{ label: , children: [] }
* ```
*/
class HashPattern extends CasePattern, THashPattern {
private Ruby::HashPattern g;
HashPattern() { this = THashPattern(g) }
/** Gets the class this pattern matches objects against, if any. */
ConstantReadAccess getClass() { toGenerated(result) = g.getClass() }
private Ruby::KeywordPattern keyValuePair(int n) { result = g.getChild(n) }
/** Gets the key of the `n`th pair. */
StringlikeLiteral getKey(int n) { toGenerated(result) = keyValuePair(n).getKey() }
/** Gets the value of the `n`th pair. */
CasePattern getValue(int n) { toGenerated(result) = keyValuePair(n).getValue() }
/** Gets the value for a given key name. */
CasePattern getValueByKey(string key) {
exists(int i | key = this.getKey(i).getValueText() and result = this.getValue(i))
}
/**
* Gets the variable of the keyword rest token, if any. For example `rest` in:
* ```rb
* in { a: 1, **rest }
* ```
*/
LocalVariableWriteAccess getRestVariable() {
toGenerated(result) =
max(int i | | g.getChild(i) order by i).(Ruby::HashSplatParameter).getName()
}
/**
* Holds if this pattern is terminated by `**nil` indicating that the pattern does not permit
* any unmatched remaining pairs.
*/
predicate allowUnmatchedElements() { g.getChild(_) instanceof Ruby::HashSplatNil }
final override string getAPrimaryQlClass() { result = "HashPattern" }
final override string toString() { result = "{ ..., ** }" }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getClass" and result = this.getClass()
or
pred = "getKey" and result = this.getKey(_)
or
pred = "getValue" and result = this.getValue(_)
or
pred = "getRestVariable" and result = this.getRestVariable()
}
}
/**
* A composite pattern matching one of the given sub-patterns, for example:
* ```rb
* in 1 | 2 | 3
* ```
*/
class AlternativePattern extends CasePattern, TAlternativePattern {
private Ruby::AlternativePattern g;
AlternativePattern() { this = TAlternativePattern(g) }
/** Gets the `n`th alternative of this pattern. */
CasePattern getAlternative(int n) { toGenerated(result) = g.getAlternatives(n) }
/** Gets an alternative of this pattern. */
CasePattern getAnAlternative() { result = this.getAlternative(_) }
final override string getAPrimaryQlClass() { result = "AlternativePattern" }
final override string toString() { result = "... | ..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getAlternative" and result = this.getAlternative(_)
}
}
/**
* A pattern match that binds to the specified local variable, for example `Integer => a`
* in the following:
* ```rb
* case 1
* in Integer => a then puts "#{a} is an integer value"
* end
* ```
*/
class AsPattern extends CasePattern, TAsPattern {
private Ruby::AsPattern g;
AsPattern() { this = TAsPattern(g) }
/** Gets the underlying pattern. */
CasePattern getValue() { toGenerated(result) = g.getValue() }
/** Gets the variable access for this pattern. */
LocalVariableWriteAccess getVariableAccess() { toGenerated(result) = g.getName() }
final override string getAPrimaryQlClass() { result = "AsPattern" }
final override string toString() { result = "... => ..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getValue" and result = this.getValue()
or
pred = "getVariableAccess" and result = this.getVariableAccess()
}
}
/**
* A variable reference in a pattern, i.e. `^x` in the following example:
* ```rb
* x = 10
* case expr
* in ^x then puts "ok"
* end
* ```
*/
class VariableReferencePattern extends CasePattern, TVariableReferencePattern {
private Ruby::VariableReferencePattern g;
VariableReferencePattern() { this = TVariableReferencePattern(g) }
/** Gets the variable access correspinging to this variable reference pattern. */
LocalVariableReadAccess getVariableAccess() { toGenerated(result) = g.getName() }
final override string getAPrimaryQlClass() { result = "VariableReferencePattern" }
final override string toString() { result = "^..." }
final override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
pred = "getVariableAccess" and result = this.getVariableAccess()
}
}

View File

@@ -2,6 +2,7 @@ import codeql.Locations
private import TreeSitter
private import codeql.ruby.ast.internal.Call
private import codeql.ruby.ast.internal.Parameter
private import codeql.ruby.ast.internal.Pattern
private import codeql.ruby.ast.internal.Variable
private import codeql.ruby.AST as AST
private import Synthesis
@@ -30,6 +31,7 @@ private module Cached {
TAddExprReal(Ruby::Binary g) { g instanceof @ruby_binary_plus } or
TAddExprSynth(AST::AstNode parent, int i) { mkSynthChild(AddExprKind(), parent, i) } or
TAliasStmt(Ruby::Alias g) or
TAlternativePattern(Ruby::AlternativePattern g) or
TArgumentList(Ruby::AstNode g) {
(
g.getParent() instanceof Ruby::Break or
@@ -44,6 +46,7 @@ private module Cached {
g instanceof Ruby::RightAssignmentList
)
} or
TArrayPattern(Ruby::ArrayPattern g) or
TAssignAddExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_plusequal } or
TAssignBitwiseAndExpr(Ruby::OperatorAssignment g) {
g instanceof @ruby_operator_assignment_ampersandequal
@@ -77,6 +80,7 @@ private module Cached {
g instanceof @ruby_operator_assignment_ranglerangleequal
} or
TAssignSubExpr(Ruby::OperatorAssignment g) { g instanceof @ruby_operator_assignment_minusequal } or
TAsPattern(Ruby::AsPattern g) or
TBareStringLiteral(Ruby::BareString g) or
TBareSymbolLiteral(Ruby::BareSymbol g) or
TBeginBlock(Ruby::BeginBlock g) or
@@ -98,6 +102,7 @@ private module Cached {
TBreakStmt(Ruby::Break g) or
TCaseEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequalequal } or
TCaseExpr(Ruby::Case g) or
TCaseMatch(Ruby::CaseMatch g) or
TCharacterLiteral(Ruby::Character g) or
TClassDeclaration(Ruby::Class g) or
TClassVariableAccessReal(Ruby::ClassVariable g, AST::ClassVariable v) {
@@ -124,12 +129,15 @@ private module Cached {
TElse(Ruby::Else g) or
TElsif(Ruby::Elsif g) or
TEmptyStmt(Ruby::EmptyStatement g) or
TEncoding(Ruby::Encoding g) or
TEndBlock(Ruby::EndBlock g) or
TEnsure(Ruby::Ensure g) or
TEqExpr(Ruby::Binary g) { g instanceof @ruby_binary_equalequal } or
TExponentExprReal(Ruby::Binary g) { g instanceof @ruby_binary_starstar } or
TExponentExprSynth(AST::AstNode parent, int i) { mkSynthChild(ExponentExprKind(), parent, i) } or
TFalseLiteral(Ruby::False g) or
TFile(Ruby::File g) or
TFindPattern(Ruby::FindPattern g) or
TFloatLiteral(Ruby::Float g) { not any(Ruby::Rational r).getChild() = g } or
TForExpr(Ruby::For g) or
TForwardParameter(Ruby::ForwardParameter g) or
@@ -144,12 +152,18 @@ private module Cached {
} or
THashKeySymbolLiteral(Ruby::HashKeySymbol g) or
THashLiteral(Ruby::Hash g) or
THashPattern(Ruby::HashPattern g) or
THashSplatExpr(Ruby::HashSplatArgument g) or
THashSplatParameter(Ruby::HashSplatParameter g) or
THashSplatNilParameter(Ruby::HashSplatNil g) { not g.getParent() instanceof Ruby::HashPattern } or
THashSplatParameter(Ruby::HashSplatParameter g) {
not g.getParent() instanceof Ruby::HashPattern
} or
THereDoc(Ruby::HeredocBeginning g) or
TIdentifierMethodCall(Ruby::Identifier g) { isIdentifierMethodCall(g) } or
TIf(Ruby::If g) or
TIfGuard(Ruby::IfGuard g) or
TIfModifierExpr(Ruby::IfModifier g) or
TInClause(Ruby::InClause g) or
TInstanceVariableAccessReal(Ruby::InstanceVariable g, AST::InstanceVariable v) {
InstanceVariableAccess::range(g, v)
} or
@@ -166,6 +180,7 @@ private module Cached {
TLShiftExprSynth(AST::AstNode parent, int i) { mkSynthChild(LShiftExprKind(), parent, i) } or
TLTExpr(Ruby::Binary g) { g instanceof @ruby_binary_langle } or
TLambda(Ruby::Lambda g) or
TLine(Ruby::Line g) or
TLeftAssignmentList(Ruby::LeftAssignmentList g) or
TLocalVariableAccessReal(Ruby::Identifier g, TLocalVariableReal v) {
LocalVariableAccess::range(g, v)
@@ -229,6 +244,10 @@ private module Cached {
vcall(g)
or
explicitAssignmentNode(g, _)
or
casePattern(g)
or
classReferencePattern(g)
)
} or
TScopeResolutionMethodCall(Ruby::ScopeResolution g, Ruby::Identifier i) {
@@ -248,7 +267,10 @@ private module Cached {
TSpaceshipExpr(Ruby::Binary g) { g instanceof @ruby_binary_langleequalrangle } or
TSplatExprReal(Ruby::SplatArgument g) or
TSplatExprSynth(AST::AstNode parent, int i) { mkSynthChild(SplatExprKind(), parent, i) } or
TSplatParameter(Ruby::SplatParameter g) or
TSplatParameter(Ruby::SplatParameter g) {
not g.getParent() instanceof Ruby::ArrayPattern and
not g.getParent() instanceof Ruby::FindPattern
} or
TStmtSequenceSynth(AST::AstNode parent, int i) { mkSynthChild(StmtSequenceKind(), parent, i) } or
TStringArrayLiteral(Ruby::StringArray g) or
TStringConcatenation(Ruby::ChainedString g) or
@@ -269,6 +291,10 @@ private module Cached {
vcall(g)
or
explicitAssignmentNode(g, _)
or
casePattern(g)
or
classReferencePattern(g)
} or
TTokenMethodName(MethodName::Token g) { MethodName::range(g) } or
TTokenSuperCall(Ruby::Super g) { vcall(g) } or
@@ -279,31 +305,36 @@ private module Cached {
TUnaryPlusExpr(Ruby::Unary g) { g instanceof @ruby_unary_plus } or
TUndefStmt(Ruby::Undef g) or
TUnlessExpr(Ruby::Unless g) or
TUnlessGuard(Ruby::UnlessGuard g) or
TUnlessModifierExpr(Ruby::UnlessModifier g) or
TUntilExpr(Ruby::Until g) or
TUntilModifierExpr(Ruby::UntilModifier g) or
TVariableReferencePattern(Ruby::VariableReferencePattern g) or
TWhenExpr(Ruby::When g) or
TWhileExpr(Ruby::While g) or
TWhileModifierExpr(Ruby::WhileModifier g) or
TYieldCall(Ruby::Yield g)
class TAstNodeReal =
TAddExprReal or TAliasStmt or TArgumentList or TAssignAddExpr or TAssignBitwiseAndExpr or
TAssignBitwiseOrExpr or TAssignBitwiseXorExpr or TAssignDivExpr or TAssignExponentExpr or
TAssignExprReal or TAssignLShiftExpr or TAssignLogicalAndExpr or TAssignLogicalOrExpr or
TAssignModuloExpr or TAssignMulExpr or TAssignRShiftExpr or TAssignSubExpr or
TBareStringLiteral or TBareSymbolLiteral or TBeginBlock or TBeginExpr or
TBitwiseAndExprReal or TBitwiseOrExprReal or TBitwiseXorExprReal or TBlockArgument or
TBlockParameter or TBraceBlockReal or TBreakStmt or TCaseEqExpr or TCaseExpr or
TAddExprReal or TAliasStmt or TAlternativePattern or TArgumentList or TArrayPattern or
TAsPattern or TAssignAddExpr or TAssignBitwiseAndExpr or TAssignBitwiseOrExpr or
TAssignBitwiseXorExpr or TAssignDivExpr or TAssignExponentExpr or TAssignExprReal or
TAssignLShiftExpr or TAssignLogicalAndExpr or TAssignLogicalOrExpr or TAssignModuloExpr or
TAssignMulExpr or TAssignRShiftExpr or TAssignSubExpr or TBareStringLiteral or
TBareSymbolLiteral or TBeginBlock or TBeginExpr or TBitwiseAndExprReal or
TBitwiseOrExprReal or TBitwiseXorExprReal or TBlockArgument or TBlockParameter or
TBraceBlockReal or TBreakStmt or TCaseEqExpr or TCaseExpr or TCaseMatch or
TCharacterLiteral or TClassDeclaration or TClassVariableAccessReal or TComplementExpr or
TComplexLiteral or TDefinedExpr or TDelimitedSymbolLiteral or TDestructuredLeftAssignment or
TDivExprReal or TDo or TDoBlock or TElementReference or TElse or TElsif or TEmptyStmt or
TEndBlock or TEnsure or TEqExpr or TExponentExprReal or TFalseLiteral or TFloatLiteral or
TForExpr or TForwardParameter or TForwardArgument or TGEExpr or TGTExpr or
TGlobalVariableAccessReal or THashKeySymbolLiteral or THashLiteral or THashSplatExpr or
THashSplatParameter or THereDoc or TIdentifierMethodCall or TIf or TIfModifierExpr or
TInstanceVariableAccessReal or TIntegerLiteralReal or TKeywordParameter or TLEExpr or
TLShiftExprReal or TLTExpr or TLambda or TLeftAssignmentList or TLocalVariableAccessReal or
TEncoding or TEndBlock or TEnsure or TEqExpr or TExponentExprReal or TFalseLiteral or
TFile or TFindPattern or TFloatLiteral or TForExpr or TForwardParameter or
TForwardArgument or TGEExpr or TGTExpr or TGlobalVariableAccessReal or
THashKeySymbolLiteral or THashLiteral or THashPattern or THashSplatExpr or
THashSplatNilParameter or THashSplatParameter or THereDoc or TIdentifierMethodCall or TIf or
TIfGuard or TIfModifierExpr or TInClause or TInstanceVariableAccessReal or
TIntegerLiteralReal or TKeywordParameter or TLEExpr or TLShiftExprReal or TLTExpr or
TLambda or TLeftAssignmentList or TLine or TLocalVariableAccessReal or
TLogicalAndExprReal or TLogicalOrExprReal or TMethod or TModuleDeclaration or
TModuloExprReal or TMulExprReal or TNEExpr or TNextStmt or TNilLiteral or
TNoRegExpMatchExpr or TNotExpr or TOptionalParameter or TPair or TParenthesizedExpr or
@@ -317,8 +348,9 @@ private module Cached {
TStringTextComponent or TSubExprReal or TSubshellLiteral or TSymbolArrayLiteral or
TTernaryIfExpr or TThen or TTokenConstantAccess or TTokenMethodName or TTokenSuperCall or
TToplevel or TTrueLiteral or TTuplePatternParameter or TUnaryMinusExpr or TUnaryPlusExpr or
TUndefStmt or TUnlessExpr or TUnlessModifierExpr or TUntilExpr or TUntilModifierExpr or
TWhenExpr or TWhileExpr or TWhileModifierExpr or TYieldCall;
TUndefStmt or TUnlessExpr or TUnlessGuard or TUnlessModifierExpr or TUntilExpr or
TUntilModifierExpr or TVariableReferencePattern or TWhenExpr or TWhileExpr or
TWhileModifierExpr or TYieldCall;
class TAstNodeSynth =
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
@@ -339,7 +371,10 @@ private module Cached {
Ruby::AstNode toGenerated(TAstNodeReal n) {
n = TAddExprReal(result) or
n = TAliasStmt(result) or
n = TAlternativePattern(result) or
n = TArgumentList(result) or
n = TArrayPattern(result) or
n = TAsPattern(result) or
n = TAssignAddExpr(result) or
n = TAssignBitwiseAndExpr(result) or
n = TAssignBitwiseOrExpr(result) or
@@ -347,9 +382,9 @@ private module Cached {
n = TAssignDivExpr(result) or
n = TAssignExponentExpr(result) or
n = TAssignExprReal(result) or
n = TAssignLShiftExpr(result) or
n = TAssignLogicalAndExpr(result) or
n = TAssignLogicalOrExpr(result) or
n = TAssignLShiftExpr(result) or
n = TAssignModuloExpr(result) or
n = TAssignMulExpr(result) or
n = TAssignRShiftExpr(result) or
@@ -367,6 +402,7 @@ private module Cached {
n = TBreakStmt(result) or
n = TCaseEqExpr(result) or
n = TCaseExpr(result) or
n = TCaseMatch(result) or
n = TCharacterLiteral(result) or
n = TClassDeclaration(result) or
n = TClassVariableAccessReal(result, _) or
@@ -376,43 +412,51 @@ private module Cached {
n = TDelimitedSymbolLiteral(result) or
n = TDestructuredLeftAssignment(result) or
n = TDivExprReal(result) or
n = TDo(result) or
n = TDoBlock(result) or
n = TDo(result) or
n = TElementReference(result) or
n = TElse(result) or
n = TElsif(result) or
n = TEmptyStmt(result) or
n = TEncoding(result) or
n = TEndBlock(result) or
n = TEnsure(result) or
n = TEqExpr(result) or
n = TExponentExprReal(result) or
n = TFalseLiteral(result) or
n = TFile(result) or
n = TFindPattern(result) or
n = TFloatLiteral(result) or
n = TForExpr(result) or
n = TForwardArgument(result) or
n = TForwardParameter(result) or
n = TGEExpr(result) or
n = TGTExpr(result) or
n = TGlobalVariableAccessReal(result, _) or
n = TGTExpr(result) or
n = THashKeySymbolLiteral(result) or
n = THashLiteral(result) or
n = THashPattern(result) or
n = THashSplatExpr(result) or
n = THashSplatNilParameter(result) or
n = THashSplatParameter(result) or
n = THereDoc(result) or
n = TIdentifierMethodCall(result) or
n = TIf(result) or
n = TIfGuard(result) or
n = TIfModifierExpr(result) or
n = TIf(result) or
n = TInClause(result) or
n = TInstanceVariableAccessReal(result, _) or
n = TIntegerLiteralReal(result) or
n = TKeywordParameter(result) or
n = TLEExpr(result) or
n = TLShiftExprReal(result) or
n = TLTExpr(result) or
n = TLambda(result) or
n = TLEExpr(result) or
n = TLeftAssignmentList(result) or
n = TLine(result) or
n = TLocalVariableAccessReal(result, _) or
n = TLogicalAndExprReal(result) or
n = TLogicalOrExprReal(result) or
n = TLShiftExprReal(result) or
n = TLTExpr(result) or
n = TMethod(result) or
n = TModuleDeclaration(result) or
n = TModuloExprReal(result) or
@@ -425,7 +469,6 @@ private module Cached {
n = TOptionalParameter(result) or
n = TPair(result) or
n = TParenthesizedExpr(result) or
n = TRShiftExprReal(result) or
n = TRangeLiteralReal(result) or
n = TRationalLiteral(result) or
n = TRedoStmt(result) or
@@ -439,6 +482,7 @@ private module Cached {
n = TRescueModifierExpr(result) or
n = TRetryStmt(result) or
n = TReturnStmt(result) or
n = TRShiftExprReal(result) or
n = TScopeResolutionConstantAccess(result, _) or
n = TScopeResolutionMethodCall(result, _) or
n = TSelfReal(result) or
@@ -469,9 +513,11 @@ private module Cached {
n = TUnaryPlusExpr(result) or
n = TUndefStmt(result) or
n = TUnlessExpr(result) or
n = TUnlessGuard(result) or
n = TUnlessModifierExpr(result) or
n = TUntilExpr(result) or
n = TUntilModifierExpr(result) or
n = TVariableReferencePattern(result) or
n = TWhenExpr(result) or
n = TWhileExpr(result) or
n = TWhileModifierExpr(result) or
@@ -591,11 +637,13 @@ class TSuperCall = TTokenSuperCall or TRegularSuperCall;
class TConstantAccess =
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth;
class TControlExpr = TConditionalExpr or TCaseExpr or TLoop;
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;
class TConditionalExpr =
TIfExpr or TUnlessExpr or TIfModifierExpr or TUnlessModifierExpr or TTernaryIfExpr;
class TPatternGuard = TIfGuard or TUnlessGuard;
class TIfExpr = TIf or TElsif;
class TConditionalLoop = TWhileExpr or TUntilExpr or TWhileModifierExpr or TUntilModifierExpr;
@@ -605,10 +653,10 @@ class TLoop = TConditionalLoop or TForExpr;
class TSelf = TSelfReal or TSelfSynth;
class TExpr =
TSelf or TArgumentList or TRescueClause or TRescueModifierExpr or TPair or TStringConcatenation or
TCall or TBlockArgument or TConstantAccess or TControlExpr or TWhenExpr or TLiteral or
TCallable or TVariableAccess or TStmtSequence or TOperation or TSimpleParameter or
TForwardArgument;
TSelf or TArgumentList or TInClause or TRescueClause or TRescueModifierExpr or TPair or
TStringConcatenation or TCall or TBlockArgument or TConstantAccess or TControlExpr or
TWhenExpr or TLiteral or TCallable or TVariableAccess or TStmtSequence or TOperation or
TSimpleParameter or TForwardArgument;
class TSplatExpr = TSplatExprReal or TSplatExprSynth;
@@ -619,8 +667,9 @@ class TStmtSequence =
class TBodyStmt = TBeginExpr or TModuleBase or TMethod or TLambda or TDoBlock or TSingletonMethod;
class TLiteral =
TNumericLiteral or TNilLiteral or TBooleanLiteral or TStringlikeLiteral or TCharacterLiteral or
TArrayLiteral or THashLiteral or TRangeLiteral or TTokenMethodName;
TEncoding or TFile or TLine or TNumericLiteral or TNilLiteral or TBooleanLiteral or
TStringlikeLiteral or TCharacterLiteral or TArrayLiteral or THashLiteral or TRangeLiteral or
TTokenMethodName;
class TNumericLiteral = TIntegerLiteral or TFloatLiteral or TRationalLiteral or TComplexLiteral;
@@ -736,8 +785,8 @@ class TStmt =
class TReturningStmt = TReturnStmt or TBreakStmt or TNextStmt;
class TParameter =
TPatternParameter or TBlockParameter or THashSplatParameter or TKeywordParameter or
TOptionalParameter or TSplatParameter or TForwardParameter;
TPatternParameter or TBlockParameter or THashSplatParameter or THashSplatNilParameter or
TKeywordParameter or TOptionalParameter or TSplatParameter or TForwardParameter;
class TSimpleParameter = TSimpleParameterReal or TSimpleParameterSynth;

View File

@@ -30,3 +30,32 @@ class LeftAssignmentListImpl extends TuplePatternImpl, Ruby::LeftAssignmentList
)
}
}
/**
* Holds if `node` is a case pattern.
*/
predicate casePattern(Ruby::AstNode node) {
node = any(Ruby::InClause parent).getPattern()
or
node = any(Ruby::ArrayPattern parent).getChild(_).(Ruby::UnderscorePatternExpr)
or
node = any(Ruby::FindPattern parent).getChild(_).(Ruby::UnderscorePatternExpr)
or
node = any(Ruby::AlternativePattern parent).getAlternatives(_)
or
node = any(Ruby::AsPattern parent).getValue()
or
node = any(Ruby::KeywordPattern parent).getValue()
}
/**
* Holds if `node` is a class reference used in an
* array, find, or hash pattern.
*/
predicate classReferencePattern(Ruby::AstNode node) {
node = any(Ruby::ArrayPattern p).getClass()
or
node = any(Ruby::FindPattern p).getClass()
or
node = any(Ruby::HashPattern p).getClass()
}

View File

@@ -3,6 +3,7 @@ private import codeql.Locations
private import codeql.ruby.AST
private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.Parameter
private import codeql.ruby.ast.internal.Pattern
private import codeql.ruby.ast.internal.Scope
private import codeql.ruby.ast.internal.Synthesis
@@ -28,6 +29,16 @@ predicate explicitAssignmentNode(Ruby::AstNode n, Ruby::AstNode assignment) {
/** Holds if `n` is inside an implicit assignment. */
predicate implicitAssignmentNode(Ruby::AstNode n) {
casePattern(n) and n instanceof Ruby::Identifier
or
n = any(Ruby::AsPattern p).getName()
or
n = any(Ruby::ArrayPattern parent).getChild(_).(Ruby::SplatParameter).getName()
or
n = any(Ruby::FindPattern parent).getChild(_).(Ruby::SplatParameter).getName()
or
n = any(Ruby::HashPattern parent).getChild(_).(Ruby::HashSplatParameter).getName()
or
n = any(Ruby::ExceptionVariable ev).getChild()
or
n = any(Ruby::For for).getPattern()
@@ -177,6 +188,8 @@ private module Cached {
or
i = any(Ruby::Case x).getValue()
or
i = any(Ruby::CaseMatch x).getValue()
or
i = any(Ruby::Class x).getChild(_)
or
i = any(Ruby::Conditional x).getCondition()
@@ -498,11 +511,10 @@ module LocalVariableAccess {
predicate range(Ruby::Identifier id, TLocalVariableReal v) {
access(id, v) and
(
explicitWriteAccess(id, _)
or
implicitWriteAccess(id)
or
vcall(id)
explicitWriteAccess(id, _) or
implicitWriteAccess(id) or
vcall(id) or
id = any(Ruby::VariableReferencePattern vr).getName()
)
}
}

View File

@@ -603,6 +603,8 @@ module Trees {
final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) }
}
private class HashSplatNilParameterTree extends LeafTree, HashSplatNilParameter { }
private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { }
private class HereDocTree extends StandardPreOrderTree, HereDoc {