Skip to content

Commit 1e026ef

Browse files
committed
AST: merge Case and CaseMatch classes
1 parent f8a62c4 commit 1e026ef

File tree

8 files changed

+271
-201
lines changed

8 files changed

+271
-201
lines changed

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

Lines changed: 65 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,36 @@ class TernaryIfExpr extends ConditionalExpr, TTernaryIfExpr {
308308
}
309309
}
310310

311-
class CaseExpr extends ControlExpr, TCaseExpr {
312-
private Ruby::Case g;
313-
314-
CaseExpr() { this = TCaseExpr(g) }
315-
311+
/**
312+
* A `case` statement. There are three forms of `case` statements:
313+
* ```rb
314+
* # a value-less case expression acting like an if-elsif expression:
315+
* case
316+
* when x == 0 then puts "zero"
317+
* when x > 0 then puts "positive"
318+
* else puts "negative"
319+
* end
320+
*
321+
* # a case expression that matches a value using `when` clauses:
322+
* case value
323+
* when 1, 2 then puts "a is one or two"
324+
* when 3 then puts "a is three"
325+
* else puts "I don't know what a is"
326+
* end
327+
*
328+
* # a case expression that matches a value against patterns using `in` clauses:
329+
* config = {db: {user: 'admin', password: 'abc123'}}
330+
* case config
331+
* in db: {user:} # matches subhash and puts matched value in variable user
332+
* puts "Connect with user '#{user}'"
333+
* in connection: {username: } unless username == 'admin'
334+
* puts "Connect with user '#{username}'"
335+
* else
336+
* puts "Unrecognized structure of config"
337+
* end
338+
* ```
339+
*/
340+
class CaseExpr extends ControlExpr, TCase {
316341
final override string getAPrimaryQlClass() { result = "CaseExpr" }
317342

318343
/**
@@ -334,25 +359,25 @@ class CaseExpr extends ControlExpr, TCaseExpr {
334359
* end
335360
* ```
336361
*/
337-
final Expr getValue() { toGenerated(result) = g.getValue() }
362+
Expr getValue() { none() }
338363

339364
/**
340-
* Gets the `n`th branch of this case expression, either a `WhenExpr` or a
341-
* `StmtSequence`.
365+
* Gets the `n`th branch of this case expression, either a `WhenExpr`, or a
366+
* `InClause` or a `StmtSequence`.
342367
*/
343-
final Expr getBranch(int n) { toGenerated(result) = g.getChild(n) }
368+
Expr getBranch(int n) { none() }
344369

345370
/**
346-
* Gets a branch of this case expression, either a `WhenExpr` or an
347-
* `ElseExpr`.
371+
* Gets a branch of this case expression, either a `WhenExpr`, or a
372+
* `InClause` or a `StmtSequence`.
348373
*/
349374
final Expr getABranch() { result = this.getBranch(_) }
350375

351376
/** Gets the `n`th `when` branch of this case expression. */
352-
final WhenExpr getWhenBranch(int n) { result = this.getBranch(n) }
377+
deprecated final WhenExpr getWhenBranch(int n) { result = this.getBranch(n) }
353378

354379
/** Gets a `when` branch of this case expression. */
355-
final WhenExpr getAWhenBranch() { result = this.getABranch() }
380+
deprecated final WhenExpr getAWhenBranch() { result = this.getABranch() }
356381

357382
/** Gets the `else` branch of this case expression, if any. */
358383
final StmtSequence getElseBranch() { result = this.getABranch() }
@@ -371,12 +396,37 @@ class CaseExpr extends ControlExpr, TCaseExpr {
371396
or
372397
pred = "getBranch" and result = this.getBranch(_)
373398
or
374-
pred = "getWhenBranch" and result = this.getWhenBranch(_)
375-
or
376399
pred = "getElseBranch" and result = this.getElseBranch()
377400
}
378401
}
379402

403+
private class CaseWhenExpr extends CaseExpr, TCaseExpr {
404+
private Ruby::Case g;
405+
406+
CaseWhenExpr() { this = TCaseExpr(g) }
407+
408+
final override Expr getValue() { toGenerated(result) = g.getValue() }
409+
410+
final override Expr getBranch(int n) {
411+
toGenerated(result) = g.getChild(n) or
412+
toGenerated(result) = g.getChild(n)
413+
}
414+
}
415+
416+
private class CaseMatch extends CaseExpr, TCaseMatch {
417+
private Ruby::CaseMatch g;
418+
419+
CaseMatch() { this = TCaseMatch(g) }
420+
421+
final override Expr getValue() { toGenerated(result) = g.getValue() }
422+
423+
final override Expr getBranch(int n) {
424+
toGenerated(result) = g.getClauses(n)
425+
or
426+
n = count(g.getClauses(_)) and toGenerated(result) = g.getElse()
427+
}
428+
}
429+
380430
/**
381431
* A `when` branch of a `case` expression.
382432
* ```rb
@@ -429,85 +479,6 @@ class WhenExpr extends Expr, TWhenExpr {
429479
}
430480
}
431481

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-
511482
/**
512483
* An `in` clause of a `case` expression.
513484
* ```rb

ruby/ql/lib/codeql/ruby/ast/internal/AST.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,8 @@ TAstNodeReal fromGenerated(Ruby::AstNode n) { n = toGenerated(result) }
623623

624624
class TCall = TMethodCall or TYieldCall;
625625

626+
class TCase = TCaseExpr or TCaseMatch;
627+
626628
class TMethodCall =
627629
TMethodCallSynth or TIdentifierMethodCall or TScopeResolutionMethodCall or TRegularMethodCall or
628630
TElementReference or TSuperCall or TUnaryOperation or TBinaryOperation;

ruby/ql/lib/codeql/ruby/controlflow/internal/Completion.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ private predicate inBooleanContext(AstNode n) {
192192
or
193193
exists(CaseExpr c, WhenExpr w |
194194
not exists(c.getValue()) and
195-
c.getAWhenBranch() = w and
195+
c.getABranch() = w and
196196
w.getPattern(_) = n
197197
)
198198
}
@@ -214,7 +214,7 @@ private predicate inMatchingContext(AstNode n) {
214214
or
215215
exists(CaseExpr c, WhenExpr w |
216216
exists(c.getValue()) and
217-
c.getAWhenBranch() = w and
217+
c.getABranch() = w and
218218
w.getPattern(_) = n
219219
)
220220
or

ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,15 @@ module Trees {
378378
override ControlFlowTree getChildElement(int i) { result = this.getArgument(i) }
379379
}
380380

381-
private class CaseTree extends PreOrderTree, CaseExpr {
381+
private class CaseTree extends PreOrderTree, CaseExpr, ASTInternal::TCaseExpr {
382382
final override predicate propagatesAbnormal(AstNode child) {
383383
child = this.getValue() or child = this.getABranch()
384384
}
385385

386386
final override predicate last(AstNode last, Completion c) {
387387
last(this.getValue(), last, c) and not exists(this.getABranch())
388388
or
389-
last(this.getAWhenBranch().getBody(), last, c)
389+
last(this.getABranch().(WhenExpr).getBody(), last, c)
390390
or
391391
exists(int i, ControlFlowTree lastBranch |
392392
lastBranch = this.getBranch(i) and

0 commit comments

Comments
 (0)