diff --git a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll index d69a65243b8..8df84ffd371 100644 --- a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll +++ b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll @@ -186,12 +186,17 @@ module SuccessorTypes { /** * A conditional control flow successor. Either a Boolean successor (`BooleanSuccessor`), - * or an emptiness successor (`EmptinessSuccessor`). + * an emptiness successor (`EmptinessSuccessor`), or a matching successor + * (`MatchingSuccessor`) */ class ConditionalSuccessor extends SuccessorType { boolean value; - ConditionalSuccessor() { this = TBooleanSuccessor(value) or this = TEmptinessSuccessor(value) } + ConditionalSuccessor() { + this = TBooleanSuccessor(value) or + this = TEmptinessSuccessor(value) or + this = TMatchingSuccessor(value) + } /** Gets the Boolean value of this successor. */ final boolean getValue() { result = value } @@ -248,12 +253,38 @@ module SuccessorTypes { * ``` */ class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor { - /** Holds if this is an empty successor. */ - predicate isEmpty() { value = true } + override string toString() { if value = true then result = "empty" else result = "non-empty" } + } - final override string toString() { - if this.isEmpty() then result = "empty" else result = "non-empty" - } + /** + * A matching control flow successor. + * + * For example, this program fragment: + * + * ```rb + * case x + * when 1 then puts "one" + * else puts "not one" + * end + * ``` + * + * has a control flow graph containing matching successors: + * + * ``` + * x + * | + * 1 + * / \ + * / \ + * / \ + * / \ + * match non-match + * | | + * puts "one" puts "not one" + * ``` + */ + class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor { + override string toString() { if value = true then result = "match" else result = "no-match" } } /** diff --git a/ql/src/codeql_ruby/controlflow/internal/Cfg.ql b/ql/src/codeql_ruby/controlflow/internal/Cfg.ql index 563f1400079..8ec24510d14 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Cfg.ql +++ b/ql/src/codeql_ruby/controlflow/internal/Cfg.ql @@ -1,3 +1,7 @@ +/** + * @kind graph + */ + import codeql_ruby.controlflow.ControlFlowGraph query predicate nodes(CfgNode n) { any() } @@ -5,6 +9,6 @@ query predicate nodes(CfgNode n) { any() } query predicate edges(CfgNode pred, CfgNode succ, string attr, string val) { exists(SuccessorType t | succ = pred.getASuccessor(t) | attr = "semmle.label" and - val = t.toString() + if t instanceof SuccessorTypes::NormalSuccessor then val = "" else val = t.toString() ) } diff --git a/ql/src/codeql_ruby/controlflow/internal/Completion.qll b/ql/src/codeql_ruby/controlflow/internal/Completion.qll index 71d09d6e7db..ebd84634ea1 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Completion.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Completion.qll @@ -7,13 +7,15 @@ private import codeql_ruby.ast.internal.TreeSitter::Generated private import codeql_ruby.controlflow.ControlFlowGraph private import AstNodes +private import ControlFlowGraphImpl private import NonReturning private import SuccessorTypes private newtype TCompletion = TSimpleCompletion() or - TBooleanCompletion(boolean b) { b = true or b = false } or - TEmptinessCompletion(boolean isEmpty) { isEmpty = true or isEmpty = false } or + TBooleanCompletion(boolean b) { b in [false, true] } or + TEmptinessCompletion(boolean isEmpty) { isEmpty in [false, true] } or + TMatchingCompletion(boolean isMatch) { isMatch in [false, true] } or TReturnCompletion() or TBreakCompletion() or TNextCompletion() or @@ -21,11 +23,35 @@ private newtype TCompletion = TRetryCompletion() or TRaiseCompletion() or // TODO: Add exception type? TExitCompletion() or - TNestedCompletion(Completion inner, Completion outer) { - outer = TSimpleCompletion() and - inner = TBreakCompletion() + TNestedCompletion(Completion inner, Completion outer, int nestLevel) { + inner = TBreakCompletion() and + outer instanceof NonNestedNormalCompletion and + nestLevel = 0 + or + inner instanceof NormalCompletion and + nestedEnsureCompletion(outer, nestLevel) } +pragma[noinline] +private predicate nestedEnsureCompletion(Completion outer, int nestLevel) { + ( + outer = TReturnCompletion() + or + outer = TBreakCompletion() + or + outer = TNextCompletion() + or + outer = TRedoCompletion() + or + outer = TRetryCompletion() + or + outer = TRaiseCompletion() + or + outer = TExitCompletion() + ) and + nestLevel = any(Trees::RescueEnsureBlockTree t).nestLevel() +} + pragma[noinline] private predicate completionIsValidForStmt(AstNode n, Completion c) { n instanceof Break and @@ -57,11 +83,15 @@ abstract class Completion extends TCompletion { this = TBooleanCompletion(_) ) or + mustHaveMatchingCompletion(n) and + this = TMatchingCompletion(_) + or n = any(RescueModifier parent).getBody() and this = TRaiseCompletion() or not n instanceof NonReturningCall and not completionIsValidForStmt(n, _) and not mustHaveBooleanCompletion(n) and + not mustHaveMatchingCompletion(n) and this = TSimpleCompletion() } @@ -140,7 +170,35 @@ private predicate inBooleanContext(AstNode n) { or n = any(ParenthesizedStatement parent | inBooleanContext(parent)).getChild() or - n instanceof Pattern + exists(Case c, When w | + not exists(c.getValue()) and + c.getChild(_) = w and + w.getPattern(_).getChild() = n + ) +} + +/** + * Holds if a normal completion of `n` must be a matching completion. + */ +private predicate mustHaveMatchingCompletion(AstNode n) { + inMatchingContext(n) and + not n instanceof NonReturningCall +} + +/** + * Holds if `n` is used in a matching context. That is, whether or + * not the value of `n` matches, determines the successor. + */ +private predicate inMatchingContext(AstNode n) { + n = any(Rescue r).getExceptions().getChild(_) + or + exists(Case c, When w | + exists(c.getValue()) and + c.getChild(_) = w and + w.getPattern(_).getChild() = n + ) + or + n.(Trees::DefaultValueParameterTree).hasDefaultValue() } /** @@ -149,8 +207,10 @@ private predicate inBooleanContext(AstNode n) { */ abstract class NormalCompletion extends Completion { } +abstract private class NonNestedNormalCompletion extends NormalCompletion { } + /** A simple (normal) completion. */ -class SimpleCompletion extends NormalCompletion, TSimpleCompletion { +class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion { override NormalSuccessor getAMatchingSuccessorType() { any() } override string toString() { result = "simple" } @@ -158,23 +218,26 @@ class SimpleCompletion extends NormalCompletion, TSimpleCompletion { /** * A completion that represents evaluation of an expression, whose value determines - * the successor. Either a Boolean completion (`BooleanCompletion`) - * or an emptiness completion (`EmptinessCompletion`). + * the successor. Either a Boolean completion (`BooleanCompletion`), an emptiness + * completion (`EmptinessCompletion`), or a matching completion (`MatchingCompletion`). */ -abstract class ConditionalCompletion extends NormalCompletion { } +abstract class ConditionalCompletion extends NonNestedNormalCompletion { + boolean value; + + bindingset[value] + ConditionalCompletion() { any() } + + /** Gets the Boolean value of this conditional completion. */ + final boolean getValue() { result = value } +} /** * A completion that represents evaluation of an expression * with a Boolean value. */ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion { - private boolean value; - BooleanCompletion() { this = TBooleanCompletion(value) } - /** Gets the Boolean value of this completion. */ - boolean getValue() { result = value } - /** Gets the dual Boolean completion. */ BooleanCompletion getDual() { result = TBooleanCompletion(value.booleanNot()) } @@ -198,84 +261,149 @@ class FalseCompletion extends BooleanCompletion { * a test in a `for in` statement. */ class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion { - /** Holds if the emptiness test evaluates to `true`. */ - predicate isEmpty() { this = TEmptinessCompletion(true) } + EmptinessCompletion() { this = TEmptinessCompletion(value) } - override EmptinessSuccessor getAMatchingSuccessorType() { - if isEmpty() then result.getValue() = true else result.getValue() = false - } + override EmptinessSuccessor getAMatchingSuccessorType() { result.getValue() = value } - override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" } + override string toString() { if value = true then result = "empty" else result = "non-empty" } +} + +/** + * 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) } + + override MatchingSuccessor getAMatchingSuccessorType() { result.getValue() = value } + + override string toString() { if value = true then result = "match" else result = "no-match" } } /** * A completion that represents evaluation of a statement or an * expression resulting in a return. */ -class ReturnCompletion extends Completion, TReturnCompletion { +class ReturnCompletion extends Completion { + ReturnCompletion() { + this = TReturnCompletion() or + this = TNestedCompletion(_, TReturnCompletion(), _) + } + override ReturnSuccessor getAMatchingSuccessorType() { any() } - override string toString() { result = "return" } + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TReturnCompletion() and result = "return" + } } /** * A completion that represents evaluation of a statement or an * expression resulting in a break from a loop. */ -class BreakCompletion extends Completion, TBreakCompletion { +class BreakCompletion extends Completion { + BreakCompletion() { + this = TBreakCompletion() or + this = TNestedCompletion(_, TBreakCompletion(), _) + } + override BreakSuccessor getAMatchingSuccessorType() { any() } - override string toString() { result = "break" } + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TBreakCompletion() and result = "break" + } } /** * A completion that represents evaluation of a statement or an * expression resulting in a continuation of a loop. */ -class NextCompletion extends Completion, TNextCompletion { +class NextCompletion extends Completion { + NextCompletion() { + this = TNextCompletion() or + this = TNestedCompletion(_, TNextCompletion(), _) + } + override NextSuccessor getAMatchingSuccessorType() { any() } - override string toString() { result = "next" } + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TNextCompletion() and result = "next" + } } /** * A completion that represents evaluation of a statement or an * expression resulting in a redo of a loop iteration. */ -class RedoCompletion extends Completion, TRedoCompletion { +class RedoCompletion extends Completion { + RedoCompletion() { + this = TRedoCompletion() or + this = TNestedCompletion(_, TRedoCompletion(), _) + } + override RedoSuccessor getAMatchingSuccessorType() { any() } - override string toString() { result = "redo" } + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TRedoCompletion() and result = "redo" + } } /** * A completion that represents evaluation of a statement or an * expression resulting in a retry. */ -class RetryCompletion extends Completion, TRetryCompletion { +class RetryCompletion extends Completion { + RetryCompletion() { + this = TRetryCompletion() or + this = TNestedCompletion(_, TRetryCompletion(), _) + } + override RetrySuccessor getAMatchingSuccessorType() { any() } - override string toString() { result = "retry" } + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TRetryCompletion() and result = "retry" + } } /** * A completion that represents evaluation of a statement or an * expression resulting in a thrown exception. */ -class RaiseCompletion extends Completion, TRaiseCompletion { +class RaiseCompletion extends Completion { + RaiseCompletion() { + this = TRaiseCompletion() or + this = TNestedCompletion(_, TRaiseCompletion(), _) + } + override RaiseSuccessor getAMatchingSuccessorType() { any() } - override string toString() { result = "raise" } + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TRaiseCompletion() and result = "raise" + } } /** * A completion that represents evaluation of a statement or an * expression resulting in an abort/exit. */ -class ExitCompletion extends Completion, TExitCompletion { +class ExitCompletion extends Completion { + ExitCompletion() { + this = TExitCompletion() or + this = TNestedCompletion(_, TExitCompletion(), _) + } + override ExitSuccessor getAMatchingSuccessorType() { any() } - override string toString() { result = "exit" } + override string toString() { + // `NestedCompletion` defines `toString()` for the other case + this = TExitCompletion() and result = "exit" + } } /** @@ -296,25 +424,60 @@ class ExitCompletion extends Completion, TExitCompletion { * the `while` loop can have a nested completion where the inner completion * is a `break` and the outer completion is a simple successor. */ -class NestedCompletion extends Completion, TNestedCompletion { +abstract class NestedCompletion extends Completion, TNestedCompletion { Completion inner; Completion outer; + int nestLevel; - NestedCompletion() { this = TNestedCompletion(inner, outer) } + NestedCompletion() { this = TNestedCompletion(inner, outer, nestLevel) } - override Completion getInnerCompletion() { result = inner } + /** Gets a completion that is compatible with the inner completion. */ + abstract Completion getAnInnerCompatibleCompletion(); + + /** Gets the level of this nested completion. */ + final int getNestLevel() { result = nestLevel } + + override string toString() { result = outer + " [" + inner + "] (" + nestLevel + ")" } +} + +class NestedBreakCompletion extends NormalCompletion, NestedCompletion { + NestedBreakCompletion() { + inner = TBreakCompletion() and + outer instanceof NonNestedNormalCompletion + } + + override BreakCompletion getInnerCompletion() { result = inner } + + override NonNestedNormalCompletion getOuterCompletion() { result = outer } + + override Completion getAnInnerCompatibleCompletion() { + result = inner and + outer = TSimpleCompletion() + or + result = TNestedCompletion(outer, inner, _) + } + + override SuccessorType getAMatchingSuccessorType() { + outer instanceof SimpleCompletion and + result instanceof BreakSuccessor + or + result = outer.(ConditionalCompletion).getAMatchingSuccessorType() + } +} + +class NestedEnsureCompletion extends NestedCompletion { + NestedEnsureCompletion() { + inner instanceof NormalCompletion and + nestedEnsureCompletion(outer, nestLevel) + } + + override NormalCompletion getInnerCompletion() { result = inner } override Completion getOuterCompletion() { result = outer } - override SuccessorType getAMatchingSuccessorType() { - inner = TBreakCompletion() and - outer = TSimpleCompletion() and - result instanceof BreakSuccessor + override Completion getAnInnerCompatibleCompletion() { + result.getOuterCompletion() = this.getInnerCompletion() } - override string toString() { result = outer + " [" + inner + "]" } -} - -private class NestedNormalCompletion extends NormalCompletion, NestedCompletion { - NestedNormalCompletion() { outer instanceof NormalCompletion } + override SuccessorType getAMatchingSuccessorType() { none() } } diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 6d9d4f7b6a3..66199da4003 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -38,6 +38,14 @@ private import Completion private import SuccessorTypes private import Splitting +private AstNode parent(AstNode n) { + result.getAFieldOrChild() = n and + not n instanceof CfgScope +} + +/** Gets the CFG scope of node `n`. */ +CfgScope getScope(AstNode n) { result = parent+(n) } + abstract private class ControlFlowTree extends AstNode { /** * Holds if `first` is the first element executed within this AST node. @@ -227,7 +235,7 @@ abstract private class LeafTree extends PreOrderTree, PostOrderTree { } /** Defines the CFG by dispatch on the various AST types. */ -private module Trees { +module Trees { private class AliasTree extends StandardPreOrderTree, Alias { final override AstNode getChildNode(int i) { i = 0 and result = this.getName() @@ -262,12 +270,9 @@ private module Trees { final override Interpolation getChildNode(int i) { result = this.getChild(i) } } - private class BeginTree extends StandardPreOrderTree, Begin { - final override AstNode getChildNode(int i) { - result = this.getChild(i) and - not result instanceof Rescue and - not result instanceof Else and - not result instanceof Ensure + private class BeginTree extends RescueEnsureBlockTree, Begin { + final override AstNode getChildNode(int i, boolean rescuable) { + result = this.getChild(i) and rescuable = true } override predicate isHidden() { any() } @@ -357,7 +362,7 @@ private module Trees { exists(int i, WhenTree branch | branch = this.getChild(i) | last(branch.getLastPattern(), pred, c) and first(this.getChild(i + 1), succ) and - c instanceof FalseCompletion + c.(ConditionalCompletion).getValue() = false ) } } @@ -370,11 +375,11 @@ private module Trees { private class CharacterTree extends LeafTree, Character { } - private class ClassTree extends StandardPreOrderTree, Class { - final override AstNode getChildNode(int i) { - result = this.getName() and i = 0 + private class ClassTree extends RescueEnsureBlockTree, Class { + final override AstNode getChildNode(int i, boolean rescuable) { + result = this.getName() and i = 0 and rescuable = false or - result = this.getChild(i - 1) + result = this.getChild(i - 1) and rescuable = true } } @@ -384,6 +389,33 @@ private module Trees { private class ConstantTree extends LeafTree, Constant { } + /** A parameter that may have a default value. */ + abstract class DefaultValueParameterTree extends PreOrderTree { + abstract AstNode getDefaultValue(); + + predicate hasDefaultValue() { exists(this.getDefaultValue()) } + + final override predicate propagatesAbnormal(AstNode child) { child = this.getDefaultValue() } + + final override predicate last(AstNode last, Completion c) { + last = this and + exists(this.getDefaultValue()) and + c.(MatchingCompletion).getValue() = true + or + last(this.getDefaultValue(), last, c) + or + last = this and + not exists(this.getDefaultValue()) and + c instanceof SimpleCompletion + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + pred = this and + first(this.getDefaultValue(), succ) and + c.(MatchingCompletion).getValue() = false + } + } + private class DestructuredLeftAssignmentTree extends StandardPostOrderTree, DestructuredLeftAssignment { final override AstNode getChildNode(int i) { result = this.getChild(i) } @@ -399,11 +431,11 @@ private module Trees { override predicate isHidden() { any() } } - private class DoBlockTree extends StandardPreOrderTree, DoBlock { - final override AstNode getChildNode(int i) { - result = this.getParameters() and i = 0 + private class DoBlockTree extends RescueEnsureBlockTree, DoBlock { + final override AstNode getChildNode(int i, boolean rescuable) { + result = this.getParameters() and i = 0 and rescuable = false or - result = this.getChild(i - 1) + result = this.getChild(i - 1) and rescuable = true } override predicate isHidden() { any() } @@ -431,8 +463,6 @@ private module Trees { private class EnsureTree extends StandardPreOrderTree, Ensure { final override AstNode getChildNode(int i) { result = this.getChild(i) } - - override predicate isHidden() { any() } } private class EscapeSequenceTree extends LeafTree, EscapeSequence { } @@ -443,8 +473,30 @@ private module Trees { override predicate isHidden() { any() } } - private class ExceptionsTree extends StandardPostOrderTree, Exceptions { - final override AstNode getChildNode(int i) { result = this.getChild(i) } + private class ExceptionsTree extends PreOrderTree, Exceptions { + final override predicate propagatesAbnormal(AstNode child) { none() } + + final override predicate last(AstNode last, Completion c) { + last(this.getChild(_), last, c) and + c.(MatchingCompletion).getValue() = true + or + exists(int lst | + last(this.getChild(lst), last, c) and + not exists(this.getChild(lst + 1)) + ) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + pred = this and + first(this.getChild(0), succ) and + c instanceof SimpleCompletion + or + exists(int i | + last(this.getChild(i), pred, c) and + c.(MatchingCompletion).getValue() = false and + first(this.getChild(i + 1), succ) + ) + } override predicate isHidden() { any() } } @@ -493,18 +545,14 @@ private module Trees { final override predicate last(AstNode last, Completion c) { last = this and - c.(EmptinessCompletion).isEmpty() + c.(EmptinessCompletion).getValue() = true or last(this.getBody(), last, c) and not c.continuesLoop() and not c instanceof BreakCompletion and not c instanceof RedoCompletion or - c = - any(NestedCompletion nc | - last(this.getBody(), last, nc.getInnerCompletion().(BreakCompletion)) and - nc.getOuterCompletion() instanceof SimpleCompletion - ) + last(this.getBody(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) } /** @@ -521,8 +569,7 @@ private module Trees { or pred = this and first(this.getPattern(0), succ) and - c instanceof EmptinessCompletion and - not c.(EmptinessCompletion).isEmpty() + c.(EmptinessCompletion).getValue() = false or exists(int i, ControlFlowTree next | last(this.getPattern(i), pred, c) and @@ -637,8 +684,8 @@ private module Trees { final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } } - private class KeywordParameterTree extends StandardPostOrderTree, KeywordParameter { - final override AstNode getChildNode(int i) { result = this.getValue() and i = 0 } + private class KeywordParameterTree extends DefaultValueParameterTree, KeywordParameter { + final override AstNode getDefaultValue() { result = this.getValue() } } private class LambdaTree extends StandardPreOrderTree, Lambda { @@ -719,11 +766,11 @@ private module Trees { } } - private class MethodTree extends StandardPreOrderTree, Method { - final override AstNode getChildNode(int i) { - result = this.getParameters() and i = 0 + private class MethodTree extends RescueEnsureBlockTree, Method { + final override AstNode getChildNode(int i, boolean rescuable) { + result = this.getParameters() and i = 0 and rescuable = false or - result = this.getChild(i - 1) + result = this.getChild(i - 1) and rescuable = true } override predicate isHidden() { any() } @@ -744,11 +791,11 @@ private module Trees { override predicate isHidden() { any() } } - private class ModuleTree extends StandardPreOrderTree, Module { - final override AstNode getChildNode(int i) { - result = this.getName() and i = 0 + private class ModuleTree extends RescueEnsureBlockTree, Module { + final override AstNode getChildNode(int i, boolean rescuable) { + result = this.getName() and i = 0 and rescuable = false or - result = this.getChild(i - 1) + result = this.getChild(i - 1) and rescuable = true } } @@ -766,8 +813,8 @@ private module Trees { } } - private class OptionalParameterTree extends StandardPostOrderTree, OptionalParameter { - final override AstNode getChildNode(int i) { result = this.getValue() and i = 0 } + private class OptionalParameterTree extends DefaultValueParameterTree, OptionalParameter { + final override AstNode getDefaultValue() { result = this.getValue() } } private class PairTree extends StandardPostOrderTree, Pair { @@ -782,8 +829,10 @@ private module Trees { final override AstNode getChildNode(int i) { result = this.getChild(i) } } - private class PatternTree extends StandardPostOrderTree, Pattern { + private class PatternTree extends StandardPreOrderTree, Pattern { final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + + final override predicate isHidden() { any() } } private class ProgramTree extends StandardPreOrderTree, Program { @@ -809,13 +858,282 @@ private module Trees { final override Interpolation getChildNode(int i) { result = this.getChild(i) } } - private class RescueTree extends StandardPreOrderTree, Rescue { - final override AstNode getChildNode(int i) { - result = this.getExceptions() and i = 0 + private class RescueTree extends PreOrderTree, Rescue { + final override predicate propagatesAbnormal(AstNode child) { child = this.getExceptions() } + + predicate lastMatch(AstNode last, Completion c) { + last(this.getBody(), last, c) or - result = this.getVariable() and i = 1 + not exists(this.getBody()) and + ( + last(this.getVariable(), last, c) + or + not exists(this.getVariable()) and + ( + last(this.getExceptions(), last, c) and + c.(MatchingCompletion).getValue() = true + or + not exists(this.getExceptions()) and + last = this and + c.isValidFor(this) + ) + ) + } + + predicate lastNoMatch(AstNode last, Completion c) { + last(this.getExceptions(), last, c) and + c.(MatchingCompletion).getValue() = false + } + + final override predicate last(AstNode last, Completion c) { + this.lastNoMatch(last, c) or - result = this.getBody() and i = 2 + this.lastMatch(last, c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(AstNode next | + pred = this and + first(next, succ) and + c instanceof SimpleCompletion + | + next = this.getExceptions() + or + not exists(this.getExceptions()) and + ( + next = this.getVariable() + or + not exists(this.getVariable()) and + next = this.getBody() + ) + ) + or + exists(AstNode next | + last(this.getExceptions(), pred, c) and + first(next, succ) and + c.(MatchingCompletion).getValue() = true + | + next = this.getVariable() + or + not exists(this.getVariable()) and + next = this.getBody() + ) + or + last(this.getVariable(), pred, c) and + first(this.getBody(), succ) and + c instanceof NormalCompletion + } + } + + /** Gets a child of `n` that is in CFG scope `scope`. */ + pragma[noinline] + private AstNode getAChildInScope(AstNode n, CfgScope scope) { + result.getParent() = n and + scope = getScope(result) + } + + /** A block that may contain `rescue`/`ensure`. */ + abstract class RescueEnsureBlockTree extends PreOrderTree { + /** + * Gets the `i`th child of this block. `rescuable` indicates whether exceptional + * execution of the child can be caught by `rescue`/`ensure`. + */ + abstract AstNode getChildNode(int i, boolean rescuable); + + /** Gets the `i`th child in the body of this block. */ + final private AstNode getBodyChild(int i, boolean rescuable) { + result = this.getChildNode(_, rescuable) and + result = + rank[i + 1](AstNode child, int j | + child = this.getChildNode(j, _) and + not result instanceof Rescue and + not result instanceof Ensure and + not result instanceof Else and + not child instanceof CfgScope + | + child order by j + ) + } + + /** Gets the `i`th `rescue` block in this block. */ + final Rescue getRescue(int i) { + result = rank[i + 1](Rescue s | s = this.getAFieldOrChild() | s order by s.getParentIndex()) + } + + /** Gets the `else` block in this block, if any. */ + final private Else getElse() { result = unique(Else s | s = this.getAFieldOrChild()) } + + /** Gets the `ensure` block in this block, if any. */ + final Ensure getEnsure() { result = unique(Ensure s | s = this.getAFieldOrChild()) } + + final private predicate hasEnsure() { exists(this.getEnsure()) } + + final override predicate propagatesAbnormal(AstNode child) { + child = this.getEnsure() + or + child = this.getBodyChild(_, false) + } + + /** + * Gets a descendant that belongs to the `ensure` block of this block, if any. + * Nested `ensure` blocks are not included. + */ + AstNode getAnEnsureDescendant() { + result = this.getEnsure() + or + exists(AstNode mid | + mid = this.getAnEnsureDescendant() and + result = getAChildInScope(mid, getScope(mid)) and + not exists(RescueEnsureBlockTree nestedBlock | + result = nestedBlock.getEnsure() and + nestedBlock != this + ) + ) + } + + /** + * Holds if `innerBlock` has an `ensure` block and is immediately nested inside the + * `ensure` block of this block. + */ + private predicate nestedEnsure(RescueEnsureBlockTree innerBlock) { + exists(Ensure innerEnsure | + innerEnsure = getAChildInScope(this.getAnEnsureDescendant(), getScope(this)) and + innerEnsure = innerBlock.getEnsure() + ) + } + + /** + * Gets the `ensure`-nesting level of this block. That is, the number of `ensure` + * blocks that this block is nested under. + */ + int nestLevel() { result = count(RescueEnsureBlockTree outer | outer.nestedEnsure+(this)) } + + /** + * Holds if `last` is a last element in the body of this block. `ensurable` + * indicates whether `last` may be a predecessor of an `ensure` block. + */ + pragma[nomagic] + private predicate lastBody(AstNode last, Completion c, boolean ensurable) { + exists(boolean rescuable | + if c instanceof RaiseCompletion then ensurable = rescuable else ensurable = true + | + last(this.getBodyChild(_, rescuable), last, c) and + not c instanceof NormalCompletion + or + exists(int lst | + last(this.getBodyChild(lst, rescuable), last, c) and + not exists(this.getBodyChild(lst + 1, _)) + ) + ) + } + + /** + * Gets a last element from this block that may finish with completion `c`, such + * that control may be transferred to the `ensure` block (if it exists), but only + * if `ensurable = true`. + */ + pragma[nomagic] + private AstNode getAnEnsurePredecessor(Completion c, boolean ensurable) { + this.lastBody(result, c, ensurable) and + ( + // Any non-throw completion will always continue directly to the `ensure` block, + // unless there is an `else` block + not c instanceof RaiseCompletion and + not exists(this.getElse()) + or + // Any completion will continue to the `ensure` block when there are no `rescue` + // blocks + not exists(this.getRescue(_)) + ) + or + // Last element from any matching `rescue` block continues to the `ensure` block + this.getRescue(_).(RescueTree).lastMatch(result, c) and + ensurable = true + or + // If the last `rescue` block does not match, continue to the `ensure` block + exists(int lst, MatchingCompletion mc | + this.getRescue(lst).(RescueTree).lastNoMatch(result, mc) and + mc.getValue() = false and + not exists(this.getRescue(lst + 1)) and + c = + any(NestedEnsureCompletion nec | + nec.getOuterCompletion() instanceof RaiseCompletion and + nec.getInnerCompletion() = mc and + nec.getNestLevel() = 0 + ) and + ensurable = true + ) + or + // Last element of `else` block continues to the `ensure` block + last(this.getElse(), result, c) and + ensurable = true + } + + pragma[nomagic] + private predicate lastEnsure0(AstNode last, Completion c) { last(this.getEnsure(), last, c) } + + pragma[nomagic] + private predicate lastEnsure( + AstNode last, NormalCompletion ensure, Completion outer, int nestLevel + ) { + this.lastEnsure0(last, ensure) and + exists( + this.getAnEnsurePredecessor(any(Completion c0 | outer = c0.getOuterCompletion()), true) + ) and + nestLevel = this.nestLevel() + } + + override predicate last(AstNode last, Completion c) { + exists(boolean ensurable | last = this.getAnEnsurePredecessor(c, ensurable) | + not this.hasEnsure() + or + ensurable = false + ) + or + // If the body completes normally, take the completion from the `ensure` block + this.lastEnsure(last, c, any(NormalCompletion nc), _) + or + // If the `ensure` block completes normally, it inherits any non-normal + // completion from the body + c = + any(NestedEnsureCompletion nec | + this + .lastEnsure(last, nec.getAnInnerCompatibleCompletion(), nec.getOuterCompletion(), + nec.getNestLevel()) + ) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + pred = this and + first(this.getBodyChild(0, _), succ) and + c instanceof SimpleCompletion + or + // Normal left-to-right evaluation in the body + exists(int i | + last(this.getBodyChild(i, _), pred, c) and + first(this.getBodyChild(i + 1, _), succ) and + c instanceof NormalCompletion + ) + or + // Exceptional flow from body to first `rescue` + this.lastBody(pred, c, true) and + first(this.getRescue(0), succ) and + c instanceof RaiseCompletion + or + // Flow from one `rescue` clause to the next when there is no match + exists(RescueTree rescue, int i | rescue = this.getRescue(i) | + rescue.lastNoMatch(pred, c) and + first(this.getRescue(i + 1), succ) + ) + or + // Flow from body to `else` block when no exception + this.lastBody(pred, c, _) and + first(this.getElse(), succ) and + c instanceof NormalCompletion + or + // Flow into `ensure` block + pred = getAnEnsurePredecessor(c, true) and + first(this.getEnsure(), succ) } } @@ -870,23 +1188,31 @@ private module Trees { private class SetterTree extends LeafTree, Setter { } - private class SingletonClassTree extends StandardPreOrderTree, SingletonClass { - final override AstNode getChildNode(int i) { - result = this.getValue() and i = 0 - or - result = this.getChild(i - 1) + private class SingletonClassTree extends RescueEnsureBlockTree, SingletonClass { + final override AstNode getChildNode(int i, boolean rescuable) { + rescuable = true and + ( + result = this.getValue() and i = 0 + or + result = this.getChild(i - 1) + ) } override predicate isHidden() { any() } } - private class SingletonMethodTree extends StandardPreOrderTree, SingletonMethod { - final override AstNode getChildNode(int i) { - result = this.getObject() and i = 0 + private class SingletonMethodTree extends RescueEnsureBlockTree, SingletonMethod { + final override AstNode getChildNode(int i, boolean rescuable) { + result = this.getObject() and + i = 0 and + rescuable = false or - result = this.getParameters() and i = 1 + result = this.getParameters() and + i = 1 and + rescuable = false or - result = this.getChild(i - 2) + result = this.getChild(i - 2) and + rescuable = true } override predicate isHidden() { any() } @@ -956,7 +1282,7 @@ private module Trees { final override predicate last(AstNode last, Completion c) { last(this.getLastPattern(), last, c) and - c instanceof FalseCompletion + c.(ConditionalCompletion).getValue() = false or last(this.getBody(), last, c) } @@ -966,13 +1292,15 @@ private module Trees { first(this.getPattern(0), succ) and c instanceof SimpleCompletion or - exists(int i, Pattern p | + exists(int i, Pattern p, boolean b | p = this.getPattern(i) and - last(p, pred, c) + last(p, pred, c) and + b = c.(ConditionalCompletion).getValue() | - c instanceof TrueCompletion and first(this.getBody(), succ) + b = true and + first(this.getBody(), succ) or - c instanceof FalseCompletion and + b = false and first(this.getPattern(i + 1), succ) ) } @@ -990,11 +1318,7 @@ private module Trees { not c instanceof BreakCompletion and not c instanceof RedoCompletion or - c = - any(NestedCompletion nc | - last(this.getBodyNode(), last, nc.getInnerCompletion().(BreakCompletion)) and - nc.getOuterCompletion() instanceof SimpleCompletion - ) + last(this.getBodyNode(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) } final override predicate succ(AstNode pred, AstNode succ, Completion c) { @@ -1010,9 +1334,9 @@ private module Trees { first(this.getConditionNode(), succ) and c.continuesLoop() or - last(this.getBodyNode(), pred, any(RedoCompletion rc)) and + last(this.getBodyNode(), pred, c) and first(this.getBodyNode(), succ) and - c instanceof SimpleCompletion + c instanceof RedoCompletion } } } @@ -1048,8 +1372,9 @@ private module Cached { cached newtype TSuccessorType = TSuccessorSuccessor() or - TBooleanSuccessor(boolean b) { b = true or b = false } or - TEmptinessSuccessor(boolean isEmpty) { isEmpty = true or isEmpty = false } or + TBooleanSuccessor(boolean b) { b in [false, true] } or + TEmptinessSuccessor(boolean isEmpty) { isEmpty in [false, true] } or + TMatchingSuccessor(boolean isMatch) { isMatch in [false, true] } or TReturnSuccessor() or TBreakSuccessor() or TNextSuccessor() or diff --git a/ql/src/codeql_ruby/controlflow/internal/NonReturning.qll b/ql/src/codeql_ruby/controlflow/internal/NonReturning.qll index 29fcfb7fd6a..acba5a0ed26 100644 --- a/ql/src/codeql_ruby/controlflow/internal/NonReturning.qll +++ b/ql/src/codeql_ruby/controlflow/internal/NonReturning.qll @@ -12,11 +12,11 @@ abstract class NonReturningCall extends AstNode { private class RaiseCall extends NonReturningCall, MethodCall { RaiseCall() { this.getMethod().toString() = "raise" } - override RaiseCompletion getACompletion() { any() } + override RaiseCompletion getACompletion() { not result instanceof NestedCompletion } } private class ExitCall extends NonReturningCall, MethodCall { ExitCall() { this.getMethod().toString() in ["abort", "exit"] } - override ExitCompletion getACompletion() { any() } + override ExitCompletion getACompletion() { not result instanceof NestedCompletion } } diff --git a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll index 6f8498b3f24..0e525beef9f 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll @@ -15,10 +15,16 @@ private int maxSplits() { result = 5 } cached private module Cached { cached - newtype TSplitKind = TConditionalCompletionSplitKind() + newtype TSplitKind = + TConditionalCompletionSplitKind() or + TEnsureSplitKind(int nestLevel) { nestLevel = any(Trees::RescueEnsureBlockTree t).nestLevel() } cached - newtype TSplit = TConditionalCompletionSplit(BooleanCompletion c) + newtype TSplit = + TConditionalCompletionSplit(ConditionalCompletion c) or + TEnsureSplit(EnsureSplitting::EnsureSplitType type, int nestLevel) { + nestLevel = any(Trees::RescueEnsureBlockTree t).nestLevel() + } cached newtype TSplits = @@ -60,13 +66,6 @@ class Split extends TSplit { string toString() { none() } } -private AstNode parent(AstNode n) { - result.getAFieldOrChild() = n and - not n instanceof CfgScope -} - -private CfgScope getScope(AstNode n) { result = parent+(n) } - /** * Holds if split kinds `sk1` and `sk2` may overlap. That is, they may apply * to at least one common AST node inside `scope`. @@ -195,7 +194,7 @@ private module ConditionalCompletionSplitting { * restrict the edges out of `x < 2 and x > 0` accordingly. */ class ConditionalCompletionSplit extends Split, TConditionalCompletionSplit { - BooleanCompletion completion; + ConditionalCompletion completion; ConditionalCompletionSplit() { this = TConditionalCompletionSplit(completion) } @@ -238,19 +237,258 @@ private module ConditionalCompletionSplitting { override predicate hasExit(AstNode pred, AstNode succ, Completion c) { this.appliesTo(pred) and succ(pred, succ, c) and - if c instanceof BooleanCompletion then completion = c else any() + if c instanceof ConditionalCompletion then completion = c else any() } override predicate hasExitScope(AstNode last, CfgScope scope, Completion c) { this.appliesTo(last) and succExit(last, scope, c) and - if c instanceof BooleanCompletion then completion = c else any() + if c instanceof ConditionalCompletion then completion = c else any() } override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { none() } } } +module EnsureSplitting { + /** + * The type of a split `ensure` node. + * + * The type represents one of the possible ways of entering an `ensure` + * block. For example, if a block ends with a `return` statement, then + * the `ensure` block must end with a `return` as well (provided that + * the `ensure` block executes normally). + */ + class EnsureSplitType extends SuccessorType { + EnsureSplitType() { not this instanceof ConditionalSuccessor } + + /** Holds if this split type matches entry into an `ensure` block with completion `c`. */ + predicate isSplitForEntryCompletion(Completion c) { + if c instanceof NormalCompletion + then + // If the entry into the `ensure` block completes with any normal completion, + // it simply means normal execution after the `ensure` block + this instanceof NormalSuccessor + else this = c.getAMatchingSuccessorType() + } + } + + /** A node that belongs to an `ensure` block. */ + private class EnsureNode extends AstNode { + private Trees::RescueEnsureBlockTree block; + + EnsureNode() { this = block.getAnEnsureDescendant() } + + /** Gets the immediate block that this node belongs to. */ + Trees::RescueEnsureBlockTree getBlock() { result = block } + + /** Holds if this node is the entry node in the `ensure` block it belongs to. */ + predicate isEntryNode() { first(block.getEnsure(), this) } + } + + /** A node that does not belong to an `ensure` block. */ + private class NonEnsureNode extends EnsureNode { + NonEnsureNode() { not this = any(Trees::RescueEnsureBlockTree t).getAnEnsureDescendant() } + } + + /** + * A split for nodes belonging to an `ensure` block, which determines how to + * continue execution after leaving the `ensure` block. For example, in + * + * ```rb + * begin + * if x + * raise "Exception" + * end + * ensure + * puts "Ensure" + * end + * ``` + * + * all control flow nodes in the `ensure` block have two splits: one representing + * normal execution of the body (when `x` evaluates to `true`), and one representing + * exceptional execution of the body (when `x` evaluates to `false`). + */ + class EnsureSplit extends Split, TEnsureSplit { + private EnsureSplitType type; + private int nestLevel; + + EnsureSplit() { this = TEnsureSplit(type, nestLevel) } + + /** + * Gets the type of this `ensure` split, that is, how to continue execution after the + * `ensure` block. + */ + EnsureSplitType getType() { result = type } + + /** Gets the nesting level. */ + int getNestLevel() { result = nestLevel } + + override string toString() { + if type instanceof NormalSuccessor + then result = "" + else + if nestLevel > 0 + then result = "ensure(" + nestLevel + "): " + type.toString() + else result = "ensure: " + type.toString() + } + } + + private int getListOrder(EnsureSplitKind kind) { + result = ConditionalCompletionSplitting::getNextListOrder() + kind.getNestLevel() + } + + int getNextListOrder() { + result = max([getListOrder(_) + 1, ConditionalCompletionSplitting::getNextListOrder()]) + } + + private class EnsureSplitKind extends SplitKind, TEnsureSplitKind { + private int nestLevel; + + EnsureSplitKind() { this = TEnsureSplitKind(nestLevel) } + + /** Gets the nesting level. */ + int getNestLevel() { result = nestLevel } + + override int getListOrder() { result = getListOrder(this) } + + override string toString() { result = "ensure (" + nestLevel + ")" } + } + + pragma[noinline] + private predicate hasEntry0(AstNode pred, EnsureNode succ, int nestLevel, Completion c) { + succ.isEntryNode() and + nestLevel = succ.getBlock().nestLevel() and + succ(pred, succ, c) + } + + private class EnsureSplitImpl extends SplitImpl, EnsureSplit { + override EnsureSplitKind getKind() { result.getNestLevel() = this.getNestLevel() } + + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { + hasEntry0(pred, succ, this.getNestLevel(), c) and + this.getType().isSplitForEntryCompletion(c) + } + + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } + + /** + * Holds if this split applies to `pred`, where `pred` is a valid predecessor. + */ + private predicate appliesToPredecessor(AstNode pred) { + this.appliesTo(pred) and + (succ(pred, _, _) or succExit(pred, _, _)) + } + + pragma[noinline] + private predicate exit0( + AstNode pred, Trees::RescueEnsureBlockTree block, int nestLevel, Completion c + ) { + this.appliesToPredecessor(pred) and + nestLevel = block.nestLevel() and + last(block, pred, c) + } + + /** + * Holds if `pred` may exit this split with completion `c`. The Boolean + * `inherited` indicates whether `c` is an inherited completion from the + * body. + */ + private predicate exit( + Trees::RescueEnsureBlockTree block, AstNode pred, Completion c, boolean inherited + ) { + exists(EnsureSplitType type | + exit0(pred, block, this.getNestLevel(), c) and + type = this.getType() + | + if last(block.getEnsure(), pred, c) + then + // `ensure` block can itself exit with completion `c`: either `c` must + // match this split, `c` must be an abnormal completion, or this split + // does not require another completion to be recovered + inherited = false and + ( + type = c.getAMatchingSuccessorType() + or + not c instanceof NormalCompletion + or + type instanceof NormalSuccessor + ) + else ( + // `ensure` block can exit with inherited completion `c`, which must + // match this split + inherited = true and + type = c.getAMatchingSuccessorType() and + not type instanceof NormalSuccessor + ) + ) + or + // If this split is normal, and an outer split can exit based on an inherited + // completion, we need to exit this split as well. For example, in + // + // ```rb + // def m(b1, b2) + // if b1 + // return + // end + // ensure + // begin + // if b2 + // raise "Exception" + // end + // ensure + // puts "inner ensure" + // end + // end + // ``` + // + // if the outer split for `puts "inner ensure"` is `return` and the inner split + // is "normal" (corresponding to `b1 = true` and `b2 = false`), then the inner + // split must be able to exit with a `return` completion. + this.appliesToPredecessor(pred) and + exists(EnsureSplitImpl outer | + outer.getNestLevel() = this.getNestLevel() - 1 and + outer.exit(_, pred, c, inherited) and + this.getType() instanceof NormalSuccessor and + inherited = true + ) + } + + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { + succ(pred, succ, c) and + ( + exit(_, pred, c, _) + or + exit(_, pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) + ) + } + + override predicate hasExitScope(AstNode last, CfgScope scope, Completion c) { + succExit(last, scope, c) and + ( + exit(_, last, c, _) + or + exit(_, last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion(), _) + ) + } + + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { + this.appliesToPredecessor(pred) and + succ(pred, succ, c) and + succ = + any(EnsureNode en | + if en.isEntryNode() + then + // entering a nested `ensure` block + en.getBlock().nestLevel() > this.getNestLevel() + else + // staying in the same (possibly nested) `ensure` block as `pred` + en.getBlock().nestLevel() >= this.getNestLevel() + ) + } + } +} + /** * A set of control flow node splits. The set is represented by a list of splits, * ordered by ascending rank. diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index cfb7974c1e6..5426d01dd9a 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1,1423 +1,3394 @@ -nodes -| case.rb:1:1:6:3 | enter if_in_case | -| case.rb:1:1:6:3 | exit if_in_case | -| case.rb:1:1:6:3 | exit if_in_case (normal) | -| case.rb:2:3:5:5 | Case | -| case.rb:2:8:2:9 | x1 | -| case.rb:3:5:3:42 | When | -| case.rb:3:10:3:10 | 1 | -| case.rb:3:10:3:10 | Pattern | -| case.rb:3:17:3:42 | ParenthesizedStatements | -| case.rb:3:18:3:41 | If | -| case.rb:3:21:3:22 | x2 | -| case.rb:3:29:3:32 | puts | -| case.rb:3:29:3:37 | MethodCall | -| case.rb:3:34:3:37 | String | -| case.rb:4:5:4:24 | When | -| case.rb:4:10:4:10 | 2 | -| case.rb:4:10:4:10 | Pattern | -| case.rb:4:17:4:20 | puts | -| case.rb:4:17:4:24 | MethodCall | -| case.rb:4:22:4:24 | String | -| cfg.rb:1:1:194:1 | enter top-level | -| cfg.rb:1:1:194:1 | exit top-level | -| cfg.rb:1:1:194:1 | exit top-level (normal) | -| cfg.rb:3:1:3:13 | Alias | -| cfg.rb:3:7:3:9 | foo | -| cfg.rb:3:11:3:13 | bar | -| cfg.rb:5:1:5:1 | b | -| cfg.rb:5:1:5:6 | Assignment | -| cfg.rb:5:5:5:6 | 42 | -| cfg.rb:7:1:7:21 | SymbolArray | -| cfg.rb:7:4:7:12 | BareSymbol | -| cfg.rb:7:7:7:12 | Interpolation | -| cfg.rb:7:10:7:10 | b | -| cfg.rb:7:14:7:20 | BareSymbol | -| cfg.rb:9:1:9:21 | StringArray | -| cfg.rb:9:4:9:12 | BareString | -| cfg.rb:9:7:9:12 | Interpolation | -| cfg.rb:9:10:9:10 | b | -| cfg.rb:9:14:9:20 | BareString | -| cfg.rb:12:3:12:6 | puts | -| cfg.rb:12:3:12:8 | MethodCall | -| cfg.rb:12:8:12:8 | 4 | -| cfg.rb:15:1:17:1 | BeginBlock | -| cfg.rb:15:1:17:1 | enter BEGIN block | -| cfg.rb:15:1:17:1 | exit BEGIN block | -| cfg.rb:15:1:17:1 | exit BEGIN block (normal) | -| cfg.rb:16:3:16:6 | puts | -| cfg.rb:16:3:16:14 | MethodCall | -| cfg.rb:16:8:16:14 | String | -| cfg.rb:19:1:21:1 | EndBlock | -| cfg.rb:19:1:21:1 | enter END block | -| cfg.rb:19:1:21:1 | exit END block | -| cfg.rb:19:1:21:1 | exit END block (normal) | -| cfg.rb:20:3:20:6 | puts | -| cfg.rb:20:3:20:14 | MethodCall | -| cfg.rb:20:8:20:14 | String | -| cfg.rb:23:1:23:2 | 41 | -| cfg.rb:23:1:23:6 | Binary | -| cfg.rb:23:6:23:6 | 1 | -| cfg.rb:25:1:25:1 | 2 | -| cfg.rb:25:1:25:7 | Call | -| cfg.rb:25:1:25:22 | MethodCall | -| cfg.rb:25:3:25:7 | times | -| cfg.rb:25:9:25:22 | enter block | -| cfg.rb:25:9:25:22 | exit block | -| cfg.rb:25:9:25:22 | exit block (normal) | -| cfg.rb:25:12:25:12 | x | -| cfg.rb:25:15:25:18 | puts | -| cfg.rb:25:15:25:20 | MethodCall | -| cfg.rb:25:20:25:20 | x | -| cfg.rb:27:1:27:4 | puts | -| cfg.rb:27:1:27:11 | MethodCall | -| cfg.rb:27:6:27:11 | BlockArgument | -| cfg.rb:27:7:27:11 | Symbol | -| cfg.rb:29:1:29:4 | Proc | -| cfg.rb:29:1:29:8 | Call | -| cfg.rb:29:1:29:24 | MethodCall | -| cfg.rb:29:6:29:8 | new | -| cfg.rb:29:10:29:24 | enter block | -| cfg.rb:29:10:29:24 | exit block | -| cfg.rb:29:10:29:24 | exit block (normal) | -| cfg.rb:29:13:29:14 | BlockParameter | -| cfg.rb:29:17:29:17 | x | -| cfg.rb:29:17:29:22 | Call | -| cfg.rb:29:19:29:22 | call | -| cfg.rb:31:1:33:3 | While | -| cfg.rb:31:7:31:10 | true | -| cfg.rb:32:3:32:9 | Break | -| cfg.rb:32:9:32:9 | 1 | -| cfg.rb:35:1:37:3 | If | -| cfg.rb:35:4:35:8 | false | -| cfg.rb:39:1:39:4 | self | -| cfg.rb:39:1:39:9 | Call | -| cfg.rb:39:1:39:12 | MethodCall | -| cfg.rb:39:6:39:9 | puts | -| cfg.rb:39:11:39:12 | 42 | -| cfg.rb:41:1:45:3 | Case | -| cfg.rb:41:6:41:7 | 10 | -| cfg.rb:42:3:42:24 | When | -| cfg.rb:42:8:42:8 | 1 | -| cfg.rb:42:8:42:8 | Pattern | -| cfg.rb:42:15:42:18 | puts | -| cfg.rb:42:15:42:24 | MethodCall | -| cfg.rb:42:20:42:24 | String | -| cfg.rb:43:3:43:31 | When | -| cfg.rb:43:8:43:8 | 2 | -| cfg.rb:43:8:43:8 | Pattern | -| cfg.rb:43:11:43:11 | 3 | -| cfg.rb:43:11:43:11 | Pattern | -| cfg.rb:43:14:43:14 | 4 | -| cfg.rb:43:14:43:14 | Pattern | -| cfg.rb:43:21:43:24 | puts | -| cfg.rb:43:21:43:31 | MethodCall | -| cfg.rb:43:26:43:31 | String | -| cfg.rb:44:8:44:11 | puts | -| cfg.rb:44:8:44:18 | MethodCall | -| cfg.rb:44:13:44:18 | String | -| cfg.rb:47:1:50:3 | Case | -| cfg.rb:48:3:48:29 | When | -| cfg.rb:48:8:48:8 | b | -| cfg.rb:48:8:48:13 | Binary | -| cfg.rb:48:8:48:13 | Pattern | -| cfg.rb:48:13:48:13 | 1 | -| cfg.rb:48:20:48:23 | puts | -| cfg.rb:48:20:48:29 | MethodCall | -| cfg.rb:48:25:48:29 | String | -| cfg.rb:49:3:49:37 | When | -| cfg.rb:49:8:49:8 | b | -| cfg.rb:49:8:49:13 | Binary | -| cfg.rb:49:8:49:13 | Pattern | -| cfg.rb:49:13:49:13 | 0 | -| cfg.rb:49:16:49:16 | b | -| cfg.rb:49:16:49:20 | Binary | -| cfg.rb:49:16:49:20 | Pattern | -| cfg.rb:49:20:49:20 | 1 | -| cfg.rb:49:27:49:30 | puts | -| cfg.rb:49:27:49:37 | MethodCall | -| cfg.rb:49:32:49:37 | String | -| cfg.rb:52:1:52:7 | chained | -| cfg.rb:52:1:52:35 | Assignment | -| cfg.rb:52:11:52:13 | String | -| cfg.rb:52:15:52:26 | String | -| cfg.rb:52:16:52:25 | Interpolation | -| cfg.rb:52:18:52:24 | chained | -| cfg.rb:52:28:52:35 | String | -| cfg.rb:54:1:54:9 | character | -| cfg.rb:54:1:54:17 | Assignment | -| cfg.rb:54:13:54:17 | ?\\x40 | -| cfg.rb:58:1:72:3 | Class | -| cfg.rb:58:7:58:11 | Silly | -| cfg.rb:58:13:58:20 | Superclass | -| cfg.rb:58:15:58:20 | Object | -| cfg.rb:59:3:59:9 | complex | -| cfg.rb:59:3:59:17 | Assignment | -| cfg.rb:59:13:59:17 | 10-2i | -| cfg.rb:60:3:60:13 | conditional | -| cfg.rb:60:3:60:40 | Assignment | -| cfg.rb:60:17:60:17 | b | -| cfg.rb:60:17:60:22 | Binary | -| cfg.rb:60:17:60:40 | Conditional | -| cfg.rb:60:21:60:22 | 10 | -| cfg.rb:60:26:60:32 | String | -| cfg.rb:60:36:60:40 | String | -| cfg.rb:61:3:61:3 | C | -| cfg.rb:61:3:61:16 | Assignment | -| cfg.rb:61:7:61:16 | String | -| cfg.rb:62:3:62:13 | DestructuredLeftAssignment | -| cfg.rb:62:3:62:27 | Assignment | -| cfg.rb:62:4:62:4 | x | -| cfg.rb:62:7:62:12 | DestructuredLeftAssignment | -| cfg.rb:62:8:62:8 | y | -| cfg.rb:62:11:62:11 | z | -| cfg.rb:62:17:62:27 | Array | -| cfg.rb:62:18:62:18 | 1 | -| cfg.rb:62:21:62:26 | Array | -| cfg.rb:62:22:62:22 | 2 | -| cfg.rb:62:25:62:25 | 3 | -| cfg.rb:63:3:66:5 | enter pattern | -| cfg.rb:63:3:66:5 | exit pattern | -| cfg.rb:63:3:66:5 | exit pattern (normal) | -| cfg.rb:63:16:63:20 | DestructuredParameter | -| cfg.rb:63:17:63:17 | a | -| cfg.rb:63:19:63:19 | b | -| cfg.rb:64:5:64:8 | puts | -| cfg.rb:64:5:64:10 | MethodCall | -| cfg.rb:64:10:64:10 | a | -| cfg.rb:65:5:65:8 | puts | -| cfg.rb:65:5:65:10 | MethodCall | -| cfg.rb:65:10:65:10 | b | -| cfg.rb:67:3:67:7 | items | -| cfg.rb:67:3:67:19 | Assignment | -| cfg.rb:67:11:67:19 | Array | -| cfg.rb:67:12:67:12 | 1 | -| cfg.rb:67:15:67:15 | 2 | -| cfg.rb:67:18:67:18 | 3 | -| cfg.rb:68:3:68:6 | puts | -| cfg.rb:68:3:68:15 | MethodCall | -| cfg.rb:68:8:68:12 | items | -| cfg.rb:68:8:68:15 | ElementReference | -| cfg.rb:68:14:68:14 | 2 | -| cfg.rb:69:3:71:5 | enter print | -| cfg.rb:69:3:71:5 | exit print | -| cfg.rb:69:3:71:5 | exit print (normal) | -| cfg.rb:70:5:70:8 | puts | -| cfg.rb:70:5:70:16 | MethodCall | -| cfg.rb:70:10:70:16 | String | -| cfg.rb:74:1:74:1 | x | -| cfg.rb:74:1:74:6 | Assignment | -| cfg.rb:74:5:74:6 | 42 | -| cfg.rb:75:1:75:47 | If | -| cfg.rb:75:4:75:4 | x | -| cfg.rb:75:4:75:8 | Binary | -| cfg.rb:75:8:75:8 | 0 | -| cfg.rb:75:15:75:15 | 0 | -| cfg.rb:75:17:75:43 | Elsif | -| cfg.rb:75:23:75:23 | x | -| cfg.rb:75:23:75:28 | Binary | -| cfg.rb:75:27:75:28 | 10 | -| cfg.rb:75:35:75:36 | 10 | -| cfg.rb:75:43:75:43 | x | -| cfg.rb:78:3:78:3 | ; | -| cfg.rb:88:1:88:6 | escape | -| cfg.rb:88:1:88:23 | Assignment | -| cfg.rb:88:10:88:23 | String | -| cfg.rb:88:17:88:20 | Interpolation | -| cfg.rb:88:19:88:19 | x | -| cfg.rb:90:1:93:3 | For | -| cfg.rb:90:5:90:5 | x | -| cfg.rb:90:10:90:26 | Array | -| cfg.rb:90:11:90:13 | 1.4 | -| cfg.rb:90:16:90:18 | 2.5 | -| cfg.rb:90:21:90:25 | 3.4e5 | -| cfg.rb:91:3:91:24 | If | -| cfg.rb:91:6:91:6 | x | -| cfg.rb:91:6:91:10 | Binary | -| cfg.rb:91:10:91:10 | 3 | -| cfg.rb:91:17:91:20 | Next | -| cfg.rb:92:3:92:6 | puts | -| cfg.rb:92:3:92:8 | MethodCall | -| cfg.rb:92:8:92:8 | x | -| cfg.rb:95:1:95:7 | $global | -| cfg.rb:95:1:95:12 | Assignment | -| cfg.rb:95:11:95:12 | 42 | -| cfg.rb:97:1:97:4 | map1 | -| cfg.rb:97:1:97:32 | Assignment | -| cfg.rb:97:8:97:32 | Hash | -| cfg.rb:97:10:97:12 | String | -| cfg.rb:97:10:97:19 | Pair | -| cfg.rb:97:17:97:19 | String | -| cfg.rb:97:22:97:24 | String | -| cfg.rb:97:22:97:29 | Pair | -| cfg.rb:97:27:97:29 | String | -| cfg.rb:98:1:98:4 | map2 | -| cfg.rb:98:1:98:36 | Assignment | -| cfg.rb:98:8:98:36 | Hash | -| cfg.rb:98:10:98:15 | HashSplatArgument | -| cfg.rb:98:12:98:15 | map1 | -| cfg.rb:98:18:98:20 | String | -| cfg.rb:98:18:98:27 | Pair | -| cfg.rb:98:25:98:27 | String | -| cfg.rb:98:30:98:35 | HashSplatArgument | -| cfg.rb:98:32:98:35 | map1 | -| cfg.rb:101:1:104:3 | enter parameters | -| cfg.rb:101:1:104:3 | exit parameters | -| cfg.rb:101:1:104:3 | exit parameters (normal) | -| cfg.rb:101:16:101:25 | OptionalParameter | -| cfg.rb:101:24:101:25 | 42 | -| cfg.rb:101:28:101:31 | KeywordParameter | -| cfg.rb:101:34:101:41 | HashSplatParameter | -| cfg.rb:102:3:102:6 | puts | -| cfg.rb:102:3:102:12 | MethodCall | -| cfg.rb:102:8:102:12 | value | -| cfg.rb:103:3:103:20 | Return | -| cfg.rb:103:10:103:15 | kwargs | -| cfg.rb:103:10:103:20 | ElementReference | -| cfg.rb:103:17:103:19 | key | -| cfg.rb:106:1:106:4 | type | -| cfg.rb:106:1:106:17 | Assignment | -| cfg.rb:106:9:106:17 | String | -| cfg.rb:107:1:107:5 | table | -| cfg.rb:107:1:107:14 | Assignment | -| cfg.rb:107:9:107:14 | String | -| cfg.rb:108:1:108:4 | puts | -| cfg.rb:108:1:108:12 | MethodCall | -| cfg.rb:108:6:108:12 | ParenthesizedStatements | -| cfg.rb:108:7:108:11 | < elements + +# 13| enter m2 +#-----| -> elements + +# 27| enter m3 +#-----| -> elements + +case.rb: +# 1| enter if_in_case +#-----| -> Case + +cfg.rb: +# 1| enter top-level +#-----| -> Alias + +# 15| enter BEGIN block +#-----| -> BeginBlock + +# 19| enter END block +#-----| -> EndBlock + +# 25| enter block +#-----| -> x + +# 29| enter block +#-----| -> BlockParameter + +# 63| enter pattern +#-----| -> a + +# 69| enter print +#-----| -> String + +# 101| enter parameters +#-----| -> OptionalParameter + +# 120| enter lambda +#-----| -> x + +# 120| enter block +#-----| -> y + +# 142| enter print +#-----| -> String + +# 149| enter method +#-----| -> silly + +# 153| enter two_parameters +#-----| -> a + +# 185| enter run_block +#-----| -> Yield + +# 189| enter block +#-----| -> x + +exit.rb: +# 1| enter m1 +#-----| -> x + +# 8| enter m2 +#-----| -> x + +ifs.rb: +# 1| enter m1 +#-----| -> x + +# 11| enter m2 +#-----| -> b + +# 18| enter m3 +#-----| -> x + +# 28| enter m4 +#-----| -> b1 + +loops.rb: +# 1| enter m1 +#-----| -> x + +# 8| enter m2 +#-----| -> x + +# 24| enter m3 +#-----| -> 1 + +# 25| enter do block +#-----| -> x + +raise.rb: +# 1| enter top-level +#-----| -> Class + +# 7| enter m1 +#-----| -> x + +# 14| enter m2 +#-----| -> b + +# 25| enter m3 +#-----| -> b + +# 36| enter m4 +#-----| -> b + +# 47| enter m5 +#-----| -> b + +# 57| enter m6 +#-----| -> b + +# 68| enter m7 +#-----| -> x + +# 79| enter m8 +#-----| -> x + +# 94| enter m9 +#-----| -> x + +# 121| enter m10 +#-----| -> OptionalParameter + +# 128| enter m11 +#-----| -> b + +break_ensure.rb: +# 1| elements +#-----| -> elements + +# 2| For +#-----| non-empty -> element +#-----| empty -> Ensure + +# 2| element +#-----| -> If + +# 2| elements +#-----| -> For + +# 3| If +#-----| -> x + +# 3| Binary +#-----| false -> For +#-----| true -> Break + +# 3| x +#-----| -> 0 + +# 3| 0 +#-----| -> Binary + +# 4| Break +#-----| break -> Ensure + +# 7| Ensure +#-----| -> If + +# 8| If +#-----| -> elements + +# 8| Call +#-----| true -> String +#-----| false -> exit m1 (normal) + +# 8| elements +#-----| -> nil? + +# 8| nil? +#-----| -> Call + +# 9| MethodCall +#-----| -> exit m1 (normal) + +# 9| puts +#-----| -> MethodCall + +# 9| String +#-----| -> puts + +# 13| elements +#-----| -> elements + +# 14| For +#-----| non-empty -> element +#-----| empty -> exit m2 (normal) + +# 14| element +#-----| -> If + +# 14| elements +#-----| -> For + +# 16| If +#-----| -> x + +# 16| Binary +#-----| true -> Break +#-----| false -> Ensure + +# 16| x +#-----| -> 0 + +# 16| 0 +#-----| -> Binary + +# 17| Break +#-----| break -> [ensure: break] Ensure + +# 19| Ensure +#-----| -> If + +# 19| [ensure: break] Ensure +#-----| -> [ensure: break] If + +# 20| If +#-----| -> elements + +# 20| [ensure: break] If +#-----| -> [ensure: break] elements + +# 20| Call +#-----| false -> For +#-----| true -> String + +# 20| [ensure: break] Call +#-----| true -> [ensure: break] String +#-----| false -> exit m2 (normal) + +# 20| elements +#-----| -> nil? + +# 20| [ensure: break] elements +#-----| -> [ensure: break] nil? + +# 20| nil? +#-----| -> Call + +# 20| [ensure: break] nil? +#-----| -> [ensure: break] Call + +# 21| MethodCall +#-----| -> For + +# 21| [ensure: break] MethodCall +#-----| break -> exit m2 (normal) + +# 21| puts +#-----| -> MethodCall + +# 21| [ensure: break] puts +#-----| -> [ensure: break] MethodCall + +# 21| String +#-----| -> puts + +# 21| [ensure: break] String +#-----| -> [ensure: break] puts + +# 27| elements +#-----| -> If + +# 29| If +#-----| -> elements + +# 29| Call +#-----| true -> Return +#-----| false -> Ensure + +# 29| elements +#-----| -> nil? + +# 29| nil? +#-----| -> Call + +# 30| Return +#-----| return -> [ensure: return] Ensure + +# 32| Ensure +#-----| -> elements + +# 32| [ensure: return] Ensure +#-----| -> [ensure: return] elements + +# 33| For +#-----| non-empty -> element +#-----| empty -> String + +# 33| [ensure: return] For +#-----| non-empty -> [ensure: return] element +#-----| return -> exit m3 (normal) + +# 33| element +#-----| -> If + +# 33| [ensure: return] element +#-----| -> [ensure: return] If + +# 33| elements +#-----| -> For + +# 33| [ensure: return] elements +#-----| -> [ensure: return] For + +# 35| If +#-----| -> x + +# 35| [ensure: return] If +#-----| -> [ensure: return] x + +# 35| Binary +#-----| false -> For +#-----| true -> Break + +# 35| [ensure: return] Binary +#-----| false -> [ensure: return] For +#-----| true -> [ensure: return] Break + +# 35| x +#-----| -> 0 + +# 35| [ensure: return] x +#-----| -> [ensure: return] 0 + +# 35| 0 +#-----| -> Binary + +# 35| [ensure: return] 0 +#-----| -> [ensure: return] Binary + +# 36| Break +#-----| break -> String + +# 36| [ensure: return] Break +#-----| return -> exit m3 (normal) + +# 41| MethodCall +#-----| -> exit m3 (normal) + +# 41| puts +#-----| -> MethodCall + +# 41| String +#-----| -> puts + +case.rb: +# 2| Case +#-----| -> x1 + +# 2| x1 +#-----| -> When + +# 3| When +#-----| -> 1 + +# 3| 1 +#-----| match -> If +#-----| no-match -> When + +# 3| ParenthesizedStatements +#-----| -> exit if_in_case (normal) + +# 3| If +#-----| -> x2 + +# 3| x2 +#-----| false -> ParenthesizedStatements +#-----| true -> String + +# 3| MethodCall +#-----| -> ParenthesizedStatements + +# 3| puts +#-----| -> MethodCall + +# 3| String +#-----| -> puts + +# 4| When +#-----| -> 2 + +# 4| 2 +#-----| match -> String +#-----| no-match -> exit if_in_case (normal) + +# 4| MethodCall +#-----| -> exit if_in_case (normal) + +# 4| puts +#-----| -> MethodCall + +# 4| String +#-----| -> puts + +cfg.rb: +# 3| Alias +#-----| -> foo + +# 3| foo +#-----| -> bar + +# 3| bar +#-----| -> 42 + +# 5| Assignment +#-----| -> b + +# 5| b +#-----| -> Assignment + +# 5| 42 +#-----| -> b + +# 7| SymbolArray +#-----| -> b + +# 7| BareSymbol +#-----| -> BareSymbol + +# 7| Interpolation +#-----| -> BareSymbol + +# 7| b +#-----| -> Interpolation + +# 7| BareSymbol +#-----| -> SymbolArray + +# 9| StringArray +#-----| -> 4 + +# 9| BareString +#-----| -> BareString + +# 9| Interpolation +#-----| -> BareString + +# 9| b +#-----| -> Interpolation + +# 9| BareString +#-----| -> StringArray + +# 12| MethodCall +#-----| -> 41 + +# 12| puts +#-----| -> MethodCall + +# 12| 4 +#-----| -> puts + +# 15| BeginBlock +#-----| -> String + +# 16| MethodCall +#-----| -> exit BEGIN block (normal) + +# 16| puts +#-----| -> MethodCall + +# 16| String +#-----| -> puts + +# 19| EndBlock +#-----| -> String + +# 20| MethodCall +#-----| -> exit END block (normal) + +# 20| puts +#-----| -> MethodCall + +# 20| String +#-----| -> puts + +# 23| Binary +#-----| -> 2 + +# 23| 41 +#-----| -> 1 + +# 23| 1 +#-----| -> Binary + +# 25| MethodCall +#-----| -> Symbol + +# 25| Call +#-----| -> MethodCall + +# 25| 2 +#-----| -> times + +# 25| times +#-----| -> Call + +# 25| x +#-----| -> x + +# 25| MethodCall +#-----| -> exit block (normal) + +# 25| puts +#-----| -> MethodCall + +# 25| x +#-----| -> puts + +# 27| MethodCall +#-----| -> Proc + +# 27| puts +#-----| -> MethodCall + +# 27| BlockArgument +#-----| -> puts + +# 27| Symbol +#-----| -> BlockArgument + +# 29| MethodCall +#-----| -> While + +# 29| Call +#-----| -> MethodCall + +# 29| Proc +#-----| -> new + +# 29| new +#-----| -> Call + +# 29| BlockParameter +#-----| -> x + +# 29| Call +#-----| -> exit block (normal) + +# 29| x +#-----| -> call + +# 29| call +#-----| -> Call + +# 31| While +#-----| -> true + +# 31| true +#-----| true -> 1 + +# 32| Break +#-----| break -> If + +# 32| 1 +#-----| -> Break + +# 35| If +#-----| -> false + +# 35| false +#-----| false -> 42 + +# 39| MethodCall +#-----| -> Case + +# 39| Call +#-----| -> MethodCall + +# 39| self +#-----| -> puts + +# 39| puts +#-----| -> Call + +# 39| 42 +#-----| -> self + +# 41| Case +#-----| -> 10 + +# 41| 10 +#-----| -> When + +# 42| When +#-----| -> 1 + +# 42| 1 +#-----| match -> String +#-----| no-match -> When + +# 42| MethodCall +#-----| -> Case + +# 42| puts +#-----| -> MethodCall + +# 42| String +#-----| -> puts + +# 43| When +#-----| -> 2 + +# 43| 2 +#-----| no-match -> 3 +#-----| match -> String + +# 43| 3 +#-----| no-match -> 4 +#-----| match -> String + +# 43| 4 +#-----| match -> String +#-----| no-match -> String + +# 43| MethodCall +#-----| -> Case + +# 43| puts +#-----| -> MethodCall + +# 43| String +#-----| -> puts + +# 44| MethodCall +#-----| -> Case + +# 44| puts +#-----| -> MethodCall + +# 44| String +#-----| -> puts + +# 47| Case +#-----| -> When + +# 48| When +#-----| -> b + +# 48| Binary +#-----| true -> String +#-----| false -> When + +# 48| b +#-----| -> 1 + +# 48| 1 +#-----| -> Binary + +# 48| MethodCall +#-----| -> String + +# 48| puts +#-----| -> MethodCall + +# 48| String +#-----| -> puts + +# 49| When +#-----| -> b + +# 49| Binary +#-----| false -> b +#-----| true -> String + +# 49| b +#-----| -> 0 + +# 49| 0 +#-----| -> Binary + +# 49| Binary +#-----| true -> String +#-----| false -> String + +# 49| b +#-----| -> 1 + +# 49| 1 +#-----| -> Binary + +# 49| MethodCall +#-----| -> String + +# 49| puts +#-----| -> MethodCall + +# 49| String +#-----| -> puts + +# 52| Assignment +#-----| -> ?\x40 + +# 52| chained +#-----| -> Assignment + +# 52| String +#-----| -> chained + +# 52| String +#-----| -> String + +# 52| Interpolation +#-----| -> String + +# 52| chained +#-----| -> Interpolation + +# 52| String +#-----| -> chained + +# 54| Assignment +#-----| -> Class + +# 54| character +#-----| -> Assignment + +# 54| ?\x40 +#-----| -> character + +# 58| Class +#-----| -> Silly + +# 58| Silly +#-----| -> Object + +# 58| Superclass +#-----| -> 10-2i + +# 58| Object +#-----| -> Superclass + +# 59| Assignment +#-----| -> Conditional + +# 59| complex +#-----| -> Assignment + +# 59| 10-2i +#-----| -> complex + +# 60| Assignment +#-----| -> String + +# 60| conditional +#-----| -> Assignment + +# 60| Conditional +#-----| -> b + +# 60| Binary +#-----| true -> String +#-----| false -> String + +# 60| b +#-----| -> 10 + +# 60| 10 +#-----| -> Binary + +# 60| String +#-----| -> conditional + +# 60| String +#-----| -> conditional + +# 61| Assignment +#-----| -> 1 + +# 61| C +#-----| -> Assignment + +# 61| String +#-----| -> C + +# 62| Assignment +#-----| -> 1 + +# 62| DestructuredLeftAssignment +#-----| -> Assignment + +# 62| x +#-----| -> y + +# 62| DestructuredLeftAssignment +#-----| -> DestructuredLeftAssignment + +# 62| y +#-----| -> z + +# 62| z +#-----| -> DestructuredLeftAssignment + +# 62| Array +#-----| -> x + +# 62| 1 +#-----| -> 2 + +# 62| Array +#-----| -> Array + +# 62| 2 +#-----| -> 3 + +# 62| 3 +#-----| -> Array + +# 63| DestructuredParameter +#-----| -> a + +# 63| a +#-----| -> b + +# 63| b +#-----| -> DestructuredParameter + +# 64| MethodCall +#-----| -> b + +# 64| puts +#-----| -> MethodCall + +# 64| a +#-----| -> puts + +# 65| MethodCall +#-----| -> exit pattern (normal) + +# 65| puts +#-----| -> MethodCall + +# 65| b +#-----| -> puts + +# 67| Assignment +#-----| -> items + +# 67| items +#-----| -> Assignment + +# 67| Array +#-----| -> items + +# 67| 1 +#-----| -> 2 + +# 67| 2 +#-----| -> 3 + +# 67| 3 +#-----| -> Array + +# 68| MethodCall +#-----| -> 42 + +# 68| puts +#-----| -> MethodCall + +# 68| ElementReference +#-----| -> puts + +# 68| items +#-----| -> 2 + +# 68| 2 +#-----| -> ElementReference + +# 70| MethodCall +#-----| -> exit print (normal) + +# 70| puts +#-----| -> MethodCall + +# 70| String +#-----| -> puts + +# 74| Assignment +#-----| -> If + +# 74| x +#-----| -> Assignment + +# 74| 42 +#-----| -> x + +# 75| If +#-----| -> x + +# 75| Binary +#-----| true -> 0 +#-----| false -> Elsif + +# 75| x +#-----| -> 0 + +# 75| 0 +#-----| -> Binary + +# 75| 0 +#-----| -> ; + +# 75| Elsif +#-----| -> x + +# 75| Binary +#-----| true -> 10 +#-----| false -> x + +# 75| x +#-----| -> 10 + +# 75| 10 +#-----| -> Binary + +# 75| 10 +#-----| -> ; + +# 75| x +#-----| -> ; + +# 78| ; +#-----| -> String + +# 83| MethodCall +#-----| -> Ensure + +# 83| puts +#-----| -> MethodCall + +# 83| String +#-----| -> puts + +# 84| Ensure +#-----| -> String + +# 85| MethodCall +#-----| -> x + +# 85| puts +#-----| -> MethodCall + +# 85| String +#-----| -> puts + +# 88| Assignment +#-----| -> 1.4 + +# 88| escape +#-----| -> Assignment + +# 88| String +#-----| -> escape + +# 88| Interpolation +#-----| -> String + +# 88| x +#-----| -> Interpolation + +# 90| For +#-----| non-empty -> x +#-----| empty -> 42 + +# 90| x +#-----| -> If + +# 90| Array +#-----| -> For + +# 90| 1.4 +#-----| -> 2.5 + +# 90| 2.5 +#-----| -> 3.4e5 + +# 90| 3.4e5 +#-----| -> Array + +# 91| If +#-----| -> x + +# 91| Binary +#-----| true -> Next +#-----| false -> x + +# 91| x +#-----| -> 3 + +# 91| 3 +#-----| -> Binary + +# 91| Next +#-----| next -> For + +# 92| MethodCall +#-----| -> For + +# 92| puts +#-----| -> MethodCall + +# 92| x +#-----| -> puts + +# 95| Assignment +#-----| -> String + +# 95| $global +#-----| -> Assignment + +# 95| 42 +#-----| -> $global + +# 97| Assignment +#-----| -> map1 + +# 97| map1 +#-----| -> Assignment + +# 97| Hash +#-----| -> map1 + +# 97| Pair +#-----| -> String + +# 97| String +#-----| -> String + +# 97| String +#-----| -> Pair + +# 97| Pair +#-----| -> Hash + +# 97| String +#-----| -> String + +# 97| String +#-----| -> Pair + +# 98| Assignment +#-----| -> String + +# 98| map2 +#-----| -> Assignment + +# 98| Hash +#-----| -> map2 + +# 98| HashSplatArgument +#-----| -> String + +# 98| map1 +#-----| -> HashSplatArgument + +# 98| Pair +#-----| -> map1 + +# 98| String +#-----| -> String + +# 98| String +#-----| -> Pair + +# 98| HashSplatArgument +#-----| -> Hash + +# 98| map1 +#-----| -> HashSplatArgument + +# 101| OptionalParameter +#-----| no-match -> 42 +#-----| match -> KeywordParameter + +# 101| 42 +#-----| -> KeywordParameter + +# 101| KeywordParameter +#-----| -> HashSplatParameter + +# 101| HashSplatParameter +#-----| -> value + +# 102| MethodCall +#-----| -> kwargs + +# 102| puts +#-----| -> MethodCall + +# 102| value +#-----| -> puts + +# 103| Return +#-----| return -> exit parameters (normal) + +# 103| ElementReference +#-----| -> Return + +# 103| kwargs +#-----| -> key + +# 103| key +#-----| -> ElementReference + +# 106| Assignment +#-----| -> String + +# 106| type +#-----| -> Assignment + +# 106| String +#-----| -> type + +# 107| Assignment +#-----| -> < Assignment + +# 107| String +#-----| -> table + +# 108| MethodCall +#-----| -> IfModifier + +# 108| puts +#-----| -> MethodCall + +# 108| ParenthesizedStatements +#-----| -> puts + +# 108| < table + +# 108| HeredocBody +#-----| -> ParenthesizedStatements + +# 109| Interpolation +#-----| -> type + +# 109| table +#-----| -> Interpolation + +# 110| Interpolation +#-----| -> HeredocBody + +# 110| type +#-----| -> Interpolation + +# 113| IfModifier +#-----| -> b + +# 113| MethodCall +#-----| -> Class + +# 113| puts +#-----| -> MethodCall + +# 113| String +#-----| -> puts + +# 113| Binary +#-----| true -> String +#-----| false -> Class + +# 113| b +#-----| -> 10 + +# 113| 10 +#-----| -> Binary + +# 115| Class +#-----| -> C + +# 115| C +#-----| -> 42 + +# 116| Assignment +#-----| -> 10 + +# 116| @field +#-----| -> Assignment + +# 116| 42 +#-----| -> @field + +# 117| Assignment +#-----| -> swap + +# 117| @@static_field +#-----| -> Assignment + +# 117| 10 +#-----| -> @@static_field + +# 120| Assignment +#-----| -> Module + +# 120| swap +#-----| -> Assignment + +# 120| DestructuredParameter +#-----| -> exit lambda (normal) + +# 120| x +#-----| -> y + +# 120| y +#-----| -> DestructuredParameter + +# 120| Array +#-----| -> exit block (normal) + +# 120| y +#-----| -> x + +# 120| x +#-----| -> Array + +# 122| Module +#-----| -> M + +# 122| M +#-----| -> nil + +# 123| Assignment +#-----| -> 2 + +# 123| nothing +#-----| -> Assignment + +# 123| nil +#-----| -> nothing + +# 124| Assignment +#-----| -> some + +# 124| some +#-----| -> Assignment + +# 124| 2 +#-----| -> some + +# 125| OperatorAssignment +#-----| -> 2 + +# 125| some +#-----| -> 10 + +# 125| 10 +#-----| -> OperatorAssignment + +# 126| Assignment +#-----| -> 0 + +# 126| last +#-----| -> Assignment + +# 126| ParenthesizedStatements +#-----| -> last + +# 126| 2 +#-----| -> 4 + +# 126| 4 +#-----| -> 7 + +# 126| 7 +#-----| -> ParenthesizedStatements + +# 127| Assignment +#-----| -> 1 + +# 127| range +#-----| -> Assignment + +# 127| Range +#-----| -> range + +# 127| 0 +#-----| -> 9 + +# 127| 9 +#-----| -> Range + +# 128| Assignment +#-----| -> range + +# 128| half +#-----| -> Assignment + +# 128| Binary +#-----| -> half + +# 128| Binary +#-----| -> 1 + +# 128| 1 +#-----| -> Rational + +# 128| Rational +#-----| -> Binary + +# 128| Binary +#-----| -> Binary + +# 128| 1 +#-----| -> Rational + +# 128| Rational +#-----| -> Binary + +# 129| Assignment +#-----| -> 5 + +# 129| regex +#-----| -> Assignment + +# 129| Regex +#-----| -> regex + +# 129| Interpolation +#-----| -> Regex + +# 129| range +#-----| -> Interpolation + +# 130| Assignment +#-----| -> RescueModifier + +# 130| Constant +#-----| -> Assignment + +# 130| 5 +#-----| -> Constant + +# 133| RescueModifier +#-----| -> 1 + +# 133| Binary +#-----| raise -> String +#-----| -> 1 + +# 133| 1 +#-----| -> 0 + +# 133| 0 +#-----| -> Binary + +# 133| MethodCall +#-----| -> 1 + +# 133| puts +#-----| -> MethodCall + +# 133| String +#-----| -> puts + +# 135| Assignment +#-----| -> M + +# 135| DestructuredLeftAssignment +#-----| -> Assignment + +# 135| RestAssignment +#-----| -> last + +# 135| init +#-----| -> RestAssignment + +# 135| last +#-----| -> DestructuredLeftAssignment + +# 135| 1 +#-----| -> 2 + +# 135| 2 +#-----| -> 3 + +# 135| 3 +#-----| -> init + +# 137| ScopeResolution +#-----| -> M + +# 137| M +#-----| -> Constant + +# 137| Constant +#-----| -> ScopeResolution + +# 138| ScopeResolution +#-----| -> Silly + +# 138| Call +#-----| -> Constant + +# 138| M +#-----| -> itself + +# 138| itself +#-----| -> Call + +# 138| Constant +#-----| -> ScopeResolution + +# 140| Call +#-----| -> Silly + +# 140| Silly +#-----| -> itself + +# 140| itself +#-----| -> Call + +# 143| MethodCall +#-----| -> super + +# 143| puts +#-----| -> MethodCall + +# 143| String +#-----| -> puts + +# 144| MethodCall +#-----| -> exit print (normal) + +# 144| puts +#-----| -> MethodCall + +# 144| MethodCall +#-----| -> puts + +# 144| Call +#-----| -> MethodCall + +# 144| super +#-----| -> print + +# 144| print +#-----| -> Call + +# 148| Assignment +#-----| -> 1 + +# 148| silly +#-----| -> Assignment + +# 148| Call +#-----| -> silly + +# 148| Silly +#-----| -> new + +# 148| new +#-----| -> Call + +# 149| silly +#-----| -> SplatParameter + +# 149| SplatParameter +#-----| -> x + +# 150| MethodCall +#-----| -> exit method (normal) + +# 150| puts +#-----| -> MethodCall + +# 150| x +#-----| -> puts + +# 153| a +#-----| -> b + +# 153| b +#-----| -> exit two_parameters (normal) + +# 155| MethodCall +#-----| -> __FILE__ + +# 155| two_parameters +#-----| -> MethodCall + +# 155| SplatArgument +#-----| -> two_parameters + +# 155| Array +#-----| -> SplatArgument + +# 155| 1 +#-----| -> 2 + +# 155| 2 +#-----| -> Array + +# 157| Assignment +#-----| -> Symbol + +# 157| scriptfile +#-----| -> Assignment + +# 157| Subshell +#-----| -> scriptfile + +# 157| Interpolation +#-----| -> Subshell + +# 157| __FILE__ +#-----| -> Interpolation + +# 159| Assignment +#-----| -> true + +# 159| symbol +#-----| -> Assignment + +# 159| Symbol +#-----| -> symbol + +# 161| Assignment +#-----| -> true + +# 161| x +#-----| -> Assignment + +# 161| true +#-----| -> x + +# 162| Assignment +#-----| -> 42 + +# 162| x +#-----| -> Assignment + +# 162| Unary +#-----| -> x + +# 162| true +#-----| -> Unary + +# 163| Assignment +#-----| -> Undef + +# 163| x +#-----| -> Assignment + +# 163| Unary +#-----| -> x + +# 163| 42 +#-----| -> Unary + +# 165| Undef +#-----| -> two_parameters + +# 165| two_parameters +#-----| -> Unless + +# 167| Unless +#-----| -> x + +# 167| Binary +#-----| false -> String +#-----| true -> String + +# 167| x +#-----| -> 10 + +# 167| 10 +#-----| -> Binary + +# 167| MethodCall +#-----| -> UnlessModifier + +# 167| puts +#-----| -> MethodCall + +# 167| String +#-----| -> puts + +# 167| MethodCall +#-----| -> UnlessModifier + +# 167| puts +#-----| -> MethodCall + +# 167| String +#-----| -> puts + +# 169| UnlessModifier +#-----| -> x + +# 169| MethodCall +#-----| -> Until + +# 169| puts +#-----| -> MethodCall + +# 169| String +#-----| -> puts + +# 169| Binary +#-----| false -> String +#-----| true -> Until + +# 169| x +#-----| -> 0 + +# 169| 0 +#-----| -> Binary + +# 171| Until +#-----| -> x + +# 171| Binary +#-----| false -> x +#-----| true -> 0 + +# 171| x +#-----| -> 10 + +# 171| 10 +#-----| -> Binary + +# 171| OperatorAssignment +#-----| -> String + +# 171| x +#-----| -> 10 + +# 171| 10 +#-----| -> OperatorAssignment + +# 171| MethodCall +#-----| -> x + +# 171| puts +#-----| -> MethodCall + +# 171| String +#-----| -> puts + +# 173| Assignment +#-----| -> UntilModifier + +# 173| i +#-----| -> Assignment + +# 173| 0 +#-----| -> i + +# 174| UntilModifier +#-----| -> i + +# 174| ParenthesizedStatements +#-----| -> i + +# 174| MethodCall +#-----| -> i + +# 174| puts +#-----| -> MethodCall + +# 174| String +#-----| -> puts + +# 174| OperatorAssignment +#-----| -> ParenthesizedStatements + +# 174| i +#-----| -> 1 + +# 174| 1 +#-----| -> OperatorAssignment + +# 174| Binary +#-----| false -> String +#-----| true -> 0 + +# 174| i +#-----| -> 10 + +# 174| 10 +#-----| -> Binary + +# 176| Assignment +#-----| -> While + +# 176| x +#-----| -> Assignment + +# 176| 0 +#-----| -> x + +# 177| While +#-----| -> x + +# 177| Binary +#-----| true -> x +#-----| false -> WhileModifier + +# 177| x +#-----| -> 10 + +# 177| 10 +#-----| -> Binary + +# 178| OperatorAssignment +#-----| -> If + +# 178| x +#-----| -> 1 + +# 178| 1 +#-----| -> OperatorAssignment + +# 179| If +#-----| -> x + +# 179| Binary +#-----| true -> Redo +#-----| false -> x + +# 179| x +#-----| -> 5 + +# 179| 5 +#-----| -> Binary + +# 179| Redo +#-----| redo -> x + +# 180| MethodCall +#-----| -> x + +# 180| puts +#-----| -> MethodCall + +# 180| x +#-----| -> puts + +# 183| WhileModifier +#-----| -> i + +# 183| ParenthesizedStatements +#-----| -> i + +# 183| MethodCall +#-----| -> i + +# 183| puts +#-----| -> MethodCall + +# 183| String +#-----| -> puts + +# 183| OperatorAssignment +#-----| -> ParenthesizedStatements + +# 183| i +#-----| -> 1 + +# 183| 1 +#-----| -> OperatorAssignment + +# 183| Binary +#-----| true -> String +#-----| false -> run_block + +# 183| i +#-----| -> 0 + +# 183| 0 +#-----| -> Binary + +# 186| Yield +#-----| -> 42 + +# 186| 42 + +# 189| MethodCall +#-----| -> exit top-level (normal) + +# 189| run_block +#-----| -> MethodCall + +# 189| x +#-----| -> x + +# 189| MethodCall +#-----| -> exit block (normal) + +# 189| puts +#-----| -> MethodCall + +# 189| x +#-----| -> puts + +exit.rb: +# 1| x +#-----| -> If + +# 2| If +#-----| -> x + +# 2| Binary +#-----| true -> 1 +#-----| false -> String + +# 2| x +#-----| -> 2 + +# 2| 2 +#-----| -> Binary + +# 3| MethodCall +#-----| exit -> exit m1 (abnormal) + +# 3| exit +#-----| -> MethodCall + +# 3| 1 +#-----| -> exit + +# 5| MethodCall +#-----| -> exit m1 (normal) + +# 5| puts +#-----| -> MethodCall + +# 5| String +#-----| -> puts + +# 8| x +#-----| -> If + +# 9| If +#-----| -> x + +# 9| Binary +#-----| true -> String +#-----| false -> String + +# 9| x +#-----| -> 2 + +# 9| 2 +#-----| -> Binary + +# 10| MethodCall +#-----| exit -> exit m2 (abnormal) + +# 10| abort +#-----| -> MethodCall + +# 10| String +#-----| -> abort + +# 12| MethodCall +#-----| -> exit m2 (normal) + +# 12| puts +#-----| -> MethodCall + +# 12| String +#-----| -> puts + +ifs.rb: +# 1| x +#-----| -> If + +# 2| If +#-----| -> x + +# 2| Binary +#-----| true -> String +#-----| false -> Elsif + +# 2| x +#-----| -> 2 + +# 2| 2 +#-----| -> Binary + +# 3| MethodCall +#-----| -> exit m1 (normal) + +# 3| puts +#-----| -> MethodCall + +# 3| String +#-----| -> puts + +# 4| Elsif +#-----| -> x + +# 4| [false] Binary +#-----| false -> String + +# 4| [true] Binary +#-----| true -> String + +# 4| [false] Binary +#-----| false -> [false] Binary + +# 4| [true] Binary +#-----| true -> x + +# 4| Binary +#-----| false -> [false] Binary +#-----| true -> x + +# 4| x +#-----| -> 2 + +# 4| 2 +#-----| -> Binary + +# 4| Binary +#-----| false -> [false] Binary +#-----| true -> [true] Binary + +# 4| x +#-----| -> 0 + +# 4| 0 +#-----| -> Binary + +# 4| [false] Unary +#-----| false -> [false] Binary + +# 4| [true] Unary +#-----| true -> [true] Binary + +# 4| [false] ParenthesizedStatements +#-----| false -> [true] Unary + +# 4| [true] ParenthesizedStatements +#-----| true -> [false] Unary + +# 4| Binary +#-----| false -> [false] ParenthesizedStatements +#-----| true -> [true] ParenthesizedStatements + +# 4| x +#-----| -> 5 + +# 4| 5 +#-----| -> Binary + +# 5| MethodCall +#-----| -> exit m1 (normal) + +# 5| puts +#-----| -> MethodCall + +# 5| String +#-----| -> puts + +# 7| MethodCall +#-----| -> exit m1 (normal) + +# 7| puts +#-----| -> MethodCall + +# 7| String +#-----| -> puts + +# 11| b +#-----| -> If + +# 12| If +#-----| -> b + +# 12| b +#-----| true -> 0 +#-----| false -> 1 + +# 13| Return +#-----| return -> exit m2 (normal) + +# 13| 0 +#-----| -> Return + +# 15| Return +#-----| return -> exit m2 (normal) + +# 15| 1 +#-----| -> Return + +# 18| x +#-----| -> If + +# 19| If +#-----| -> x + +# 19| Binary +#-----| true -> x +#-----| false -> x + +# 19| x +#-----| -> 0 + +# 19| 0 +#-----| -> Binary + +# 20| Assignment +#-----| -> If + +# 20| x +#-----| -> Assignment + +# 20| Unary +#-----| -> x + +# 20| x +#-----| -> Unary + +# 21| If +#-----| -> x + +# 21| Binary +#-----| true -> x +#-----| false -> x + +# 21| x +#-----| -> 10 + +# 21| 10 +#-----| -> Binary + +# 22| Assignment +#-----| -> x + +# 22| x +#-----| -> Assignment + +# 22| Binary +#-----| -> x + +# 22| x +#-----| -> 1 + +# 22| 1 +#-----| -> Binary + +# 25| MethodCall +#-----| -> exit m3 (normal) + +# 25| puts +#-----| -> MethodCall + +# 25| x +#-----| -> puts + +# 28| b1 +#-----| -> b2 + +# 28| b2 +#-----| -> b3 + +# 28| b3 +#-----| -> Conditional + +# 29| Return +#-----| return -> exit m4 (normal) + +# 29| Conditional +#-----| -> Conditional + +# 29| ParenthesizedStatements +#-----| true -> String +#-----| false -> String + +# 29| Conditional +#-----| -> b1 + +# 29| b1 +#-----| true -> b2 +#-----| false -> b3 + +# 29| b2 +#-----| -> ParenthesizedStatements + +# 29| b3 +#-----| -> ParenthesizedStatements + +# 29| String +#-----| -> Return + +# 29| String +#-----| -> Return + +loops.rb: +# 1| x +#-----| -> While + +# 2| While +#-----| -> x + +# 2| Binary +#-----| true -> x +#-----| false -> exit m1 (normal) + +# 2| x +#-----| -> 0 + +# 2| 0 +#-----| -> Binary + +# 3| MethodCall +#-----| -> x + +# 3| puts +#-----| -> MethodCall + +# 3| x +#-----| -> puts + +# 4| OperatorAssignment +#-----| -> x + +# 4| x +#-----| -> 1 + +# 4| 1 +#-----| -> OperatorAssignment + +# 8| x +#-----| -> While + +# 9| While +#-----| -> x + +# 9| Binary +#-----| true -> x +#-----| false -> String + +# 9| x +#-----| -> 0 + +# 9| 0 +#-----| -> Binary + +# 10| MethodCall +#-----| -> x + +# 10| puts +#-----| -> MethodCall + +# 10| x +#-----| -> puts + +# 11| OperatorAssignment +#-----| -> If + +# 11| x +#-----| -> 1 + +# 11| 1 +#-----| -> OperatorAssignment + +# 12| If +#-----| -> x + +# 12| Binary +#-----| true -> Break +#-----| false -> Elsif + +# 12| x +#-----| -> 100 + +# 12| 100 +#-----| -> Binary + +# 13| Break +#-----| break -> String + +# 14| Elsif +#-----| -> x + +# 14| Binary +#-----| true -> Next +#-----| false -> Elsif + +# 14| x +#-----| -> 50 + +# 14| 50 +#-----| -> Binary + +# 15| Next +#-----| next -> x + +# 16| Elsif +#-----| -> x + +# 16| Binary +#-----| true -> Redo +#-----| false -> String + +# 16| x +#-----| -> 10 + +# 16| 10 +#-----| -> Binary + +# 17| Redo +#-----| redo -> x + +# 19| MethodCall +#-----| -> x + +# 19| puts +#-----| -> MethodCall + +# 19| String +#-----| -> puts + +# 21| MethodCall +#-----| -> exit m2 (normal) + +# 21| puts +#-----| -> MethodCall + +# 21| String +#-----| -> puts + +# 25| MethodCall +#-----| -> exit m3 (normal) + +# 25| Call +#-----| -> MethodCall + +# 25| Array +#-----| -> each + +# 25| 1 +#-----| -> 2 + +# 25| 2 +#-----| -> 3 + +# 25| 3 +#-----| -> Array + +# 25| each +#-----| -> Call + +# 25| x +#-----| -> x + +# 26| MethodCall +#-----| -> exit do block (normal) + +# 26| puts +#-----| -> MethodCall + +# 26| x +#-----| -> puts + +raise.rb: +# 1| Class +#-----| -> ExceptionA + +# 1| ExceptionA +#-----| -> Exception + +# 1| Superclass +#-----| -> Class + +# 1| Exception +#-----| -> Superclass + +# 4| Class +#-----| -> ExceptionB + +# 4| ExceptionB +#-----| -> Exception + +# 4| Superclass +#-----| -> exit top-level (normal) + +# 4| Exception +#-----| -> Superclass + +# 7| x +#-----| -> If + +# 8| If +#-----| -> x + +# 8| Binary +#-----| true -> String +#-----| false -> String + +# 8| x +#-----| -> 2 + +# 8| 2 +#-----| -> Binary + +# 9| MethodCall +#-----| raise -> exit m1 (abnormal) + +# 9| raise +#-----| -> MethodCall + +# 9| String +#-----| -> raise + +# 11| MethodCall +#-----| -> exit m1 (normal) + +# 11| puts +#-----| -> MethodCall + +# 11| String +#-----| -> puts + +# 14| b +#-----| -> If + +# 16| If +#-----| -> b + +# 16| b +#-----| true -> ExceptionA +#-----| false -> String + +# 17| MethodCall +#-----| raise -> Rescue + +# 17| raise +#-----| -> MethodCall + +# 17| ExceptionA +#-----| -> raise + +# 19| Rescue +#-----| -> ExceptionA + +# 19| ExceptionA +#-----| match -> String +#-----| raise -> exit m2 (abnormal) + +# 20| MethodCall +#-----| -> String + +# 20| puts +#-----| -> MethodCall + +# 20| String +#-----| -> puts + +# 22| MethodCall +#-----| -> exit m2 (normal) + +# 22| puts +#-----| -> MethodCall + +# 22| String +#-----| -> puts + +# 25| b +#-----| -> If + +# 27| If +#-----| -> b + +# 27| b +#-----| true -> ExceptionA +#-----| false -> String + +# 28| MethodCall +#-----| raise -> Rescue + +# 28| raise +#-----| -> MethodCall + +# 28| ExceptionA +#-----| -> raise + +# 30| Rescue +#-----| -> String + +# 31| MethodCall +#-----| -> String + +# 31| puts +#-----| -> MethodCall + +# 31| String +#-----| -> puts + +# 33| MethodCall +#-----| -> exit m3 (normal) + +# 33| puts +#-----| -> MethodCall + +# 33| String +#-----| -> puts + +# 36| b +#-----| -> If + +# 38| If +#-----| -> b + +# 38| b +#-----| true -> ExceptionA +#-----| false -> String + +# 39| MethodCall +#-----| raise -> Rescue + +# 39| raise +#-----| -> MethodCall + +# 39| ExceptionA +#-----| -> raise + +# 41| Rescue +#-----| -> e + +# 41| e +#-----| -> String + +# 42| MethodCall +#-----| -> String + +# 42| puts +#-----| -> MethodCall + +# 42| String +#-----| -> puts + +# 44| MethodCall +#-----| -> exit m4 (normal) + +# 44| puts +#-----| -> MethodCall + +# 44| String +#-----| -> puts + +# 47| b +#-----| -> If + +# 49| If +#-----| -> b + +# 49| b +#-----| true -> ExceptionA +#-----| false -> String + +# 50| MethodCall +#-----| raise -> Rescue + +# 50| raise +#-----| -> MethodCall + +# 50| ExceptionA +#-----| -> raise + +# 52| Rescue +#-----| -> e + +# 52| e +#-----| -> String + +# 54| MethodCall +#-----| -> exit m5 (normal) + +# 54| puts +#-----| -> MethodCall + +# 54| String +#-----| -> puts + +# 57| b +#-----| -> If + +# 59| If +#-----| -> b + +# 59| b +#-----| true -> ExceptionA +#-----| false -> String + +# 60| MethodCall +#-----| raise -> Rescue + +# 60| raise +#-----| -> MethodCall + +# 60| ExceptionA +#-----| -> raise + +# 62| Rescue +#-----| -> ExceptionA + +# 62| ExceptionA +#-----| no-match -> ExceptionB +#-----| match -> e + +# 62| ExceptionB +#-----| match -> e +#-----| raise -> exit m6 (abnormal) + +# 62| e +#-----| -> String + +# 63| MethodCall +#-----| -> String + +# 63| puts +#-----| -> MethodCall + +# 63| String +#-----| -> puts + +# 65| MethodCall +#-----| -> exit m6 (normal) + +# 65| puts +#-----| -> MethodCall + +# 65| String +#-----| -> puts + +# 68| x +#-----| -> If + +# 69| If +#-----| -> x + +# 69| Binary +#-----| true -> String +#-----| false -> Elsif + +# 69| x +#-----| -> 2 + +# 69| 2 +#-----| -> Binary + +# 70| MethodCall +#-----| raise -> [ensure: raise] Ensure + +# 70| raise +#-----| -> MethodCall + +# 70| String +#-----| -> raise + +# 71| Elsif +#-----| -> x + +# 71| Binary +#-----| true -> String +#-----| false -> String + +# 71| x +#-----| -> 0 + +# 71| 0 +#-----| -> Binary + +# 72| Return +#-----| return -> [ensure: return] Ensure + +# 72| String +#-----| -> Return + +# 74| MethodCall +#-----| -> Ensure + +# 74| puts +#-----| -> MethodCall + +# 74| String +#-----| -> puts + +# 75| Ensure +#-----| -> String + +# 75| [ensure: return] Ensure +#-----| -> [ensure: return] String + +# 75| [ensure: raise] Ensure +#-----| -> [ensure: raise] String + +# 76| MethodCall +#-----| -> exit m7 (normal) + +# 76| [ensure: return] MethodCall +#-----| return -> exit m7 (normal) + +# 76| [ensure: raise] MethodCall +#-----| raise -> exit m7 (abnormal) + +# 76| puts +#-----| -> MethodCall + +# 76| [ensure: return] puts +#-----| -> [ensure: return] MethodCall + +# 76| [ensure: raise] puts +#-----| -> [ensure: raise] MethodCall + +# 76| String +#-----| -> puts + +# 76| [ensure: return] String +#-----| -> [ensure: return] puts + +# 76| [ensure: raise] String +#-----| -> [ensure: raise] puts + +# 79| x +#-----| -> String + +# 80| MethodCall +#-----| -> If + +# 80| puts +#-----| -> MethodCall + +# 80| String +#-----| -> puts + +# 82| If +#-----| -> x + +# 82| Binary +#-----| true -> String +#-----| false -> Elsif + +# 82| x +#-----| -> 2 + +# 82| 2 +#-----| -> Binary + +# 83| MethodCall +#-----| raise -> [ensure: raise] Ensure + +# 83| raise +#-----| -> MethodCall + +# 83| String +#-----| -> raise + +# 84| Elsif +#-----| -> x + +# 84| Binary +#-----| true -> String +#-----| false -> String + +# 84| x +#-----| -> 0 + +# 84| 0 +#-----| -> Binary + +# 85| Return +#-----| return -> [ensure: return] Ensure + +# 85| String +#-----| -> Return + +# 87| MethodCall +#-----| -> Ensure + +# 87| puts +#-----| -> MethodCall + +# 87| String +#-----| -> puts + +# 88| Ensure +#-----| -> String + +# 88| [ensure: return] Ensure +#-----| -> [ensure: return] String + +# 88| [ensure: raise] Ensure +#-----| -> [ensure: raise] String + +# 89| MethodCall +#-----| -> String + +# 89| [ensure: return] MethodCall +#-----| return -> exit m8 (normal) + +# 89| [ensure: raise] MethodCall +#-----| raise -> exit m8 (abnormal) + +# 89| puts +#-----| -> MethodCall + +# 89| [ensure: return] puts +#-----| -> [ensure: return] MethodCall + +# 89| [ensure: raise] puts +#-----| -> [ensure: raise] MethodCall + +# 89| String +#-----| -> puts + +# 89| [ensure: return] String +#-----| -> [ensure: return] puts + +# 89| [ensure: raise] String +#-----| -> [ensure: raise] puts + +# 91| MethodCall +#-----| -> exit m8 (normal) + +# 91| puts +#-----| -> MethodCall + +# 91| String +#-----| -> puts + +# 94| x +#-----| -> b1 + +# 94| b1 +#-----| -> b2 + +# 94| b2 +#-----| -> String + +# 95| MethodCall +#-----| -> If + +# 95| puts +#-----| -> MethodCall + +# 95| String +#-----| -> puts + +# 97| If +#-----| -> x + +# 97| Binary +#-----| true -> String +#-----| false -> Elsif + +# 97| x +#-----| -> 2 + +# 97| 2 +#-----| -> Binary + +# 98| MethodCall +#-----| raise -> [ensure: raise] Ensure + +# 98| raise +#-----| -> MethodCall + +# 98| String +#-----| -> raise + +# 99| Elsif +#-----| -> x + +# 99| Binary +#-----| true -> String +#-----| false -> String + +# 99| x +#-----| -> 0 + +# 99| 0 +#-----| -> Binary + +# 100| Return +#-----| return -> [ensure: return] Ensure + +# 100| String +#-----| -> Return + +# 102| MethodCall +#-----| -> Ensure + +# 102| puts +#-----| -> MethodCall + +# 102| String +#-----| -> puts + +# 103| Ensure +#-----| -> String + +# 103| [ensure: return] Ensure +#-----| -> [ensure: return] String + +# 103| [ensure: raise] Ensure +#-----| -> [ensure: raise] String + +# 104| MethodCall +#-----| -> If + +# 104| [ensure: return] MethodCall +#-----| -> [ensure: return] If + +# 104| [ensure: raise] MethodCall +#-----| -> [ensure: raise] If + +# 104| puts +#-----| -> MethodCall + +# 104| [ensure: return] puts +#-----| -> [ensure: return] MethodCall + +# 104| [ensure: raise] puts +#-----| -> [ensure: raise] MethodCall + +# 104| String +#-----| -> puts + +# 104| [ensure: return] String +#-----| -> [ensure: return] puts + +# 104| [ensure: raise] String +#-----| -> [ensure: raise] puts + +# 106| If +#-----| -> b1 + +# 106| [ensure: return] If +#-----| -> [ensure: return] b1 + +# 106| [ensure: raise] If +#-----| -> [ensure: raise] b1 + +# 106| b1 +#-----| true -> String +#-----| false -> Ensure + +# 106| [ensure: return] b1 +#-----| true -> [ensure: return] String +#-----| false -> [ensure: return] Ensure + +# 106| [ensure: raise] b1 +#-----| true -> [ensure: raise] String +#-----| false -> [ensure: raise] Ensure + +# 107| MethodCall +#-----| raise -> [ensure(1): raise] Ensure + +# 107| [ensure: return] MethodCall +#-----| raise -> [ensure: return, ensure(1): raise] Ensure + +# 107| [ensure: raise] MethodCall +#-----| raise -> [ensure: raise, ensure(1): raise] Ensure + +# 107| raise +#-----| -> MethodCall + +# 107| [ensure: return] raise +#-----| -> [ensure: return] MethodCall + +# 107| [ensure: raise] raise +#-----| -> [ensure: raise] MethodCall + +# 107| String +#-----| -> raise + +# 107| [ensure: return] String +#-----| -> [ensure: return] raise + +# 107| [ensure: raise] String +#-----| -> [ensure: raise] raise + +# 109| Ensure +#-----| -> String + +# 109| [ensure(1): raise] Ensure +#-----| -> [ensure(1): raise] String + +# 109| [ensure: return] Ensure +#-----| -> [ensure: return] String + +# 109| [ensure: return, ensure(1): raise] Ensure +#-----| -> [ensure: return, ensure(1): raise] String + +# 109| [ensure: raise] Ensure +#-----| -> [ensure: raise] String + +# 109| [ensure: raise, ensure(1): raise] Ensure +#-----| -> [ensure: raise, ensure(1): raise] String + +# 110| MethodCall +#-----| -> String + +# 110| [ensure(1): raise] MethodCall +#-----| raise -> [ensure: raise] Ensure + +# 110| [ensure: return] MethodCall +#-----| return -> [ensure: return] Ensure + +# 110| [ensure: return, ensure(1): raise] MethodCall +#-----| raise -> [ensure: raise] Ensure + +# 110| [ensure: raise] MethodCall +#-----| raise -> [ensure: raise] Ensure + +# 110| [ensure: raise, ensure(1): raise] MethodCall +#-----| raise -> [ensure: raise] Ensure + +# 110| puts +#-----| -> MethodCall + +# 110| [ensure(1): raise] puts +#-----| -> [ensure(1): raise] MethodCall + +# 110| [ensure: return] puts +#-----| -> [ensure: return] MethodCall + +# 110| [ensure: return, ensure(1): raise] puts +#-----| -> [ensure: return, ensure(1): raise] MethodCall + +# 110| [ensure: raise] puts +#-----| -> [ensure: raise] MethodCall + +# 110| [ensure: raise, ensure(1): raise] puts +#-----| -> [ensure: raise, ensure(1): raise] MethodCall + +# 110| String +#-----| -> puts + +# 110| [ensure(1): raise] String +#-----| -> [ensure(1): raise] puts + +# 110| [ensure: return] String +#-----| -> [ensure: return] puts + +# 110| [ensure: return, ensure(1): raise] String +#-----| -> [ensure: return, ensure(1): raise] puts + +# 110| [ensure: raise] String +#-----| -> [ensure: raise] puts + +# 110| [ensure: raise, ensure(1): raise] String +#-----| -> [ensure: raise, ensure(1): raise] puts + +# 113| MethodCall +#-----| -> Ensure + +# 113| puts +#-----| -> MethodCall + +# 113| String +#-----| -> puts + +# 114| Ensure +#-----| -> String + +# 114| [ensure: return] Ensure +#-----| -> [ensure: return] String + +# 114| [ensure: raise] Ensure +#-----| -> [ensure: raise] String + +# 115| MethodCall +#-----| -> If + +# 115| [ensure: return] MethodCall +#-----| -> [ensure: return] If + +# 115| [ensure: raise] MethodCall +#-----| -> [ensure: raise] If + +# 115| puts +#-----| -> MethodCall + +# 115| [ensure: return] puts +#-----| -> [ensure: return] MethodCall + +# 115| [ensure: raise] puts +#-----| -> [ensure: raise] MethodCall + +# 115| String +#-----| -> puts + +# 115| [ensure: return] String +#-----| -> [ensure: return] puts + +# 115| [ensure: raise] String +#-----| -> [ensure: raise] puts + +# 116| If +#-----| -> b2 + +# 116| [ensure: return] If +#-----| -> [ensure: return] b2 + +# 116| [ensure: raise] If +#-----| -> [ensure: raise] b2 + +# 116| b2 +#-----| true -> String +#-----| false -> exit m9 (normal) + +# 116| [ensure: return] b2 +#-----| true -> [ensure: return] String +#-----| return -> exit m9 (normal) + +# 116| [ensure: raise] b2 +#-----| true -> [ensure: raise] String +#-----| raise -> exit m9 (abnormal) + +# 117| MethodCall +#-----| raise -> exit m9 (abnormal) + +# 117| [ensure: return] MethodCall +#-----| raise -> exit m9 (abnormal) + +# 117| [ensure: raise] MethodCall +#-----| raise -> exit m9 (abnormal) + +# 117| raise +#-----| -> MethodCall + +# 117| [ensure: return] raise +#-----| -> [ensure: return] MethodCall + +# 117| [ensure: raise] raise +#-----| -> [ensure: raise] MethodCall + +# 117| String +#-----| -> raise + +# 117| [ensure: return] String +#-----| -> [ensure: return] raise + +# 117| [ensure: raise] String +#-----| -> [ensure: raise] raise + +# 121| OptionalParameter +#-----| no-match -> String +#-----| match -> Ensure + +# 121| MethodCall +#-----| raise -> exit m10 (abnormal) + +# 121| raise +#-----| -> MethodCall + +# 121| String +#-----| -> raise + +# 124| Ensure +#-----| -> String + +# 125| MethodCall +#-----| -> exit m10 (normal) + +# 125| puts +#-----| -> MethodCall + +# 125| String +#-----| -> puts + +# 128| b +#-----| -> If + +# 130| If +#-----| -> b + +# 130| b +#-----| true -> ExceptionA +#-----| false -> Ensure + +# 131| MethodCall +#-----| raise -> Rescue + +# 131| raise +#-----| -> MethodCall + +# 131| ExceptionA +#-----| -> raise + +# 133| Rescue +#-----| -> ExceptionA + +# 133| ExceptionA +#-----| no-match -> Rescue +#-----| match -> Ensure + +# 134| Rescue +#-----| -> ExceptionB + +# 134| ExceptionB +#-----| match -> String +#-----| raise -> [ensure: raise] Ensure + +# 135| MethodCall +#-----| -> Ensure + +# 135| puts +#-----| -> MethodCall + +# 135| String +#-----| -> puts + +# 136| Ensure +#-----| -> String + +# 136| [ensure: raise] Ensure +#-----| -> [ensure: raise] String + +# 137| MethodCall +#-----| -> String + +# 137| [ensure: raise] MethodCall +#-----| raise -> exit m11 (abnormal) + +# 137| puts +#-----| -> MethodCall + +# 137| [ensure: raise] puts +#-----| -> [ensure: raise] MethodCall + +# 137| String +#-----| -> puts + +# 137| [ensure: raise] String +#-----| -> [ensure: raise] puts + +# 139| MethodCall +#-----| -> exit m11 (normal) + +# 139| puts +#-----| -> MethodCall + +# 139| String +#-----| -> puts + +break_ensure.rb: +# 1| exit m1 + +# 13| exit m2 + +# 27| exit m3 + +case.rb: +# 1| exit if_in_case + +cfg.rb: +# 1| exit top-level + +# 15| exit BEGIN block + +# 19| exit END block + +# 25| exit block + +# 29| exit block + +# 63| exit pattern + +# 69| exit print + +# 101| exit parameters + +# 120| exit lambda + +# 120| exit block + +# 142| exit print + +# 149| exit method + +# 153| exit two_parameters + +# 189| exit block + +exit.rb: +# 1| exit m1 + +# 8| exit m2 + +ifs.rb: +# 1| exit m1 + +# 11| exit m2 + +# 18| exit m3 + +# 28| exit m4 + +loops.rb: +# 1| exit m1 + +# 8| exit m2 + +# 24| exit m3 + +# 25| exit do block + +raise.rb: +# 1| exit top-level + +# 7| exit m1 + +# 14| exit m2 + +# 25| exit m3 + +# 36| exit m4 + +# 47| exit m5 + +# 57| exit m6 + +# 68| exit m7 + +# 79| exit m8 + +# 94| exit m9 + +# 121| exit m10 + +# 128| exit m11 + +break_ensure.rb: +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 13| exit m2 (normal) +#-----| -> exit m2 + +# 27| exit m3 (normal) +#-----| -> exit m3 + +case.rb: +# 1| exit if_in_case (normal) +#-----| -> exit if_in_case + +cfg.rb: +# 1| exit top-level (normal) +#-----| -> exit top-level + +# 15| exit BEGIN block (normal) +#-----| -> exit BEGIN block + +# 19| exit END block (normal) +#-----| -> exit END block + +# 25| exit block (normal) +#-----| -> exit block + +# 29| exit block (normal) +#-----| -> exit block + +# 63| exit pattern (normal) +#-----| -> exit pattern + +# 69| exit print (normal) +#-----| -> exit print + +# 101| exit parameters (normal) +#-----| -> exit parameters + +# 120| exit lambda (normal) +#-----| -> exit lambda + +# 120| exit block (normal) +#-----| -> exit block + +# 142| exit print (normal) +#-----| -> exit print + +# 149| exit method (normal) +#-----| -> exit method + +# 153| exit two_parameters (normal) +#-----| -> exit two_parameters + +# 189| exit block (normal) +#-----| -> exit block + +exit.rb: +# 1| exit m1 (abnormal) +#-----| -> exit m1 + +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 8| exit m2 (abnormal) +#-----| -> exit m2 + +# 8| exit m2 (normal) +#-----| -> exit m2 + +ifs.rb: +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 11| exit m2 (normal) +#-----| -> exit m2 + +# 18| exit m3 (normal) +#-----| -> exit m3 + +# 28| exit m4 (normal) +#-----| -> exit m4 + +loops.rb: +# 1| exit m1 (normal) +#-----| -> exit m1 + +# 8| exit m2 (normal) +#-----| -> exit m2 + +# 24| exit m3 (normal) +#-----| -> exit m3 + +# 25| exit do block (normal) +#-----| -> exit do block + +raise.rb: +# 1| exit top-level (normal) +#-----| -> exit top-level + +# 7| exit m1 (abnormal) +#-----| -> exit m1 + +# 7| exit m1 (normal) +#-----| -> exit m1 + +# 14| exit m2 (abnormal) +#-----| -> exit m2 + +# 14| exit m2 (normal) +#-----| -> exit m2 + +# 25| exit m3 (normal) +#-----| -> exit m3 + +# 36| exit m4 (normal) +#-----| -> exit m4 + +# 47| exit m5 (normal) +#-----| -> exit m5 + +# 57| exit m6 (abnormal) +#-----| -> exit m6 + +# 57| exit m6 (normal) +#-----| -> exit m6 + +# 68| exit m7 (abnormal) +#-----| -> exit m7 + +# 68| exit m7 (normal) +#-----| -> exit m7 + +# 79| exit m8 (abnormal) +#-----| -> exit m8 + +# 79| exit m8 (normal) +#-----| -> exit m8 + +# 94| exit m9 (abnormal) +#-----| -> exit m9 + +# 94| exit m9 (normal) +#-----| -> exit m9 + +# 121| exit m10 (abnormal) +#-----| -> exit m10 + +# 121| exit m10 (normal) +#-----| -> exit m10 + +# 128| exit m11 (abnormal) +#-----| -> exit m11 + +# 128| exit m11 (normal) +#-----| -> exit m11 diff --git a/ql/test/library-tests/controlflow/graph/break_ensure.rb b/ql/test/library-tests/controlflow/graph/break_ensure.rb new file mode 100644 index 00000000000..cf181e3fd38 --- /dev/null +++ b/ql/test/library-tests/controlflow/graph/break_ensure.rb @@ -0,0 +1,42 @@ +def m1 elements + for element in elements do + if x > 0 then + break + end + end +ensure + if elements.nil? then + puts "elements nil" + end +end + +def m2 elements + for element in elements do + begin + if x > 0 then + break + end + ensure + if elements.nil? then + puts "elements nil" + end + end + end +end + +def m3 elements + begin + if elements.nil? then + return + end + ensure + for element in elements do + begin + if x > 0 then + break + end + end + end + end + puts "Done" +end diff --git a/ql/test/library-tests/controlflow/graph/ifs.rb b/ql/test/library-tests/controlflow/graph/ifs.rb index fdd3ae21928..4ea7922b1ae 100644 --- a/ql/test/library-tests/controlflow/graph/ifs.rb +++ b/ql/test/library-tests/controlflow/graph/ifs.rb @@ -24,3 +24,7 @@ def m3 x end puts x end + +def m4 (b1, b2, b3) + return (b1 ? b2 : b3) ? "b2 || b3" : "!b2 || !b3" +end \ No newline at end of file diff --git a/ql/test/library-tests/controlflow/graph/raise.rb b/ql/test/library-tests/controlflow/graph/raise.rb index 9b92eafdcf6..cf775077594 100644 --- a/ql/test/library-tests/controlflow/graph/raise.rb +++ b/ql/test/library-tests/controlflow/graph/raise.rb @@ -1,6 +1,140 @@ +class ExceptionA < Exception +end + +class ExceptionB < Exception +end + def m1 x if x > 2 raise "x > 2" end puts "x <= 2" end + +def m2 b + begin + if b + raise ExceptionA + end + rescue ExceptionA + puts "Rescued" + end + puts "End m2" +end + +def m3 b + begin + if b + raise ExceptionA + end + rescue + puts "Rescued" + end + puts "End m3" +end + +def m4 b + begin + if b + raise ExceptionA + end + rescue => e + puts "Rescued {e}" + end + puts "End m4" +end + +def m5 b + begin + if b + raise ExceptionA + end + rescue => e + end + puts "End m5" +end + +def m6 b + begin + if b + raise ExceptionA + end + rescue ExceptionA, ExceptionB => e + puts "Rescued {e}" + end + puts "End m6" +end + +def m7 x + if x > 2 + raise "x > 2" + elsif x < 0 + return "x < 0" + end + puts "0 <= x <= 2" +ensure + puts "ensure" +end + +def m8 x + puts "Begin m8" + begin + if x > 2 + raise "x > 2" + elsif x < 0 + return "x < 0" + end + puts "0 <= x <= 2" + ensure + puts "ensure" + end + puts "End m8" +end + +def m9(x, b1, b2) + puts "Begin m9" + begin + if x > 2 + raise "x > 2" + elsif x < 0 + return "x < 0" + end + puts "0 <= x <= 2" + ensure + puts "outer ensure" + begin + if b1 + raise "b1 is true" + end + ensure + puts "inner ensure" + end + end + puts "End m9" +ensure + puts "method ensure" + if b2 + raise "b2 is true" + end +end + +def m10(p = (raise "Exception")) +rescue + puts "Will not get executed if p is not supplied" +ensure + puts "Will not get executed if p is not supplied" +end + +def m11 b + begin + if b + raise ExceptionA + end + rescue ExceptionA + rescue ExceptionB + puts "ExceptionB" + ensure + puts "Ensure" + end + puts "End m5" +end