Skip to content

Commit 078a2aa

Browse files
committed
Update AST library
1 parent ec0bd24 commit 078a2aa

File tree

8 files changed

+706
-39
lines changed

8 files changed

+706
-39
lines changed

ruby/ql/lib/codeql/ruby/ast/Control.qll

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ class CaseExpr extends ControlExpr, TCaseExpr {
348348
*/
349349
final Expr getABranch() { result = this.getBranch(_) }
350350

351+
/** Gets the `n`th `when` branch of this case expression. */
352+
final WhenExpr getWhenBranch(int n) { result = this.getBranch(n) }
353+
351354
/** Gets a `when` branch of this case expression. */
352355
final WhenExpr getAWhenBranch() { result = this.getABranch() }
353356

@@ -367,6 +370,10 @@ class CaseExpr extends ControlExpr, TCaseExpr {
367370
pred = "getValue" and result = this.getValue()
368371
or
369372
pred = "getBranch" and result = this.getBranch(_)
373+
or
374+
pred = "getWhenBranch" and result = this.getWhenBranch(_)
375+
or
376+
pred = "getElseBranch" and result = this.getElseBranch()
370377
}
371378
}
372379

@@ -422,6 +429,204 @@ class WhenExpr extends Expr, TWhenExpr {
422429
}
423430
}
424431

432+
/**
433+
* A `case` statement used for pattern matching. For example:
434+
* ```rb
435+
* config = {db: {user: 'admin', password: 'abc123'}}
436+
* case config
437+
* in db: {user:} # matches subhash and puts matched value in variable user
438+
* puts "Connect with user '#{user}'"
439+
* in connection: {username: } unless username == 'admin'
440+
* puts "Connect with user '#{username}'"
441+
* else
442+
* puts "Unrecognized structure of config"
443+
* end
444+
* ```
445+
*/
446+
class CaseMatch extends ControlExpr, TCaseMatch {
447+
private Ruby::CaseMatch g;
448+
449+
CaseMatch() { this = TCaseMatch(g) }
450+
451+
final override string getAPrimaryQlClass() { result = "CaseMatch" }
452+
453+
/**
454+
* Gets the expression being matched. For example, `foo` in the following example.
455+
* ```rb
456+
* case foo
457+
* in 0
458+
* puts 'zero'
459+
* in 1
460+
* puts 'one'
461+
* end
462+
* ```
463+
*/
464+
final Expr getValue() { toGenerated(result) = g.getValue() }
465+
466+
/**
467+
* Gets the `n`th branch of this case expression, either an `InClause` or a
468+
* `StmtSequence`.
469+
*/
470+
final Expr getBranch(int n) {
471+
toGenerated(result) = g.getClauses(n)
472+
or
473+
n = count(g.getClauses(_)) and toGenerated(result) = g.getElse()
474+
}
475+
476+
/**
477+
* Gets a branch of this case expression, either an `InClause` or an
478+
* `StmtSequence`.
479+
*/
480+
final Expr getABranch() { result = this.getBranch(_) }
481+
482+
/** Gets the `n`th `in` clause of this case expression. */
483+
final InClause getInClause(int n) { result = this.getBranch(n) }
484+
485+
/** Gets an `in` clause of this case expression. */
486+
final InClause getAnInClause() { result = this.getABranch() }
487+
488+
/** Gets the `else` branch of this case expression, if any. */
489+
final StmtSequence getElseBranch() { result = this.getABranch() }
490+
491+
/**
492+
* Gets the number of branches of this case expression.
493+
*/
494+
final int getNumberOfBranches() { result = count(this.getBranch(_)) }
495+
496+
final override string toString() { result = "case ... in" }
497+
498+
override AstNode getAChild(string pred) {
499+
result = super.getAChild(pred)
500+
or
501+
pred = "getValue" and result = this.getValue()
502+
or
503+
pred = "getBranch" and result = this.getBranch(_)
504+
or
505+
pred = "getInClause" and result = this.getInClause(_)
506+
or
507+
pred = "getElseBranch" and result = this.getElseBranch()
508+
}
509+
}
510+
511+
/**
512+
* An `in` clause of a `case` expression.
513+
* ```rb
514+
* case foo
515+
* in [ a ] then a
516+
* end
517+
* ```
518+
*/
519+
class InClause extends Expr, TInClause {
520+
private Ruby::InClause g;
521+
522+
InClause() { this = TInClause(g) }
523+
524+
final override string getAPrimaryQlClass() { result = "InClause" }
525+
526+
/** Gets the body of this case-in expression. */
527+
final Stmt getBody() { toGenerated(result) = g.getBody() }
528+
529+
/**
530+
* Gets the pattern in this case-in expression. In the
531+
* following example, the pattern is `Point{ x:, y: }`.
532+
* ```rb
533+
* case foo
534+
* in Point{ x:, y: }
535+
* x + y
536+
* end
537+
* ```
538+
*/
539+
final CasePattern getPattern() { toGenerated(result) = g.getPattern() }
540+
541+
/**
542+
* Gets the pattern guard in this case-in expression. In the
543+
* following example, the pattern guard is `x > 10`.
544+
* ```rb
545+
* case foo
546+
* in [ x ] if x > 10 then ...
547+
* end
548+
* ```
549+
*/
550+
final PatternGuard getPatternGuard() { toGenerated(result) = g.getGuard() }
551+
552+
final override string toString() { result = "in ... then ..." }
553+
554+
override AstNode getAChild(string pred) {
555+
result = super.getAChild(pred)
556+
or
557+
pred = "getBody" and result = this.getBody()
558+
or
559+
pred = "getPattern" and result = this.getPattern()
560+
or
561+
pred = "getGuard" and result = this.getPatternGuard()
562+
}
563+
}
564+
565+
/**
566+
* A guard used in pattern matching. For example:
567+
* ```rb
568+
* in pattern if guard
569+
* in pattern unless guard
570+
* ```
571+
*/
572+
class PatternGuard extends AstNode, TPatternGuard {
573+
/**
574+
* Gets the condition expression. For example, the result is `foo` in the
575+
* following:
576+
* ```rb
577+
* if foo
578+
* unless foo
579+
* ```
580+
*/
581+
Expr getCondition() { none() }
582+
583+
override AstNode getAChild(string pred) {
584+
result = super.getAChild(pred)
585+
or
586+
pred = "getCondition" and result = this.getCondition()
587+
}
588+
}
589+
590+
/**
591+
* An `if` pattern guard. For example:
592+
* ```rb
593+
* case foo
594+
* in [ bar] if bar > 10
595+
* end
596+
* ```
597+
*/
598+
class IfGuard extends PatternGuard, TIfGuard {
599+
private Ruby::IfGuard g;
600+
601+
IfGuard() { this = TIfGuard(g) }
602+
603+
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
604+
605+
final override string getAPrimaryQlClass() { result = "IfGuard" }
606+
607+
final override string toString() { result = "if ..." }
608+
}
609+
610+
/**
611+
* An `unless` pattern guard. For example:
612+
* ```rb
613+
* case foo
614+
* in [ bar] unless bar > 10
615+
* end
616+
* ```
617+
*/
618+
class UnlessGuard extends PatternGuard, TUnlessGuard {
619+
private Ruby::UnlessGuard g;
620+
621+
UnlessGuard() { this = TUnlessGuard(g) }
622+
623+
final override Expr getCondition() { toGenerated(result) = g.getCondition() }
624+
625+
final override string getAPrimaryQlClass() { result = "UnlessGuard" }
626+
627+
final override string toString() { result = "unless ..." }
628+
}
629+
425630
/**
426631
* A loop. That is, a `for` loop, a `while` or `until` loop, or their
427632
* expression-modifier variants.

ruby/ql/lib/codeql/ruby/ast/Literal.qll

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,40 @@ private class FalseLiteral extends BooleanLiteral, TFalseLiteral {
223223
final override predicate isFalse() { any() }
224224
}
225225

226+
/**
227+
* An `__ENCODING__` literal.
228+
*/
229+
class EncodingLiteral extends Literal, TEncoding {
230+
final override string getAPrimaryQlClass() { result = "EncodingLiteral" }
231+
232+
final override string toString() { result = "__ENCODING__" }
233+
234+
// TODO: return the encoding defined by a magic encoding: comment, if any.
235+
override string getValueText() { result = "UTF-8" }
236+
}
237+
238+
/**
239+
* A `__LINE__` literal.
240+
*/
241+
class LineLiteral extends Literal, TLine {
242+
final override string getAPrimaryQlClass() { result = "LineLiteral" }
243+
244+
final override string toString() { result = "__LINE__" }
245+
246+
override string getValueText() { result = this.getLocation().getStartLine().toString() }
247+
}
248+
249+
/**
250+
* A `__FILE__` literal.
251+
*/
252+
class FileLiteral extends Literal, TFile {
253+
final override string getAPrimaryQlClass() { result = "FileLiteral" }
254+
255+
final override string toString() { result = "__FILE__" }
256+
257+
override string getValueText() { result = this.getLocation().getFile().getAbsolutePath() }
258+
}
259+
226260
/**
227261
* The base class for a component of a string: `StringTextComponent`,
228262
* `StringEscapeSequenceComponent`, or `StringInterpolationComponent`.

ruby/ql/lib/codeql/ruby/ast/Parameter.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,23 @@ class HashSplatParameter extends NamedParameter, THashSplatParameter {
131131
final override string getName() { result = g.getName().getValue() }
132132
}
133133

134+
/**
135+
* A `nil` hash splat (`**nil`) indicating that there are no keyword parameters or keyword patterns.
136+
* For example:
137+
* ```rb
138+
* def foo(bar, **nil)
139+
* case bar
140+
* in { x:, **nil } then puts x
141+
* end
142+
* end
143+
* ```
144+
*/
145+
class HashSplatNilParameter extends Parameter, THashSplatNilParameter {
146+
final override string getAPrimaryQlClass() { result = "HashSplatNilParameter" }
147+
148+
final override string toString() { result = "**nil" }
149+
}
150+
134151
/**
135152
* A keyword parameter, including a default value if the parameter is optional.
136153
* For example, in the following example, `foo` is a keyword parameter with a

0 commit comments

Comments
 (0)