Merge pull request #7283 from aibaars/ruby-pattern-matching-cfg

Ruby: pattern matching: CFG
This commit is contained in:
Arthur Baars
2021-12-10 10:24:38 +01:00
committed by GitHub
13 changed files with 1364 additions and 44 deletions

View File

@@ -246,8 +246,6 @@ private module Cached {
explicitAssignmentNode(g, _)
or
casePattern(g)
or
classReferencePattern(g)
)
} or
TScopeResolutionMethodCall(Ruby::ScopeResolution g, Ruby::Identifier i) {
@@ -293,8 +291,6 @@ private module Cached {
explicitAssignmentNode(g, _)
or
casePattern(g)
or
classReferencePattern(g)
} or
TTokenMethodName(MethodName::Token g) { MethodName::range(g) } or
TTokenSuperCall(Ruby::Super g) { vcall(g) } or

View File

@@ -48,13 +48,7 @@ predicate casePattern(Ruby::AstNode node) {
node = any(Ruby::KeywordPattern parent).getValue()
or
node = any(Ruby::ParenthesizedPattern parent).getChild()
}
/**
* Holds if `node` is a class reference used in an
* array, find, or hash pattern.
*/
predicate classReferencePattern(Ruby::AstNode node) {
or
node = any(Ruby::ArrayPattern p).getClass()
or
node = any(Ruby::FindPattern p).getClass()

View File

@@ -6,6 +6,7 @@
private import codeql.ruby.AST
private import codeql.ruby.ast.internal.AST
private import codeql.ruby.ast.internal.Control
private import codeql.ruby.controlflow.ControlFlowGraph
private import ControlFlowGraphImpl
private import NonReturning
@@ -27,6 +28,10 @@ private newtype TCompletion =
outer instanceof NonNestedNormalCompletion and
nestLevel = 0
or
inner instanceof TBooleanCompletion and
outer instanceof TMatchingCompletion and
nestLevel = 0
or
inner instanceof NormalCompletion and
nestedEnsureCompletion(outer, nestLevel)
}
@@ -81,8 +86,9 @@ private predicate mayRaise(Call c) {
/** A completion of a statement or an expression. */
abstract class Completion extends TCompletion {
/** Holds if this completion is valid for node `n`. */
predicate isValidFor(AstNode n) {
private predicate isValidForSpecific(AstNode n) {
exists(AstNode other | n = other.getDesugared() and this.isValidForSpecific(other))
or
this = n.(NonReturningCall).getACompletion()
or
completionIsValidForStmt(n, this)
@@ -98,15 +104,26 @@ abstract class Completion extends TCompletion {
mustHaveMatchingCompletion(n) and
this = TMatchingCompletion(_)
or
n = any(RescueModifierExpr parent).getBody() and this = TRaiseCompletion()
n = any(RescueModifierExpr parent).getBody() and
this = [TSimpleCompletion().(TCompletion), TRaiseCompletion()]
or
mayRaise(n) and
this = TRaiseCompletion()
(
mayRaise(n)
or
n instanceof CaseMatch and not exists(n.(CaseExpr).getElseBranch())
) and
(
this = TRaiseCompletion()
or
this = TSimpleCompletion() and not n instanceof NonReturningCall
)
}
/** Holds if this completion is valid for node `n`. */
predicate isValidFor(AstNode n) {
this.isValidForSpecific(n)
or
not n instanceof NonReturningCall and
not completionIsValidForStmt(n, _) and
not mustHaveBooleanCompletion(n) and
not mustHaveMatchingCompletion(n) and
not any(Completion c).isValidForSpecific(n) and
this = TSimpleCompletion()
}
@@ -172,6 +189,8 @@ private predicate inBooleanContext(AstNode n) {
or
n = any(ConditionalLoop parent).getCondition()
or
n = any(InClause parent).getCondition()
or
exists(LogicalAndExpr parent |
n = parent.getLeftOperand()
or
@@ -218,6 +237,10 @@ private predicate inMatchingContext(AstNode n) {
w.getPattern(_) = n
)
or
n instanceof CasePattern
or
n = any(VariableReferencePattern p).getVariableAccess()
or
n.(Trees::DefaultValueParameterTree).hasDefaultValue()
}
@@ -241,7 +264,7 @@ class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion {
* the successor. Either a Boolean completion (`BooleanCompletion`), or a matching
* completion (`MatchingCompletion`).
*/
abstract class ConditionalCompletion extends NonNestedNormalCompletion {
abstract class ConditionalCompletion extends NormalCompletion {
boolean value;
bindingset[value]
@@ -255,7 +278,7 @@ abstract class ConditionalCompletion extends NonNestedNormalCompletion {
* A completion that represents evaluation of an expression
* with a Boolean value.
*/
class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
class BooleanCompletion extends ConditionalCompletion, NonNestedNormalCompletion, TBooleanCompletion {
BooleanCompletion() { this = TBooleanCompletion(value) }
/** Gets the dual Boolean completion. */
@@ -280,10 +303,16 @@ class FalseCompletion extends BooleanCompletion {
* A completion that represents evaluation of a matching test, for example
* a test in a `rescue` statement.
*/
class MatchingCompletion extends ConditionalCompletion, TMatchingCompletion {
MatchingCompletion() { this = TMatchingCompletion(value) }
class MatchingCompletion extends ConditionalCompletion {
MatchingCompletion() {
this = TMatchingCompletion(value)
or
this = TNestedCompletion(_, TMatchingCompletion(value), _)
}
override MatchingSuccessor getAMatchingSuccessorType() { result.getValue() = value }
override ConditionalSuccessor getAMatchingSuccessorType() {
this = TMatchingCompletion(result.(MatchingSuccessor).getValue())
}
override string toString() { if value = true then result = "match" else result = "no-match" }
}
@@ -440,7 +469,9 @@ abstract class NestedCompletion extends Completion, TNestedCompletion {
NestedCompletion() { this = TNestedCompletion(inner, outer, nestLevel) }
/** Gets a completion that is compatible with the inner completion. */
abstract Completion getAnInnerCompatibleCompletion();
Completion getAnInnerCompatibleCompletion() {
result.getOuterCompletion() = this.getInnerCompletion()
}
/** Gets the level of this nested completion. */
final int getNestLevel() { result = nestLevel }
@@ -483,9 +514,39 @@ class NestedEnsureCompletion extends NestedCompletion {
override Completion getOuterCompletion() { result = outer }
override Completion getAnInnerCompatibleCompletion() {
result.getOuterCompletion() = this.getInnerCompletion()
}
override SuccessorType getAMatchingSuccessorType() { none() }
}
/**
* A completion used for conditions in pattern matching:
*
* ```rb
* in x if x == 5 then puts "five"
* in x unless x == 4 then puts "not four"
* ```
*
* The outer (Matching) completion indicates whether there is a match, and
* the inner (Boolean) completion indicates what the condition evaluated
* to.
*
* For the condition `x == 5` above, `TNestedCompletion(true, true, 0)` and
* `TNestedCompletion(false, false, 0)` are both valid completions, while
* `TNestedCompletion(true, false, 0)` and `TNestedCompletion(false, true, 0)`
* are valid completions for `x == 4`.
*/
class NestedMatchingCompletion extends NestedCompletion, MatchingCompletion {
NestedMatchingCompletion() {
inner instanceof TBooleanCompletion and
outer instanceof TMatchingCompletion
}
override BooleanCompletion getInnerCompletion() { result = inner }
override MatchingCompletion getOuterCompletion() { result = outer }
override BooleanSuccessor getAMatchingSuccessorType() {
result.getValue() = this.getInnerCompletion().getValue()
}
override string toString() { result = NestedCompletion.super.toString() }
}

View File

@@ -419,6 +419,387 @@ module Trees {
}
}
private class CaseMatchTree extends PreOrderTree, CaseExpr, ASTInternal::TCaseMatch {
final override predicate propagatesAbnormal(AstNode child) {
child = this.getValue() or child = this.getABranch()
}
final override predicate last(AstNode last, Completion c) {
last(this.getABranch(), last, c) and
not c.(MatchingCompletion).getValue() = false
or
not exists(this.getElseBranch()) and
exists(MatchingCompletion lc, Expr lastBranch |
lastBranch = max(int i | | this.getBranch(i) order by i) and
lc.getValue() = false and
last(lastBranch, last, lc) and
c instanceof RaiseCompletion and
not c instanceof NestedCompletion
)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
pred = this and
first(this.getValue(), succ) and
c instanceof SimpleCompletion
or
last(this.getValue(), pred, c) and
first(this.getBranch(0), succ) and
c instanceof SimpleCompletion
or
exists(int i, Expr branch | branch = this.getBranch(i) |
last(branch, pred, c) and
first(this.getBranch(i + 1), succ) and
c.(MatchingCompletion).getValue() = false
)
}
}
private class PatternVariableAccessTree extends LocalVariableAccessTree, LocalVariableWriteAccess,
CasePattern {
final override predicate last(AstNode last, Completion c) {
super.last(last, c) and
c.(MatchingCompletion).getValue() = true
}
}
private class ArrayPatternTree extends ControlFlowTree, ArrayPattern {
final override predicate propagatesAbnormal(AstNode child) {
child = this.getClass() or
child = this.getPrefixElement(_) or
child = this.getRestVariableAccess() or
child = this.getSuffixElement(_)
}
final override predicate first(AstNode first) {
first(this.getClass(), first)
or
not exists(this.getClass()) and first = this
}
final override predicate last(AstNode last, Completion c) {
c.(MatchingCompletion).getValue() = false and
(
last = this and
c.isValidFor(this)
or
exists(AstNode node |
node = this.getClass() or
node = this.getPrefixElement(_) or
node = this.getSuffixElement(_)
|
last(node, last, c)
)
)
or
c.(MatchingCompletion).getValue() = true and
last = this and
c.isValidFor(this) and
not exists(this.getPrefixElement(_)) and
not exists(this.getRestVariableAccess())
or
c.(MatchingCompletion).getValue() = true and
last(max(int i | | this.getPrefixElement(i) order by i), last, c) and
not exists(this.getRestVariableAccess())
or
last(this.getRestVariableAccess(), last, c) and
not exists(this.getSuffixElement(_))
or
c.(MatchingCompletion).getValue() = true and
last(max(int i | | this.getSuffixElement(i) order by i), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
last(this.getClass(), pred, c) and
succ = this and
c.(MatchingCompletion).getValue() = true
or
exists(AstNode next |
pred = this and
c.(MatchingCompletion).getValue() = true and
first(next, succ)
|
next = this.getPrefixElement(0)
or
not exists(this.getPrefixElement(_)) and
next = this.getRestVariableAccess()
)
or
last(max(int i | | this.getPrefixElement(i) order by i), pred, c) and
first(this.getRestVariableAccess(), succ) and
c.(MatchingCompletion).getValue() = true
or
exists(int i |
last(this.getPrefixElement(i), pred, c) and
first(this.getPrefixElement(i + 1), succ) and
c.(MatchingCompletion).getValue() = true
)
or
last(this.getRestVariableAccess(), pred, c) and
first(this.getSuffixElement(0), succ) and
c instanceof SimpleCompletion
or
exists(int i |
last(this.getSuffixElement(i), pred, c) and
first(this.getSuffixElement(i + 1), succ) and
c.(MatchingCompletion).getValue() = true
)
}
}
private class FindPatternTree extends ControlFlowTree, FindPattern {
final override predicate propagatesAbnormal(AstNode child) {
child = this.getClass() or
child = this.getPrefixVariableAccess() or
child = this.getElement(_) or
child = this.getSuffixVariableAccess()
}
final override predicate first(AstNode first) {
first(this.getClass(), first)
or
not exists(this.getClass()) and first = this
}
final override predicate last(AstNode last, Completion c) {
last(this.getSuffixVariableAccess(), last, c)
or
last(max(int i | | this.getElement(i) order by i), last, c) and
not exists(this.getSuffixVariableAccess())
or
c.(MatchingCompletion).getValue() = false and
(
last = this and
c.isValidFor(this)
or
exists(AstNode node | node = this.getClass() or node = this.getElement(_) |
last(node, last, c)
)
)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
last(this.getClass(), pred, c) and
succ = this and
c.(MatchingCompletion).getValue() = true
or
exists(AstNode next |
pred = this and
c.(MatchingCompletion).getValue() = true and
first(next, succ)
|
next = this.getPrefixVariableAccess()
or
not exists(this.getPrefixVariableAccess()) and
next = this.getElement(0)
)
or
last(this.getPrefixVariableAccess(), pred, c) and
first(this.getElement(0), succ) and
c instanceof SimpleCompletion
or
c.(MatchingCompletion).getValue() = true and
exists(int i |
last(this.getElement(i), pred, c) and
first(this.getElement(i + 1), succ)
)
or
c.(MatchingCompletion).getValue() = true and
last(max(int i | | this.getElement(i) order by i), pred, c) and
first(this.getSuffixVariableAccess(), succ)
}
}
private class HashPatternTree extends ControlFlowTree, HashPattern {
final override predicate propagatesAbnormal(AstNode child) {
child = this.getClass() or
child = this.getValue(_) or
child = this.getRestVariableAccess()
}
final override predicate first(AstNode first) {
first(this.getClass(), first)
or
not exists(this.getClass()) and first = this
}
final override predicate last(AstNode last, Completion c) {
c.(MatchingCompletion).getValue() = false and
(
last = this and
c.isValidFor(this)
or
exists(AstNode node |
node = this.getClass() or
node = this.getValue(_)
|
last(node, last, c)
)
)
or
c.(MatchingCompletion).getValue() = true and
last = this and
not exists(this.getValue(_)) and
not exists(this.getRestVariableAccess())
or
c.(MatchingCompletion).getValue() = true and
last(max(int i | | this.getValue(i) order by i), last, c) and
not exists(this.getRestVariableAccess())
or
last(this.getRestVariableAccess(), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
last(this.getClass(), pred, c) and
succ = this and
c.(MatchingCompletion).getValue() = true
or
exists(AstNode next |
pred = this and
c.(MatchingCompletion).getValue() = true and
first(next, succ)
|
next = this.getValue(0)
or
not exists(this.getValue(_)) and
next = this.getRestVariableAccess()
)
or
last(max(int i | | this.getValue(i) order by i), pred, c) and
first(this.getRestVariableAccess(), succ) and
c.(MatchingCompletion).getValue() = true
or
exists(int i |
last(this.getValue(i), pred, c) and
first(this.getValue(i + 1), succ) and
c.(MatchingCompletion).getValue() = true
)
}
}
private class LineLiteralTree extends LeafTree, LineLiteral { }
private class FileLiteralTree extends LeafTree, FileLiteral { }
private class EncodingLiteralTree extends LeafTree, EncodingLiteral { }
private class AlternativePatternTree extends PreOrderTree, AlternativePattern {
final override predicate propagatesAbnormal(AstNode child) { child = this.getAnAlternative() }
final override predicate last(AstNode last, Completion c) {
last(this.getAnAlternative(), last, c) and
c.(MatchingCompletion).getValue() = true
or
last(max(int i | | this.getAlternative(i) order by i), last, c) and
c.(MatchingCompletion).getValue() = false
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
pred = this and
first(this.getAlternative(0), succ) and
c instanceof SimpleCompletion
or
exists(int i |
last(this.getAlternative(i), pred, c) and
first(this.getAlternative(i + 1), succ) and
c.(MatchingCompletion).getValue() = false
)
}
}
private class AsPatternTree extends PreOrderTree, AsPattern {
final override predicate propagatesAbnormal(AstNode child) { child = this.getPattern() }
final override predicate last(AstNode last, Completion c) {
last(this.getPattern(), last, c) and
c.(MatchingCompletion).getValue() = false
or
last(this.getVariableAccess(), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
pred = this and
first(this.getPattern(), succ) and
c instanceof SimpleCompletion
or
last(this.getPattern(), pred, c) and
first(this.getVariableAccess(), succ) and
c.(MatchingCompletion).getValue() = true
}
}
private class ParenthesizedPatternTree extends StandardPreOrderTree, ParenthesizedPattern {
override ControlFlowTree getChildElement(int i) { result = this.getPattern() and i = 0 }
}
private class VariableReferencePatternTree extends StandardPreOrderTree, VariableReferencePattern {
override ControlFlowTree getChildElement(int i) { result = this.getVariableAccess() and i = 0 }
}
private class InClauseTree extends PreOrderTree, InClause {
final override predicate propagatesAbnormal(AstNode child) {
child = this.getPattern() or
child = this.getCondition()
}
private predicate lastCondition(AstNode last, BooleanCompletion c, boolean flag) {
last(this.getCondition(), last, c) and
(
flag = true and this.hasIfCondition()
or
flag = false and this.hasUnlessCondition()
)
}
final override predicate last(AstNode last, Completion c) {
last(this.getPattern(), last, c) and
c.(MatchingCompletion).getValue() = false
or
exists(BooleanCompletion bc, boolean flag, MatchingCompletion mc |
lastCondition(last, bc, flag) and
c =
any(NestedMatchingCompletion nmc |
nmc.getInnerCompletion() = bc and nmc.getOuterCompletion() = mc
)
|
mc.getValue() = false and
bc.getValue() = flag.booleanNot()
or
not exists(this.getBody()) and
mc.getValue() = true and
bc.getValue() = flag
)
or
last(this.getBody(), last, c)
or
not exists(this.getBody()) and
not exists(this.getCondition()) and
last(this.getPattern(), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
pred = this and
first(this.getPattern(), succ) and
c instanceof SimpleCompletion
or
exists(Expr next |
last(this.getPattern(), pred, c) and
c.(MatchingCompletion).getValue() = true and
first(next, succ)
|
next = this.getCondition()
or
not exists(this.getCondition()) and next = this.getBody()
)
or
exists(boolean flag |
lastCondition(pred, c, flag) and
c.(BooleanCompletion).getValue() = flag and
first(this.getBody(), succ)
)
}
}
private class CharacterTree extends LeafTree, CharacterLiteral { }
private class ClassDeclarationTree extends NamespaceTree, ClassDeclaration {
@@ -542,7 +923,8 @@ module Trees {
c instanceof SimpleCompletion
or
this.hasDefaultValue() and
c.(MatchingCompletion).getValue() = true
c.(MatchingCompletion).getValue() = true and
c.isValidFor(this)
)
}

View File

@@ -864,6 +864,7 @@ control/cases.rb:
# 67| getPattern: [ArrayPattern] [ ..., * ]
# 68| getBranch: [InClause] in ... then ...
# 68| getPattern: [ArrayPattern] [ ..., * ]
# 68| getRestVariableAccess: [LocalVariableAccess] x
# 68| getSuffixElement: [IntegerLiteral] 3
# 68| getSuffixElement: [IntegerLiteral] 4
# 69| getBranch: [InClause] in ... then ...

View File

@@ -1,4 +0,0 @@
deadEnd
| control/cases.rb:19:13:19:19 | then ... |
| control/cases.rb:20:13:20:19 | then ... |
| control/cases.rb:21:13:21:19 | then ... |

View File

@@ -156,8 +156,8 @@
| control/cases.rb:65:13:65:13 | 3 | 3 |
| control/cases.rb:66:7:66:7 | 1 | 1 |
| control/cases.rb:66:14:66:14 | 3 | 3 |
| control/cases.rb:68:10:68:10 | 3 | 3 |
| control/cases.rb:68:13:68:13 | 4 | 4 |
| control/cases.rb:68:11:68:11 | 3 | 3 |
| control/cases.rb:68:14:68:14 | 4 | 4 |
| control/cases.rb:69:10:69:10 | 3 | 3 |
| control/cases.rb:70:11:70:11 | 3 | 3 |
| control/cases.rb:71:7:71:7 | :a | a |
@@ -176,6 +176,7 @@
| control/cases.rb:76:13:76:13 | :b | b |
| control/cases.rb:84:7:84:8 | 42 | 42 |
| control/cases.rb:87:6:87:6 | 5 | 5 |
| control/cases.rb:88:7:88:9 | foo | 42 |
| control/cases.rb:90:6:90:13 | "string" | string |
| control/cases.rb:91:9:91:11 | "foo" | foo |
| control/cases.rb:91:13:91:15 | "bar" | bar |

View File

@@ -1,4 +0,0 @@
deadEnd
| cases.rb:19:13:19:19 | then ... |
| cases.rb:20:13:20:19 | then ... |
| cases.rb:21:13:21:19 | then ... |

View File

@@ -67,7 +67,7 @@ caseAllBranches
| cases.rb:39:1:80:3 | case ... | 25 | cases.rb:65:3:65:18 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 26 | cases.rb:66:3:66:16 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 27 | cases.rb:67:3:67:9 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 28 | cases.rb:68:3:68:15 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 28 | cases.rb:68:3:68:16 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 29 | cases.rb:69:3:69:15 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 30 | cases.rb:70:3:70:17 | in ... then ... |
| cases.rb:39:1:80:3 | case ... | 31 | cases.rb:71:3:71:10 | in ... then ... |

View File

@@ -65,7 +65,7 @@ case expr
in [1, 2, 3, *]
in [1, *x, 3]
in [*]
in [*, 3, 4]
in [*x, 3, 4]
in [*, 3, *]
in [*a, 3, *b]
in {a:}

View File

@@ -492,7 +492,7 @@ case.rb:
#-----| -> if_in_case
# 1| if_in_case
#-----| -> exit case.rb (normal)
#-----| -> case_match
# 1| exit if_in_case
@@ -567,6 +567,795 @@ case.rb:
# 4| "2"
#-----| -> call to puts
# 8| enter case_match
#-----| -> value
# 8| case_match
#-----| -> case_match_no_match
# 8| exit case_match
# 8| exit case_match (normal)
#-----| -> exit case_match
# 8| value
#-----| -> case ...
# 9| case ...
#-----| -> value
# 9| value
#-----| -> in ... then ...
# 10| in ... then ...
#-----| -> 0
# 10| 0
#-----| no-match -> in ... then ...
#-----| match -> exit case_match (normal)
# 11| in ... then ...
#-----| -> 1
# 11| 1
#-----| no-match -> in ... then ...
#-----| match -> 3
# 11| then ...
#-----| -> exit case_match (normal)
# 11| 3
#-----| -> then ...
# 12| in ... then ...
#-----| -> 2
# 12| 2
#-----| no-match -> in ... then ...
#-----| match -> 4
# 12| then ...
#-----| -> exit case_match (normal)
# 13| 4
#-----| -> then ...
# 14| in ... then ...
#-----| -> x
# 14| x
#-----| match -> x
# 14| ... == ...
#-----| false -> in ... then ...
#-----| true -> 6
# 14| x
#-----| -> 5
# 14| 5
#-----| -> ... == ...
# 14| then ...
#-----| -> exit case_match (normal)
# 14| 6
#-----| -> then ...
# 15| in ... then ...
#-----| -> x
# 15| x
#-----| match -> x
# 15| ... < ...
#-----| false -> 7
#-----| true -> 8
# 15| x
#-----| -> 0
# 15| 0
#-----| -> ... < ...
# 15| then ...
#-----| -> exit case_match (normal)
# 15| 7
#-----| -> then ...
# 16| else ...
#-----| -> exit case_match (normal)
# 16| 8
#-----| -> else ...
# 20| enter case_match_no_match
#-----| -> value
# 20| case_match_no_match
#-----| -> case_match_raise
# 20| exit case_match_no_match
# 20| exit case_match_no_match (abnormal)
#-----| -> exit case_match_no_match
# 20| exit case_match_no_match (normal)
#-----| -> exit case_match_no_match
# 20| value
#-----| -> case ...
# 21| case ...
#-----| -> value
# 21| value
#-----| -> in ... then ...
# 22| in ... then ...
#-----| -> 1
# 22| 1
#-----| raise -> exit case_match_no_match (abnormal)
#-----| match -> exit case_match_no_match (normal)
# 26| enter case_match_raise
#-----| -> value
# 26| case_match_raise
#-----| -> case_match_array
# 26| exit case_match_raise
# 26| exit case_match_raise (abnormal)
#-----| -> exit case_match_raise
# 26| exit case_match_raise (normal)
#-----| -> exit case_match_raise
# 26| value
#-----| -> case ...
# 27| case ...
#-----| -> value
# 27| value
#-----| -> in ... then ...
# 28| in ... then ...
#-----| -> -> { ... }
# 28| enter -> { ... }
#-----| -> x
# 28| -> { ... }
#-----| raise -> exit case_match_raise (abnormal)
#-----| match -> exit case_match_raise (normal)
# 28| exit -> { ... }
# 28| exit -> { ... } (abnormal)
#-----| -> exit -> { ... }
# 28| x
#-----| -> self
# 28| call to raise
#-----| raise -> exit -> { ... } (abnormal)
# 28| self
#-----| -> "oops"
# 28| "oops"
#-----| -> call to raise
# 32| enter case_match_array
#-----| -> value
# 32| case_match_array
#-----| -> case_match_find
# 32| exit case_match_array
# 32| exit case_match_array (abnormal)
#-----| -> exit case_match_array
# 32| exit case_match_array (normal)
#-----| -> exit case_match_array
# 32| value
#-----| -> case ...
# 33| case ...
#-----| -> value
# 33| value
#-----| -> in ... then ...
# 34| in ... then ...
#-----| -> [ ..., * ]
# 34| [ ..., * ]
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_array (normal)
# 35| in ... then ...
#-----| -> [ ..., * ]
# 35| [ ..., * ]
#-----| no-match -> in ... then ...
#-----| match -> x
# 35| x
#-----| match -> exit case_match_array (normal)
# 36| in ... then ...
#-----| -> [ ..., * ]
# 36| [ ..., * ]
#-----| no-match -> in ... then ...
#-----| match -> x
# 36| x
#-----| match -> exit case_match_array (normal)
# 37| in ... then ...
#-----| -> Bar
# 37| [ ..., * ]
#-----| match -> a
#-----| raise -> exit case_match_array (abnormal)
# 37| Bar
#-----| match -> [ ..., * ]
#-----| raise -> exit case_match_array (abnormal)
# 37| a
#-----| match -> b
# 37| b
#-----| match -> c
# 37| c
#-----| -> d
# 37| d
#-----| match -> e
# 37| e
#-----| match -> exit case_match_array (normal)
# 41| enter case_match_find
#-----| -> value
# 41| case_match_find
#-----| -> case_match_hash
# 41| exit case_match_find
# 41| exit case_match_find (abnormal)
#-----| -> exit case_match_find
# 41| exit case_match_find (normal)
#-----| -> exit case_match_find
# 41| value
#-----| -> case ...
# 42| case ...
#-----| -> value
# 42| value
#-----| -> in ... then ...
# 43| in ... then ...
#-----| -> [ *,...,* ]
# 43| [ *,...,* ]
#-----| match -> x
#-----| raise -> exit case_match_find (abnormal)
# 43| x
#-----| -> 1
# 43| 1
#-----| match -> 2
#-----| raise -> exit case_match_find (abnormal)
# 43| 2
#-----| match -> y
#-----| raise -> exit case_match_find (abnormal)
# 43| y
#-----| -> exit case_match_find (normal)
# 47| enter case_match_hash
#-----| -> value
# 47| case_match_hash
#-----| -> case_match_variable
# 47| exit case_match_hash
# 47| exit case_match_hash (abnormal)
#-----| -> exit case_match_hash
# 47| exit case_match_hash (normal)
#-----| -> exit case_match_hash
# 47| value
#-----| -> case ...
# 48| case ...
#-----| -> value
# 48| value
#-----| -> in ... then ...
# 49| in ... then ...
#-----| -> Foo
# 49| { ..., ** }
#-----| no-match -> in ... then ...
#-----| match -> 1
# 49| Foo
#-----| -> Bar
# 49| Bar
#-----| match -> { ..., ** }
#-----| no-match -> in ... then ...
# 49| 1
#-----| no-match -> in ... then ...
#-----| match -> rest
# 49| rest
#-----| -> exit case_match_hash (normal)
# 50| in ... then ...
#-----| -> Bar
# 50| { ..., ** }
#-----| no-match -> in ... then ...
#-----| match -> 1
# 50| Bar
#-----| match -> { ..., ** }
#-----| no-match -> in ... then ...
# 50| 1
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_hash (normal)
# 51| in ... then ...
#-----| -> Bar
# 51| { ..., ** }
#-----| raise -> exit case_match_hash (abnormal)
#-----| match -> exit case_match_hash (normal)
# 51| Bar
#-----| match -> { ..., ** }
#-----| raise -> exit case_match_hash (abnormal)
# 55| enter case_match_variable
#-----| -> value
# 55| case_match_variable
#-----| -> case_match_underscore
# 55| exit case_match_variable
# 55| exit case_match_variable (normal)
#-----| -> exit case_match_variable
# 55| value
#-----| -> case ...
# 56| case ...
#-----| -> value
# 56| value
#-----| -> in ... then ...
# 57| in ... then ...
#-----| -> 5
# 57| 5
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_variable (normal)
# 58| in ... then ...
#-----| -> var
# 58| var
#-----| match -> exit case_match_variable (normal)
# 63| enter case_match_underscore
#-----| -> value
# 63| case_match_underscore
#-----| -> case_match_various
# 63| exit case_match_underscore
# 63| exit case_match_underscore (normal)
#-----| -> exit case_match_underscore
# 63| value
#-----| -> case ...
# 64| case ...
#-----| -> value
# 64| value
#-----| -> in ... then ...
# 65| in ... then ...
#-----| -> ... | ...
# 65| ... | ...
#-----| -> 5
# 65| 5
#-----| no-match -> _
#-----| match -> exit case_match_underscore (normal)
# 65| _
#-----| match -> exit case_match_underscore (normal)
# 69| enter case_match_various
#-----| -> value
# 69| case_match_various
#-----| -> case_match_guard_no_else
# 69| exit case_match_various
# 69| exit case_match_various (abnormal)
#-----| -> exit case_match_various
# 69| exit case_match_various (normal)
#-----| -> exit case_match_various
# 69| value
#-----| -> foo
# 70| ... = ...
#-----| -> case ...
# 70| foo
#-----| -> 42
# 70| 42
#-----| -> ... = ...
# 72| case ...
#-----| -> value
# 72| value
#-----| -> in ... then ...
# 73| in ... then ...
#-----| -> 5
# 73| 5
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 74| in ... then ...
#-----| -> ^...
# 74| ^...
#-----| -> foo
# 74| foo
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 75| in ... then ...
#-----| -> "string"
# 75| "string"
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 76| in ... then ...
#-----| -> Array
# 76| call to []
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 76| Array
#-----| -> "foo"
# 76| "foo"
#-----| -> "bar"
# 76| "bar"
#-----| -> call to []
# 77| in ... then ...
#-----| -> Array
# 77| call to []
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 77| Array
#-----| -> :"foo"
# 77| :"foo"
#-----| -> :"bar"
# 77| :"bar"
#-----| -> call to []
# 78| in ... then ...
#-----| -> /.*abc[0-9]/
# 78| /.*abc[0-9]/
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 79| in ... then ...
#-----| -> 5
# 79| 5
#-----| -> 10
# 79| _ .. _
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 79| 10
#-----| -> _ .. _
# 80| in ... then ...
#-----| -> 10
# 80| _ .. _
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 80| 10
#-----| -> _ .. _
# 81| in ... then ...
#-----| -> 5
# 81| 5
#-----| -> _ .. _
# 81| _ .. _
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 82| in ... then ...
#-----| -> ... => ...
# 82| ... => ...
#-----| -> 5
# 82| 5
#-----| no-match -> in ... then ...
#-----| match -> x
# 82| x
#-----| -> exit case_match_various (normal)
# 83| in ... then ...
#-----| -> ... | ...
# 83| ... | ...
#-----| -> 5
# 83| 5
#-----| no-match -> ^...
#-----| match -> exit case_match_various (normal)
# 83| ^...
#-----| -> foo
# 83| foo
#-----| no-match -> "string"
#-----| match -> exit case_match_various (normal)
# 83| "string"
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 84| in ... then ...
#-----| -> Foo
# 84| Bar
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 84| Foo
#-----| -> Bar
# 85| in ... then ...
#-----| -> -> { ... }
# 85| enter -> { ... }
#-----| -> x
# 85| -> { ... }
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 85| exit -> { ... }
# 85| exit -> { ... } (normal)
#-----| -> exit -> { ... }
# 85| x
#-----| -> x
# 85| ... == ...
#-----| -> exit -> { ... } (normal)
# 85| x
#-----| -> 10
# 85| 10
#-----| -> ... == ...
# 86| in ... then ...
#-----| -> :foo
# 86| :foo
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 87| in ... then ...
#-----| -> :"foo bar"
# 87| :"foo bar"
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 88| in ... then ...
#-----| -> ... | ...
# 88| ... | ...
#-----| -> 5
# 88| - ...
#-----| no-match -> 10
#-----| match -> exit case_match_various (normal)
# 88| 5
#-----| -> - ...
# 88| + ...
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 88| 10
#-----| -> + ...
# 89| in ... then ...
#-----| -> ... | ...
# 89| ... | ...
#-----| -> nil
# 89| nil
#-----| no-match -> self
#-----| match -> exit case_match_various (normal)
# 89| self
#-----| no-match -> true
#-----| match -> exit case_match_various (normal)
# 89| true
#-----| no-match -> false
#-----| match -> exit case_match_various (normal)
# 89| false
#-----| no-match -> __LINE__
#-----| match -> exit case_match_various (normal)
# 89| __LINE__
#-----| no-match -> __FILE__
#-----| match -> exit case_match_various (normal)
# 89| __FILE__
#-----| no-match -> __ENCODING__
#-----| match -> exit case_match_various (normal)
# 89| __ENCODING__
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 90| in ... then ...
#-----| -> ( ... )
# 90| ( ... )
#-----| -> 1
# 90| 1
#-----| -> _ .. _
# 90| _ .. _
#-----| no-match -> in ... then ...
#-----| match -> exit case_match_various (normal)
# 91| in ... then ...
#-----| -> ( ... )
# 91| ( ... )
#-----| -> ... | ...
# 91| ... | ...
#-----| -> 0
# 91| 0
#-----| no-match -> ""
#-----| match -> exit case_match_various (normal)
# 91| ""
#-----| no-match -> [ ..., * ]
#-----| match -> exit case_match_various (normal)
# 91| [ ..., * ]
#-----| no-match -> { ..., ** }
#-----| match -> exit case_match_various (normal)
# 91| { ..., ** }
#-----| raise -> exit case_match_various (abnormal)
#-----| match -> exit case_match_various (normal)
# 95| enter case_match_guard_no_else
#-----| -> value
# 95| case_match_guard_no_else
#-----| -> exit case.rb (normal)
# 95| exit case_match_guard_no_else
# 95| exit case_match_guard_no_else (abnormal)
#-----| -> exit case_match_guard_no_else
# 95| exit case_match_guard_no_else (normal)
#-----| -> exit case_match_guard_no_else
# 95| value
#-----| -> case ...
# 96| case ...
#-----| -> value
# 96| value
#-----| -> in ... then ...
# 97| in ... then ...
#-----| -> x
# 97| x
#-----| match -> x
# 97| ... == ...
#-----| true -> 6
#-----| raise -> exit case_match_guard_no_else (abnormal)
# 97| x
#-----| -> 5
# 97| 5
#-----| -> ... == ...
# 97| then ...
#-----| -> exit case_match_guard_no_else (normal)
# 97| 6
#-----| -> then ...
cfg.html.erb:
# 5| enter cfg.html.erb
#-----| -> @title

View File

@@ -9,6 +9,8 @@ callsWithNoArguments
| break_ensure.rb:29:8:29:13 | call to nil? |
| case.rb:2:8:2:9 | call to x1 |
| case.rb:3:21:3:22 | call to x2 |
| case.rb:88:8:88:9 | - ... |
| case.rb:88:13:88:15 | + ... |
| cfg.html.erb:12:24:12:24 | call to a |
| cfg.html.erb:15:32:15:32 | call to a |
| cfg.html.erb:16:32:16:32 | call to b |
@@ -80,6 +82,15 @@ positionalArguments
| break_ensure.rb:51:10:51:14 | [ensure: raise] ... > ... | break_ensure.rb:51:14:51:14 | [ensure: raise] 0 |
| case.rb:3:29:3:37 | call to puts | case.rb:3:34:3:37 | "x2" |
| case.rb:4:17:4:24 | call to puts | case.rb:4:22:4:24 | "2" |
| case.rb:14:13:14:18 | ... == ... | case.rb:14:18:14:18 | 5 |
| case.rb:15:17:15:21 | ... < ... | case.rb:15:21:15:21 | 0 |
| case.rb:28:14:28:25 | call to raise | case.rb:28:20:28:25 | "oops" |
| case.rb:76:8:76:18 | call to [] | case.rb:76:11:76:13 | "foo" |
| case.rb:76:8:76:18 | call to [] | case.rb:76:15:76:17 | "bar" |
| case.rb:77:8:77:18 | call to [] | case.rb:77:11:77:13 | :"foo" |
| case.rb:77:8:77:18 | call to [] | case.rb:77:15:77:17 | :"bar" |
| case.rb:85:15:85:21 | ... == ... | case.rb:85:20:85:21 | 10 |
| case.rb:97:13:97:18 | ... == ... | case.rb:97:18:97:18 | 5 |
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | cfg.html.erb:6:29:6:41 | "application" |
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | cfg.html.erb:6:44:6:58 | Pair |
| cfg.html.erb:12:11:12:33 | call to link_to | cfg.html.erb:12:19:12:21 | "A" |

View File

@@ -4,3 +4,96 @@ def if_in_case
when 2 then puts "2"
end
end
def case_match value
case value
in 0
in 1 then 3
in 2
4
in x if x == 5 then 6
in x unless x < 0 then 7
else 8
end
end
def case_match_no_match value
case value
in 1
end
end
def case_match_raise value
case value
in -> x { raise "oops" }
end
end
def case_match_array value
case value
in [];
in [x];
in [x, ];
in Bar(a, b, *c, d, e);
end
end
def case_match_find value
case value
in [*x, 1, 2, *y];
end
end
def case_match_hash value
case value
in Foo::Bar[ x:1, a:, **rest ];
in Bar( a: 1, **nil);
in Bar( ** );
end
end
def case_match_variable value
case value
in 5
in var
in "unreachable"
end
end
def case_match_underscore value
case value
in 5 | _ | "unreachable"
end
end
def case_match_various value
foo = 42
case value
in 5
in ^foo
in "string"
in %w(foo bar)
in %i(foo bar)
in /.*abc[0-9]/
in 5 .. 10
in .. 10
in 5 ..
in 5 => x
in 5 | ^foo | "string"
in ::Foo::Bar
in -> x { x == 10 }
in :foo
in :"foo bar"
in -5 | +10
in nil | self | true | false | __LINE__ | __FILE__ | __ENCODING__
in (1 ..)
in (0 | "" | [] | {})
end
end
def case_match_guard_no_else value
case value
in x if x == 5 then 6
end
end