diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll index 4392a42f9acd..6c3faaba1a68 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -4,26 +4,20 @@ import csharp private import ControlFlow::SuccessorTypes +private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as CfgImpl +private import CfgImpl::BasicBlocks as BasicBlocksImpl /** * A basic block, that is, a maximal straight-line sequence of control flow nodes * without branches or joins. */ -class BasicBlock extends TBasicBlockStart { - /** Gets an immediate successor of this basic block, if any. */ - BasicBlock getASuccessor() { result.getFirstNode() = this.getLastNode().getASuccessor() } - +final class BasicBlock extends BasicBlocksImpl::BasicBlock { /** Gets an immediate successor of this basic block of a given type, if any. */ - BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) { - result.getFirstNode() = this.getLastNode().getASuccessorByType(t) - } - - /** Gets an immediate predecessor of this basic block, if any. */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } + BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) { result = this.getASuccessor(t) } /** Gets an immediate predecessor of this basic block of a given type, if any. */ BasicBlock getAPredecessorByType(ControlFlow::SuccessorType t) { - result.getASuccessorByType(t) = this + result = this.getAPredecessor(t) } /** @@ -65,23 +59,20 @@ class BasicBlock extends TBasicBlockStart { } /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ - ControlFlow::Node getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } + ControlFlow::Node getNode(int pos) { result = super.getNode(pos) } /** Gets a control flow node in this basic block. */ - ControlFlow::Node getANode() { result = this.getNode(_) } + ControlFlow::Node getANode() { result = super.getANode() } /** Gets the first control flow node in this basic block. */ - ControlFlow::Node getFirstNode() { this = TBasicBlockStart(result) } + ControlFlow::Node getFirstNode() { result = super.getFirstNode() } /** Gets the last control flow node in this basic block. */ - ControlFlow::Node getLastNode() { result = this.getNode(this.length() - 1) } + ControlFlow::Node getLastNode() { result = super.getLastNode() } /** Gets the callable that this basic block belongs to. */ final Callable getCallable() { result = this.getFirstNode().getEnclosingCallable() } - /** Gets the length of this basic block. */ - int length() { result = strictcount(this.getANode()) } - /** * Holds if this basic block immediately dominates basic block `bb`. * @@ -103,7 +94,7 @@ class BasicBlock extends TBasicBlockStart { * basic block on line 4 (all paths from the entry point of `M` * to `return s.Length;` must go through the null check). */ - predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } + predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) } /** * Holds if this basic block strictly dominates basic block `bb`. @@ -126,7 +117,7 @@ class BasicBlock extends TBasicBlockStart { * basic block on line 4 (all paths from the entry point of `M` * to `return s.Length;` must go through the null check). */ - predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } + predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) } /** * Holds if this basic block dominates basic block `bb`. @@ -178,15 +169,7 @@ class BasicBlock extends TBasicBlockStart { * `Console.Write(x);`. Also, the basic block starting on line 2 * does not dominate the basic block on line 6. */ - predicate inDominanceFrontier(BasicBlock df) { - this.dominatesPredecessor(df) and - not this.strictlyDominates(df) - } - - /** - * Holds if this basic block dominates a predecessor of `df`. - */ - private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } /** * Gets the basic block that immediately dominates this basic block, if any. @@ -208,7 +191,7 @@ class BasicBlock extends TBasicBlockStart { * the basic block online 4 (all paths from the entry point of `M` * to `return s.Length;` must go through the null check. */ - BasicBlock getImmediateDominator() { bbIDominates(result, this) } + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } /** * Holds if this basic block strictly post-dominates basic block `bb`. @@ -234,7 +217,7 @@ class BasicBlock extends TBasicBlockStart { * line 3 (all paths to the exit point of `M` from `return s.Length;` * must go through the `WriteLine` call). */ - predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) } /** * Holds if this basic block post-dominates basic block `bb`. @@ -262,10 +245,7 @@ class BasicBlock extends TBasicBlockStart { * This predicate is *reflexive*, so for example `Console.WriteLine("M");` * post-dominates itself. */ - predicate postDominates(BasicBlock bb) { - this.strictlyPostDominates(bb) or - this = bb - } + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } /** * Holds if this basic block is in a loop in the control flow graph. This @@ -274,230 +254,44 @@ class BasicBlock extends TBasicBlockStart { * necessary back edges are unreachable. */ predicate inLoop() { this.getASuccessor+() = this } - - /** Gets a textual representation of this basic block. */ - string toString() { result = this.getFirstNode().toString() } - - /** Gets the location of this basic block. */ - Location getLocation() { result = this.getFirstNode().getLocation() } -} - -/** - * Internal implementation details. - */ -cached -private module Internal { - /** Internal representation of basic blocks. */ - cached - newtype TBasicBlock = TBasicBlockStart(ControlFlow::Node cfn) { startsBB(cfn) } - - /** Holds if `cfn` starts a new basic block. */ - private predicate startsBB(ControlFlow::Node cfn) { - not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor()) - or - cfn.isJoin() - or - cfn.getAPredecessor().isBranch() - or - /* - * In cases such as - * ```csharp - * if (b) - * M() - * ``` - * where the `false` edge out of `b` is not present (because we can prove it - * impossible), we still split up the basic block in two, in order to generate - * a `ConditionBlock` which can be used by the guards library. - */ - - exists(cfn.getAPredecessorByType(any(ControlFlow::SuccessorTypes::ConditionalSuccessor s))) - } - - /** - * Holds if `succ` is a control flow successor of `pred` within - * the same basic block. - */ - private predicate intraBBSucc(ControlFlow::Node pred, ControlFlow::Node succ) { - succ = pred.getASuccessor() and - not startsBB(succ) - } - - /** - * Holds if `cfn` is the `i`th node in basic block `bb`. - * - * In other words, `i` is the shortest distance from a node `bb` - * that starts a basic block to `cfn` along the `intraBBSucc` relation. - */ - cached - predicate bbIndex(ControlFlow::Node bbStart, ControlFlow::Node cfn, int i) = - shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) - - /** - * Holds if the first node of basic block `succ` is a control flow - * successor of the last node of basic block `pred`. - */ - private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() } - - /** Holds if `dom` is an immediate dominator of `bb`. */ - cached - predicate bbIDominates(BasicBlock dom, BasicBlock bb) = - idominance(entryBB/1, succBB/2)(_, dom, bb) - - /** Holds if `pred` is a basic block predecessor of `succ`. */ - private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } - - /** Holds if `bb` is an exit basic block that represents normal exit. */ - private predicate normalExitBB(BasicBlock bb) { - bb.getANode().(ControlFlow::Nodes::AnnotatedExitNode).isNormal() - } - - /** Holds if `dom` is an immediate post-dominator of `bb`. */ - cached - predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = - idominance(normalExitBB/1, predBB/2)(_, dom, bb) } -private import Internal - /** * An entry basic block, that is, a basic block whose first node is - * the entry node of a callable. + * an entry node. */ -class EntryBasicBlock extends BasicBlock { - EntryBasicBlock() { entryBB(this) } -} - -/** Holds if `bb` is an entry basic block. */ -private predicate entryBB(BasicBlock bb) { - bb.getFirstNode() instanceof ControlFlow::Nodes::EntryNode -} +final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { } /** - * An annotated exit basic block, that is, a basic block that contains + * An annotated exit basic block, that is, a basic block whose last node is * an annotated exit node. */ -class AnnotatedExitBasicBlock extends BasicBlock { - private boolean isNormal; - - AnnotatedExitBasicBlock() { - this.getANode() = - any(ControlFlow::Nodes::AnnotatedExitNode n | - if n.isNormal() then isNormal = true else isNormal = false - ) - } - - /** Holds if this block represents a normal exit. */ - predicate isNormal() { isNormal = true } -} +final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { } /** * An exit basic block, that is, a basic block whose last node is - * the exit node of a callable. + * an exit node. */ -class ExitBasicBlock extends BasicBlock { - ExitBasicBlock() { this.getLastNode() instanceof ControlFlow::Nodes::ExitNode } -} - -private module JoinBlockPredecessors { - private import ControlFlow::Nodes - private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as Impl - - int getId(JoinBlockPredecessor jbp) { - exists(Impl::AstNode n | result = n.getId() | - n = jbp.getFirstNode().getAstNode() - or - n = jbp.(EntryBasicBlock).getCallable() - ) - } - - string getSplitString(JoinBlockPredecessor jbp) { - result = jbp.getFirstNode().(ElementNode).getSplitsString() - or - not exists(jbp.getFirstNode().(ElementNode).getSplitsString()) and - result = "" - } -} +final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { } /** A basic block with more than one predecessor. */ -class JoinBlock extends BasicBlock { - JoinBlock() { this.getFirstNode().isJoin() } - - /** - * Gets the `i`th predecessor of this join block, with respect to some - * arbitrary order. - */ - cached - JoinBlockPredecessor getJoinBlockPredecessor(int i) { - result = - rank[i + 1](JoinBlockPredecessor jbp | - jbp = this.getAPredecessor() - | - jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) - ) - } +final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock { + JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) } } /** A basic block that is an immediate predecessor of a join block. */ -class JoinBlockPredecessor extends BasicBlock { - JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock } -} - -/** A basic block that terminates in a condition, splitting the subsequent control flow. */ -class ConditionBlock extends BasicBlock { - ConditionBlock() { this.getLastNode().isCondition() } +final class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { } - /** - * Holds if basic block `succ` is immediately controlled by this basic - * block with conditional value `s`. That is, `succ` is an immediate - * successor of this block, and `succ` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ - pragma[nomagic] +/** + * A basic block that terminates in a condition, splitting the subsequent + * control flow. + */ +final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock { predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) { - succ = this.getASuccessorByType(s) and - forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred)) + super.immediatelyControls(succ, s) } - /** - * Holds if basic block `controlled` is controlled by this basic block with - * conditional value `s`. That is, `controlled` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ predicate controls(BasicBlock controlled, ConditionalSuccessor s) { - /* - * For this block to control the block `controlled` with `testIsTrue` the following must be true: - * Execution must have passed through the test i.e. `this` must strictly dominate `controlled`. - * Execution must have passed through the `testIsTrue` edge leaving `this`. - * - * Although "passed through the true edge" implies that `this.getATrueSuccessor()` dominates `controlled`, - * the reverse is not true, as flow may have passed through another edge to get to `this.getATrueSuccessor()` - * so we need to assert that `this.getATrueSuccessor()` dominates `controlled` *and* that - * all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`. - * - * For example, in the following C# snippet: - * ```csharp - * if (x) - * controlled; - * false_successor; - * uncontrolled; - * ``` - * `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`) - * or dominated by itself. Whereas in the following code: - * ```csharp - * if (x) - * while (controlled) - * also_controlled; - * false_successor; - * uncontrolled; - * ``` - * the block `while controlled` is controlled because all of its predecessors are `this` (`if (x)`) - * or (in the case of `also_controlled`) dominated by itself. - * - * The additional constraint on the predecessors of the test successor implies - * that `this` strictly dominates `controlled` so that isn't necessary to check - * directly. - */ - - exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled)) + super.controls(controlled, s) } } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index 463ed260067c..fcbdaeb8b4c1 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -95,6 +95,10 @@ private module CfgInput implements CfgShared::InputSig { t instanceof ST::SuccessorTypes::ExceptionSuccessor or t instanceof ST::SuccessorTypes::ExitSuccessor } + + predicate idOfAstNode(AstNode node, int id) { node.getId() = id } + + predicate idOfCfgScope(CfgScope node, int id) { idOfAstNode(node, id) } } private module CfgSplittingInput implements CfgShared::SplittingInputSig { diff --git a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll index 350f07a431a7..25eeee61d225 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/BasicBlocks.qll @@ -4,45 +4,40 @@ private import codeql.ruby.AST private import codeql.ruby.ast.internal.AST private import codeql.ruby.ast.internal.TreeSitter private import codeql.ruby.controlflow.ControlFlowGraph +private import codeql.ruby.controlflow.ControlFlowGraph as Cfg +private import internal.ControlFlowGraphImpl as CfgImpl private import CfgNodes private import SuccessorTypes +private import codeql.controlflow.BasicBlock as BB +private import CfgImpl::BasicBlocks as BasicBlocksImpl /** * A basic block, that is, a maximal straight-line sequence of control flow nodes * without branches or joins. */ -class BasicBlock extends TBasicBlockStart { - /** Gets the scope of this basic block. */ - final CfgScope getScope() { result = this.getFirstNode().getScope() } - +final class BasicBlock extends BasicBlocksImpl::BasicBlock { /** Gets an immediate successor of this basic block, if any. */ - BasicBlock getASuccessor() { result = this.getASuccessor(_) } + BasicBlock getASuccessor() { result = super.getASuccessor() } /** Gets an immediate successor of this basic block of a given type, if any. */ - BasicBlock getASuccessor(SuccessorType t) { - result.getFirstNode() = this.getLastNode().getASuccessor(t) - } + BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor(t) } /** Gets an immediate predecessor of this basic block, if any. */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } + BasicBlock getAPredecessor() { result = super.getAPredecessor() } /** Gets an immediate predecessor of this basic block of a given type, if any. */ - BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } + BasicBlock getAPredecessor(SuccessorType t) { result = super.getAPredecessor(t) } - /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ - CfgNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } + // The overrides below are to use `CfgNode` instead of `CfgImpl::Node` + CfgNode getNode(int pos) { result = super.getNode(pos) } - /** Gets a control flow node in this basic block. */ - CfgNode getANode() { result = this.getNode(_) } + CfgNode getANode() { result = super.getANode() } /** Gets the first control flow node in this basic block. */ - CfgNode getFirstNode() { this = TBasicBlockStart(result) } + CfgNode getFirstNode() { result = super.getFirstNode() } /** Gets the last control flow node in this basic block. */ - CfgNode getLastNode() { result = this.getNode(this.length() - 1) } - - /** Gets the length of this basic block. */ - int length() { result = strictcount(this.getANode()) } + CfgNode getLastNode() { result = super.getLastNode() } /** * Holds if this basic block immediately dominates basic block `bb`. @@ -66,31 +61,7 @@ class BasicBlock extends TBasicBlockStart { * basic block on line 5 (all paths from the entry point of `m` * to `return 1` must go through the `if` block). */ - predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } - - /** - * Holds if this basic block strictly dominates basic block `bb`. - * - * That is, all paths reaching basic block `bb` from some entry point - * basic block must go through this basic block (which must be different - * from `bb`). - * - * Example: - * - * ```rb - * def m b - * if b - * return 0 - * end - * return 1 - * end - * ``` - * - * The basic block starting on line 2 strictly dominates the - * basic block on line 5 (all paths from the entry point of `m` - * to `return 1` must go through the `if` block). - */ - predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } + predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) } /** * Holds if this basic block dominates basic block `bb`. @@ -113,10 +84,7 @@ class BasicBlock extends TBasicBlockStart { * basic block on line 5 (all paths from the entry point of `m` * to `return 1` must go through the `if` block). */ - predicate dominates(BasicBlock bb) { - bb = this or - this.strictlyDominates(bb) - } + predicate dominates(BasicBlock bb) { super.dominates(bb) } /** * Holds if `df` is in the dominance frontier of this basic block. @@ -143,15 +111,7 @@ class BasicBlock extends TBasicBlockStart { * `puts x`. Also, the basic block starting on line 3 does not * dominate the basic block on line 8. */ - predicate inDominanceFrontier(BasicBlock df) { - this.dominatesPredecessor(df) and - not this.strictlyDominates(df) - } - - /** - * Holds if this basic block dominates a predecessor of `df`. - */ - private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } /** * Gets the basic block that immediately dominates this basic block, if any. @@ -176,7 +136,7 @@ class BasicBlock extends TBasicBlockStart { * to `return 1` must go through the `if` block, and the `if` block * is an immediate predecessor of `return 1`). */ - BasicBlock getImmediateDominator() { bbIDominates(result, this) } + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } /** * Holds if this basic block strictly post-dominates basic block `bb`. @@ -200,7 +160,7 @@ class BasicBlock extends TBasicBlockStart { * line 3 (all paths to the exit point of `m` from `puts "b"` must go * through `puts "m"`). */ - predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyDominates(bb) } /** * Holds if this basic block post-dominates basic block `bb`. @@ -223,221 +183,45 @@ class BasicBlock extends TBasicBlockStart { * (all paths to the exit point of `m` from `puts "b"` must go through * `puts "m"`). */ - predicate postDominates(BasicBlock bb) { - this.strictlyPostDominates(bb) or - this = bb - } - - /** Holds if this basic block is in a loop in the control flow graph. */ - predicate inLoop() { this.getASuccessor+() = this } - - /** Gets a textual representation of this basic block. */ - string toString() { result = this.getFirstNode().toString() } - - /** Gets the location of this basic block. */ - Location getLocation() { result = this.getFirstNode().getLocation() } -} - -cached -private module Cached { - /** Internal representation of basic blocks. */ - cached - newtype TBasicBlock = TBasicBlockStart(CfgNode cfn) { startsBB(cfn) } - - /** Holds if `cfn` starts a new basic block. */ - private predicate startsBB(CfgNode cfn) { - not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor()) - or - cfn.isJoin() - or - cfn.getAPredecessor().isBranch() - or - /* - * In cases such as - * - * ```rb - * if x or y - * foo - * else - * bar - * ``` - * - * we have a CFG that looks like - * - * x --false--> [false] x or y --false--> bar - * \ | - * --true--> y --false-- - * \ - * --true--> [true] x or y --true--> foo - * - * and we want to ensure that both `foo` and `bar` start a new basic block, - * in order to get a `ConditionalBlock` out of the disjunction. - */ - - exists(cfn.getAPredecessor(any(SuccessorTypes::ConditionalSuccessor s))) - } - - /** - * Holds if `succ` is a control flow successor of `pred` within - * the same basic block. - */ - private predicate intraBBSucc(CfgNode pred, CfgNode succ) { - succ = pred.getASuccessor() and - not startsBB(succ) - } - - /** - * Holds if `cfn` is the `i`th node in basic block `bb`. - * - * In other words, `i` is the shortest distance from a node `bb` - * that starts a basic block to `cfn` along the `intraBBSucc` relation. - */ - cached - predicate bbIndex(CfgNode bbStart, CfgNode cfn, int i) = - shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) - - /** - * Holds if the first node of basic block `succ` is a control flow - * successor of the last node of basic block `pred`. - */ - private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() } - - /** Holds if `dom` is an immediate dominator of `bb`. */ - cached - predicate bbIDominates(BasicBlock dom, BasicBlock bb) = - idominance(entryBB/1, succBB/2)(_, dom, bb) - - /** Holds if `pred` is a basic block predecessor of `succ`. */ - private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } - - /** Holds if `bb` is an exit basic block that represents normal exit. */ - private predicate normalExitBB(BasicBlock bb) { bb.getANode().(AnnotatedExitNode).isNormal() } - - /** Holds if `dom` is an immediate post-dominator of `bb`. */ - cached - predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = - idominance(normalExitBB/1, predBB/2)(_, dom, bb) - - /** - * Gets the `i`th predecessor of join block `jb`, with respect to some - * arbitrary order. - */ - cached - JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) { - result = - rank[i + 1](JoinBlockPredecessor jbp | - jbp = jb.getAPredecessor() - | - jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) - ) - } - - cached - predicate immediatelyControls(ConditionBlock cb, BasicBlock succ, ConditionalSuccessor s) { - succ = cb.getASuccessor(s) and - forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != cb | succ.dominates(pred)) - } - - cached - predicate controls(ConditionBlock cb, BasicBlock controlled, ConditionalSuccessor s) { - exists(BasicBlock succ | cb.immediatelyControls(succ, s) | succ.dominates(controlled)) - } + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } } -private import Cached - -/** Holds if `bb` is an entry basic block. */ -private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode } - /** * An entry basic block, that is, a basic block whose first node is * an entry node. */ -class EntryBasicBlock extends BasicBlock { - EntryBasicBlock() { entryBB(this) } -} - -/** - * An annotated exit basic block, that is, a basic block whose last node is - * an annotated exit node. - */ -class AnnotatedExitBasicBlock extends BasicBlock { - private boolean normal; - - AnnotatedExitBasicBlock() { - exists(AnnotatedExitNode n | - n = this.getANode() and - if n.isNormal() then normal = true else normal = false - ) - } - - /** Holds if this block represent a normal exit. */ - final predicate isNormal() { normal = true } -} +final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { } /** * An exit basic block, that is, a basic block whose last node is * an exit node. */ -class ExitBasicBlock extends BasicBlock { - ExitBasicBlock() { this.getLastNode() instanceof ExitNode } -} - -private module JoinBlockPredecessors { - private predicate id(Ruby::AstNode x, Ruby::AstNode y) { x = y } - - private predicate idOf(Ruby::AstNode x, int y) = equivalenceRelation(id/2)(x, y) - - int getId(JoinBlockPredecessor jbp) { - idOf(toGeneratedInclSynth(jbp.getFirstNode().(AstCfgNode).getAstNode()), result) - or - idOf(toGeneratedInclSynth(jbp.(EntryBasicBlock).getScope()), result) - } +final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { } - string getSplitString(JoinBlockPredecessor jbp) { - result = jbp.getFirstNode().(AstCfgNode).getSplitsString() - or - not exists(jbp.getFirstNode().(AstCfgNode).getSplitsString()) and - result = "" - } -} +/** + * An annotated exit basic block, that is, a basic block whose last node is + * an annotated exit node. + */ +final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { } /** A basic block with more than one predecessor. */ -class JoinBlock extends BasicBlock { - JoinBlock() { this.getFirstNode().isJoin() } - - /** - * Gets the `i`th predecessor of this join block, with respect to some - * arbitrary order. - */ - JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) } +final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock { + JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) } } /** A basic block that is an immediate predecessor of a join block. */ -class JoinBlockPredecessor extends BasicBlock { - JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock } -} - -/** A basic block that terminates in a condition, splitting the subsequent control flow. */ -class ConditionBlock extends BasicBlock { - ConditionBlock() { this.getLastNode().isCondition() } +final class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { } - /** - * Holds if basic block `succ` is immediately controlled by this basic - * block with conditional value `s`. That is, `succ` is an immediate - * successor of this block, and `succ` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ +/** + * A basic block that terminates in a condition, splitting the subsequent + * control flow. + */ +final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock { predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) { - immediatelyControls(this, succ, s) + super.immediatelyControls(succ, s) } - /** - * Holds if basic block `controlled` is controlled by this basic block with - * conditional value `s`. That is, `controlled` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ predicate controls(BasicBlock controlled, ConditionalSuccessor s) { - controls(this, controlled, s) + super.controls(controlled, s) } } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll index 643decb560b9..3046994d3d54 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -60,6 +60,14 @@ private module CfgInput implements CfgShared::InputSig { t instanceof Cfg::SuccessorTypes::RaiseSuccessor or t instanceof Cfg::SuccessorTypes::ExitSuccessor } + + private predicate id(Ruby::AstNode node1, Ruby::AstNode node2) { node1 = node2 } + + private predicate idOf(Ruby::AstNode node, int id) = equivalenceRelation(id/2)(node, id) + + predicate idOfAstNode(AstNode node, int id) { idOf(AstInternal::toGeneratedInclSynth(node), id) } + + predicate idOfCfgScope(CfgScope node, int id) { idOfAstNode(node, id) } } private module CfgSplittingInput implements CfgShared::SplittingInputSig { diff --git a/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll b/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll index f87a48a28a5b..4a4b637f3d3d 100644 --- a/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll +++ b/rust/ql/lib/codeql/rust/controlflow/BasicBlocks.qll @@ -1,263 +1,16 @@ -private import rust -private import ControlFlowGraph -private import internal.SuccessorType -private import internal.ControlFlowGraphImpl as Impl -private import codeql.rust.elements.internal.generated.Raw -private import codeql.rust.elements.internal.generated.Synth +private import internal.ControlFlowGraphImpl as CfgImpl +private import CfgImpl::BasicBlocks as BasicBlocksImpl -final class BasicBlock = BasicBlockImpl; +final class BasicBlock = BasicBlocksImpl::BasicBlock; -/** - * A basic block, that is, a maximal straight-line sequence of control flow nodes - * without branches or joins. - */ -private class BasicBlockImpl extends TBasicBlockStart { - /** Gets the CFG scope of this basic block. */ - CfgScope getScope() { result = this.getAPredecessor().getScope() } +final class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock; - /** Gets an immediate successor of this basic block, if any. */ - BasicBlock getASuccessor() { result = this.getASuccessor(_) } +final class ExitBasicBlock = BasicBlocksImpl::ExitBasicBlock; - /** Gets an immediate successor of this basic block of a given type, if any. */ - BasicBlock getASuccessor(SuccessorType t) { - result.getFirstNode() = this.getLastNode().getASuccessor(t) - } +final class AnnotatedExitBasicBlock = BasicBlocksImpl::AnnotatedExitBasicBlock; - /** Gets an immediate predecessor of this basic block, if any. */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } +final class ConditionBasicBlock = BasicBlocksImpl::ConditionBasicBlock; - /** Gets an immediate predecessor of this basic block of a given type, if any. */ - BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } +final class JoinBasicBlock = BasicBlocksImpl::JoinBasicBlock; - /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ - CfgNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } - - /** Gets a control flow node in this basic block. */ - CfgNode getANode() { result = this.getNode(_) } - - /** Gets the first control flow node in this basic block. */ - CfgNode getFirstNode() { this = TBasicBlockStart(result) } - - /** Gets the last control flow node in this basic block. */ - CfgNode getLastNode() { result = this.getNode(this.length() - 1) } - - /** Gets the length of this basic block. */ - int length() { result = strictcount(this.getANode()) } - - predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } - - predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } - - predicate dominates(BasicBlock bb) { - bb = this or - this.strictlyDominates(bb) - } - - predicate inDominanceFrontier(BasicBlock df) { - this.dominatesPredecessor(df) and - not this.strictlyDominates(df) - } - - /** - * Holds if this basic block dominates a predecessor of `df`. - */ - private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } - - BasicBlock getImmediateDominator() { bbIDominates(result, this) } - - predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } - - predicate postDominates(BasicBlock bb) { - this.strictlyPostDominates(bb) or - this = bb - } - - /** Holds if this basic block is in a loop in the control flow graph. */ - predicate inLoop() { this.getASuccessor+() = this } - - /** Gets a textual representation of this basic block. */ - string toString() { result = this.getFirstNode().toString() } - - /** Gets the location of this basic block. */ - Location getLocation() { result = this.getFirstNode().getLocation() } -} - -cached -private module Cached { - /** Internal representation of basic blocks. */ - cached - newtype TBasicBlock = TBasicBlockStart(CfgNode cfn) { startsBB(cfn) } - - /** Holds if `cfn` starts a new basic block. */ - private predicate startsBB(CfgNode cfn) { - not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor()) - or - cfn.isJoin() - or - cfn.getAPredecessor().isBranch() - } - - /** - * Holds if `succ` is a control flow successor of `pred` within - * the same basic block. - */ - private predicate intraBBSucc(CfgNode pred, CfgNode succ) { - succ = pred.getASuccessor() and - not startsBB(succ) - } - - /** - * Holds if `cfn` is the `i`th node in basic block `bb`. - * - * In other words, `i` is the shortest distance from a node `bb` - * that starts a basic block to `cfn` along the `intraBBSucc` relation. - */ - cached - predicate bbIndex(CfgNode bbStart, CfgNode cfn, int i) = - shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) - - /** - * Holds if the first node of basic block `succ` is a control flow - * successor of the last node of basic block `pred`. - */ - private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() } - - /** Holds if `dom` is an immediate dominator of `bb`. */ - cached - predicate bbIDominates(BasicBlock dom, BasicBlock bb) = - idominance(entryBB/1, succBB/2)(_, dom, bb) - - /** Holds if `pred` is a basic block predecessor of `succ`. */ - private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } - - /** Holds if `bb` is an exit basic block that represents normal exit. */ - private predicate normalExitBB(BasicBlock bb) { - bb.getANode().(Impl::AnnotatedExitNode).isNormal() - } - - /** Holds if `dom` is an immediate post-dominator of `bb`. */ - cached - predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = - idominance(normalExitBB/1, predBB/2)(_, dom, bb) - - /** - * Gets the `i`th predecessor of join block `jb`, with respect to some - * arbitrary order. - */ - cached - JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) { - result = - rank[i + 1](JoinBlockPredecessor jbp | - jbp = jb.getAPredecessor() - | - jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) - ) - } -} - -private import Cached - -/** Holds if `bb` is an entry basic block. */ -private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof Impl::EntryNode } - -/** - * An entry basic block, that is, a basic block whose first node is - * an entry node. - */ -class EntryBasicBlock extends BasicBlockImpl { - EntryBasicBlock() { entryBB(this) } - - override CfgScope getScope() { - this.getFirstNode() = any(Impl::EntryNode node | node.getScope() = result) - } -} - -/** - * An annotated exit basic block, that is, a basic block whose last node is - * an annotated exit node. - */ -class AnnotatedExitBasicBlock extends BasicBlockImpl { - private boolean normal; - - AnnotatedExitBasicBlock() { - exists(Impl::AnnotatedExitNode n | - n = this.getANode() and - if n.isNormal() then normal = true else normal = false - ) - } - - /** Holds if this block represent a normal exit. */ - final predicate isNormal() { normal = true } -} - -/** - * An exit basic block, that is, a basic block whose last node is - * an exit node. - */ -class ExitBasicBlock extends BasicBlockImpl { - ExitBasicBlock() { this.getLastNode() instanceof Impl::ExitNode } -} - -private module JoinBlockPredecessors { - private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y } - - private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y) - - // TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block - private predicate idOf(AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) } - - int getId(JoinBlockPredecessor jbp) { - idOf(jbp.getFirstNode().(Impl::AstCfgNode).getAstNode(), result) - or - idOf(jbp.(EntryBasicBlock).getScope(), result) - } - - string getSplitString(JoinBlockPredecessor jbp) { - result = jbp.getFirstNode().(Impl::AstCfgNode).getSplitsString() - or - not exists(jbp.getFirstNode().(Impl::AstCfgNode).getSplitsString()) and - result = "" - } -} - -/** A basic block with more than one predecessor. */ -class JoinBlock extends BasicBlockImpl { - JoinBlock() { this.getFirstNode().isJoin() } - - /** - * Gets the `i`th predecessor of this join block, with respect to some - * arbitrary order. - */ - JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) } -} - -/** A basic block that is an immediate predecessor of a join block. */ -class JoinBlockPredecessor extends BasicBlockImpl { - JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock } -} - -/** A basic block that terminates in a condition, splitting the subsequent control flow. */ -class ConditionBlock extends BasicBlockImpl { - ConditionBlock() { this.getLastNode().isCondition() } - - /** - * Holds if basic block `succ` is immediately controlled by this basic - * block with conditional value `s`. That is, `succ` is an immediate - * successor of this block, and `succ` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ - pragma[nomagic] - predicate immediatelyControls(BasicBlock succ, SuccessorType s) { - succ = this.getASuccessor(s) and - forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred)) - } - - /** - * Holds if basic block `controlled` is controlled by this basic block with - * conditional value `s`. That is, `controlled` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ - predicate controls(BasicBlock controlled, BooleanSuccessor s) { - exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled)) - } -} +final class JoinPredecessorBasicBlock = BasicBlocksImpl::JoinPredecessorBasicBlock; diff --git a/rust/ql/lib/codeql/rust/controlflow/ControlFlowGraph.qll b/rust/ql/lib/codeql/rust/controlflow/ControlFlowGraph.qll index 20f1af4a4afb..eb8dbdea39f7 100644 --- a/rust/ql/lib/codeql/rust/controlflow/ControlFlowGraph.qll +++ b/rust/ql/lib/codeql/rust/controlflow/ControlFlowGraph.qll @@ -1,11 +1,8 @@ /** Provides classes representing the control flow graph. */ -private import rust private import internal.ControlFlowGraphImpl -private import internal.Completion private import internal.SuccessorType private import internal.Scope as Scope -private import BasicBlocks final class CfgScope = Scope::CfgScope; @@ -25,30 +22,4 @@ final class ContinueSuccessor = ContinueSuccessorImpl; final class ReturnSuccessor = ReturnSuccessorImpl; -/** - * A control flow node. - * - * A control flow node is a node in the control flow graph (CFG). There is a - * many-to-one relationship between CFG nodes and AST nodes. - * - * Only nodes that can be reached from an entry point are included in the CFG. - */ -final class CfgNode extends Node { - /** Gets the file of this control flow node. */ - File getFile() { result = this.getLocation().getFile() } - - /** Gets a successor node of a given type, if any. */ - CfgNode getASuccessor(SuccessorType t) { result = super.getASuccessor(t) } - - /** Gets an immediate successor, if any. */ - CfgNode getASuccessor() { result = this.getASuccessor(_) } - - /** Gets an immediate predecessor node of a given flow type, if any. */ - CfgNode getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } - - /** Gets an immediate predecessor, if any. */ - CfgNode getAPredecessor() { result = this.getAPredecessor(_) } - - /** Gets the basic block that this control flow node belongs to. */ - BasicBlock getBasicBlock() { result.getANode() = this } -} +final class CfgNode = Node; diff --git a/rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll b/rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll index b999c5c78488..2039ec755464 100644 --- a/rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll +++ b/rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll @@ -3,6 +3,8 @@ import codeql.controlflow.Cfg import Completion private import Scope as Scope private import codeql.rust.controlflow.ControlFlowGraph as Cfg +private import codeql.rust.elements.internal.generated.Raw +private import codeql.rust.elements.internal.generated.Synth private module CfgInput implements InputSig { private import codeql.rust.internal.CachedStages @@ -48,6 +50,17 @@ private module CfgInput implements InputSig { /** Holds if `scope` is exited when `last` finishes with completion `c`. */ predicate scopeLast(CfgScope scope, AstNode last, Completion c) { scope.scopeLast(last, c) } + + private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y } + + private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y) + + // TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block + predicate idOfAstNode(AstNode node, int id) { + idOfDbAstNode(Synth::convertAstNodeToRaw(node), id) + } + + predicate idOfCfgScope(CfgScope node, int id) { idOfAstNode(node, id) } } private module CfgSplittingInput implements SplittingInputSig { diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll index f2078999e60d..0ed5c3ee2a17 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/SsaImpl.qll @@ -488,7 +488,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu /** Holds if the guard `guard` controls block `bb` upon evaluating to `branch`. */ predicate guardControlsBlock(Guard guard, SsaInput::BasicBlock bb, boolean branch) { - exists(ConditionBlock conditionBlock, ConditionalSuccessor s | + exists(ConditionBasicBlock conditionBlock, ConditionalSuccessor s | guard = conditionBlock.getLastNode() and s.getValue() = branch and conditionBlock.controls(bb, s) diff --git a/rust/ql/test/library-tests/controlflow/BasicBlocks.expected b/rust/ql/test/library-tests/controlflow/BasicBlocks.expected index a17ff6a28ce4..a1e189a99e09 100644 --- a/rust/ql/test/library-tests/controlflow/BasicBlocks.expected +++ b/rust/ql/test/library-tests/controlflow/BasicBlocks.expected @@ -184,9 +184,13 @@ dominates | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:39:153:48 | [boolean(false)] { ... } | | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:39:153:48 | [boolean(true)] { ... } | | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:41:153:41 | a | +| test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:154:13:154:13 | 1 | +| test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:156:13:156:13 | 0 | | test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:153:9:157:9 | if ... {...} else {...} | | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | +| test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:156:13:156:13 | 0 | | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | +| test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | test.rs:154:13:154:13 | 1 | | test.rs:153:22:153:32 | [boolean(false)] { ... } | test.rs:153:22:153:32 | [boolean(false)] { ... } | | test.rs:153:22:153:32 | [boolean(true)] { ... } | test.rs:153:22:153:32 | [boolean(true)] { ... } | | test.rs:153:24:153:24 | a | test.rs:153:22:153:32 | [boolean(false)] { ... } | @@ -197,27 +201,55 @@ dominates | test.rs:153:41:153:41 | a | test.rs:153:39:153:48 | [boolean(false)] { ... } | | test.rs:153:41:153:41 | a | test.rs:153:39:153:48 | [boolean(true)] { ... } | | test.rs:153:41:153:41 | a | test.rs:153:41:153:41 | a | +| test.rs:154:13:154:13 | 1 | test.rs:154:13:154:13 | 1 | +| test.rs:156:13:156:13 | 0 | test.rs:156:13:156:13 | 0 | | test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:160:5:169:5 | enter fn test_nested_if_match | | test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:161:9:168:9 | if ... {...} else {...} | +| test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | +| test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | | test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:162:18:162:21 | true | | test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:163:13:163:13 | _ | +| test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:165:13:165:13 | 1 | +| test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:167:13:167:13 | 0 | | test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:161:9:168:9 | if ... {...} else {...} | +| test.rs:161:13:164:9 | [boolean(false)] match a { ... } | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | +| test.rs:161:13:164:9 | [boolean(false)] match a { ... } | test.rs:167:13:167:13 | 0 | +| test.rs:161:13:164:9 | [boolean(true)] match a { ... } | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | +| test.rs:161:13:164:9 | [boolean(true)] match a { ... } | test.rs:165:13:165:13 | 1 | +| test.rs:162:18:162:21 | true | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | | test.rs:162:18:162:21 | true | test.rs:162:18:162:21 | true | +| test.rs:162:18:162:21 | true | test.rs:165:13:165:13 | 1 | +| test.rs:163:13:163:13 | _ | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | | test.rs:163:13:163:13 | _ | test.rs:163:13:163:13 | _ | +| test.rs:163:13:163:13 | _ | test.rs:167:13:167:13 | 0 | +| test.rs:165:13:165:13 | 1 | test.rs:165:13:165:13 | 1 | +| test.rs:167:13:167:13 | 0 | test.rs:167:13:167:13 | 0 | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:171:5:180:5 | enter fn test_nested_if_block | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:172:9:179:9 | if ... {...} else {...} | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:172:12:175:9 | [boolean(false)] { ... } | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:172:12:175:9 | [boolean(true)] { ... } | +| test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:176:13:176:13 | 1 | +| test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:178:13:178:13 | 0 | | test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:172:9:179:9 | if ... {...} else {...} | | test.rs:172:12:175:9 | [boolean(false)] { ... } | test.rs:172:12:175:9 | [boolean(false)] { ... } | +| test.rs:172:12:175:9 | [boolean(false)] { ... } | test.rs:178:13:178:13 | 0 | | test.rs:172:12:175:9 | [boolean(true)] { ... } | test.rs:172:12:175:9 | [boolean(true)] { ... } | +| test.rs:172:12:175:9 | [boolean(true)] { ... } | test.rs:176:13:176:13 | 1 | +| test.rs:176:13:176:13 | 1 | test.rs:176:13:176:13 | 1 | +| test.rs:178:13:178:13 | 0 | test.rs:178:13:178:13 | 0 | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:182:5:192:5 | enter fn test_if_assignment | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:184:9:191:9 | if ... {...} else {...} | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:184:12:187:9 | [boolean(false)] { ... } | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:184:12:187:9 | [boolean(true)] { ... } | +| test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:188:13:188:13 | 1 | +| test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:190:13:190:13 | 0 | | test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:184:9:191:9 | if ... {...} else {...} | | test.rs:184:12:187:9 | [boolean(false)] { ... } | test.rs:184:12:187:9 | [boolean(false)] { ... } | +| test.rs:184:12:187:9 | [boolean(false)] { ... } | test.rs:190:13:190:13 | 0 | | test.rs:184:12:187:9 | [boolean(true)] { ... } | test.rs:184:12:187:9 | [boolean(true)] { ... } | +| test.rs:184:12:187:9 | [boolean(true)] { ... } | test.rs:188:13:188:13 | 1 | +| test.rs:188:13:188:13 | 1 | test.rs:188:13:188:13 | 1 | +| test.rs:190:13:190:13 | 0 | test.rs:190:13:190:13 | 0 | | test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:194:5:205:5 | enter fn test_if_loop1 | | test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:195:9:204:9 | if ... {...} else {...} | | test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:196:13:198:13 | if ... {...} | @@ -225,6 +257,8 @@ dominates | test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:197:17:197:28 | [boolean(false)] break ... | | test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:197:17:197:28 | [boolean(true)] break ... | | test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:197:17:197:29 | ExprStmt | +| test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:201:13:201:13 | 1 | +| test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:203:13:203:13 | 0 | | test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:195:9:204:9 | if ... {...} else {...} | | test.rs:196:13:198:13 | if ... {...} | test.rs:196:13:198:13 | if ... {...} | | test.rs:196:13:198:14 | ExprStmt | test.rs:195:9:204:9 | if ... {...} else {...} | @@ -233,12 +267,20 @@ dominates | test.rs:196:13:198:14 | ExprStmt | test.rs:197:17:197:28 | [boolean(false)] break ... | | test.rs:196:13:198:14 | ExprStmt | test.rs:197:17:197:28 | [boolean(true)] break ... | | test.rs:196:13:198:14 | ExprStmt | test.rs:197:17:197:29 | ExprStmt | +| test.rs:196:13:198:14 | ExprStmt | test.rs:201:13:201:13 | 1 | +| test.rs:196:13:198:14 | ExprStmt | test.rs:203:13:203:13 | 0 | | test.rs:197:17:197:28 | [boolean(false)] break ... | test.rs:197:17:197:28 | [boolean(false)] break ... | +| test.rs:197:17:197:28 | [boolean(false)] break ... | test.rs:203:13:203:13 | 0 | | test.rs:197:17:197:28 | [boolean(true)] break ... | test.rs:197:17:197:28 | [boolean(true)] break ... | +| test.rs:197:17:197:28 | [boolean(true)] break ... | test.rs:201:13:201:13 | 1 | | test.rs:197:17:197:29 | ExprStmt | test.rs:195:9:204:9 | if ... {...} else {...} | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:28 | [boolean(false)] break ... | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:28 | [boolean(true)] break ... | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:29 | ExprStmt | +| test.rs:197:17:197:29 | ExprStmt | test.rs:201:13:201:13 | 1 | +| test.rs:197:17:197:29 | ExprStmt | test.rs:203:13:203:13 | 0 | +| test.rs:201:13:201:13 | 1 | test.rs:201:13:201:13 | 1 | +| test.rs:203:13:203:13 | 0 | test.rs:203:13:203:13 | 0 | | test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:207:5:218:5 | enter fn test_if_loop2 | | test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:208:9:217:9 | if ... {...} else {...} | | test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:209:13:211:13 | if ... {...} | @@ -246,6 +288,8 @@ dominates | test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | | test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | | test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:210:17:210:36 | ExprStmt | +| test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:214:13:214:13 | 1 | +| test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:216:13:216:13 | 0 | | test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:208:9:217:9 | if ... {...} else {...} | | test.rs:209:13:211:13 | if ... {...} | test.rs:209:13:211:13 | if ... {...} | | test.rs:209:13:211:14 | ExprStmt | test.rs:208:9:217:9 | if ... {...} else {...} | @@ -254,49 +298,75 @@ dominates | test.rs:209:13:211:14 | ExprStmt | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | | test.rs:209:13:211:14 | ExprStmt | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | | test.rs:209:13:211:14 | ExprStmt | test.rs:210:17:210:36 | ExprStmt | +| test.rs:209:13:211:14 | ExprStmt | test.rs:214:13:214:13 | 1 | +| test.rs:209:13:211:14 | ExprStmt | test.rs:216:13:216:13 | 0 | | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | +| test.rs:210:17:210:35 | [boolean(false)] break ''label ... | test.rs:216:13:216:13 | 0 | | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | +| test.rs:210:17:210:35 | [boolean(true)] break ''label ... | test.rs:214:13:214:13 | 1 | | test.rs:210:17:210:36 | ExprStmt | test.rs:208:9:217:9 | if ... {...} else {...} | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:36 | ExprStmt | +| test.rs:210:17:210:36 | ExprStmt | test.rs:214:13:214:13 | 1 | +| test.rs:210:17:210:36 | ExprStmt | test.rs:216:13:216:13 | 0 | +| test.rs:214:13:214:13 | 1 | test.rs:214:13:214:13 | 1 | +| test.rs:216:13:216:13 | 0 | test.rs:216:13:216:13 | 0 | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:220:5:228:5 | enter fn test_labelled_block | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:221:9:227:9 | if ... {...} else {...} | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | +| test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:224:13:224:13 | 1 | +| test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:226:13:226:13 | 0 | | test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:221:9:227:9 | if ... {...} else {...} | | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | +| test.rs:222:13:222:30 | [boolean(false)] break ''block ... | test.rs:226:13:226:13 | 0 | | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | +| test.rs:222:13:222:30 | [boolean(true)] break ''block ... | test.rs:224:13:224:13 | 1 | +| test.rs:224:13:224:13 | 1 | test.rs:224:13:224:13 | 1 | +| test.rs:226:13:226:13 | 0 | test.rs:226:13:226:13 | 0 | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:233:5:236:5 | enter fn test_and_operator | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:17:234:22 | [boolean(false)] ... && ... | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:17:234:22 | [boolean(true)] ... && ... | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:17:234:27 | ... && ... | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:22:234:22 | b | +| test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:27:234:27 | c | | test.rs:234:17:234:22 | [boolean(false)] ... && ... | test.rs:234:17:234:22 | [boolean(false)] ... && ... | | test.rs:234:17:234:22 | [boolean(true)] ... && ... | test.rs:234:17:234:22 | [boolean(true)] ... && ... | +| test.rs:234:17:234:22 | [boolean(true)] ... && ... | test.rs:234:27:234:27 | c | | test.rs:234:17:234:27 | ... && ... | test.rs:234:17:234:27 | ... && ... | | test.rs:234:22:234:22 | b | test.rs:234:17:234:22 | [boolean(true)] ... && ... | | test.rs:234:22:234:22 | b | test.rs:234:22:234:22 | b | +| test.rs:234:22:234:22 | b | test.rs:234:27:234:27 | c | +| test.rs:234:27:234:27 | c | test.rs:234:27:234:27 | c | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:238:5:241:5 | enter fn test_or_operator | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:17:239:27 | ... \|\| ... | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:22:239:22 | b | +| test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:27:239:27 | c | | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | +| test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | test.rs:239:27:239:27 | c | | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | | test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:17:239:27 | ... \|\| ... | | test.rs:239:22:239:22 | b | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | | test.rs:239:22:239:22 | b | test.rs:239:22:239:22 | b | +| test.rs:239:22:239:22 | b | test.rs:239:27:239:27 | c | +| test.rs:239:27:239:27 | c | test.rs:239:27:239:27 | c | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:243:5:246:5 | enter fn test_or_operator_2 | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:17:244:35 | ... \|\| ... | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:23:244:23 | b | +| test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:35:244:35 | c | | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | +| test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | test.rs:244:35:244:35 | c | | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | | test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:17:244:35 | ... \|\| ... | | test.rs:244:23:244:23 | b | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | | test.rs:244:23:244:23 | b | test.rs:244:23:244:23 | b | +| test.rs:244:23:244:23 | b | test.rs:244:35:244:35 | c | +| test.rs:244:35:244:35 | c | test.rs:244:35:244:35 | c | | test.rs:248:5:251:5 | enter fn test_not_operator | test.rs:248:5:251:5 | enter fn test_not_operator | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:253:5:259:5 | enter fn test_if_and_operator | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:9:258:9 | if ... {...} else {...} | @@ -305,15 +375,29 @@ dominates | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:12:254:22 | [boolean(false)] ... && ... | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:12:254:22 | [boolean(true)] ... && ... | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:17:254:17 | b | +| test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:22:254:22 | c | +| test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:255:13:255:16 | true | +| test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:257:13:257:17 | false | | test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:254:9:258:9 | if ... {...} else {...} | | test.rs:254:12:254:17 | [boolean(false)] ... && ... | test.rs:254:12:254:17 | [boolean(false)] ... && ... | | test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:12:254:17 | [boolean(true)] ... && ... | | test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:12:254:22 | [boolean(true)] ... && ... | +| test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:22:254:22 | c | +| test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:255:13:255:16 | true | | test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:254:12:254:22 | [boolean(false)] ... && ... | +| test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:257:13:257:17 | false | | test.rs:254:12:254:22 | [boolean(true)] ... && ... | test.rs:254:12:254:22 | [boolean(true)] ... && ... | +| test.rs:254:12:254:22 | [boolean(true)] ... && ... | test.rs:255:13:255:16 | true | | test.rs:254:17:254:17 | b | test.rs:254:12:254:17 | [boolean(true)] ... && ... | | test.rs:254:17:254:17 | b | test.rs:254:12:254:22 | [boolean(true)] ... && ... | | test.rs:254:17:254:17 | b | test.rs:254:17:254:17 | b | +| test.rs:254:17:254:17 | b | test.rs:254:22:254:22 | c | +| test.rs:254:17:254:17 | b | test.rs:255:13:255:16 | true | +| test.rs:254:22:254:22 | c | test.rs:254:12:254:22 | [boolean(true)] ... && ... | +| test.rs:254:22:254:22 | c | test.rs:254:22:254:22 | c | +| test.rs:254:22:254:22 | c | test.rs:255:13:255:16 | true | +| test.rs:255:13:255:16 | true | test.rs:255:13:255:16 | true | +| test.rs:257:13:257:17 | false | test.rs:257:13:257:17 | false | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:261:5:267:5 | enter fn test_if_or_operator | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:9:266:9 | if ... {...} else {...} | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | @@ -321,22 +405,42 @@ dominates | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:17:262:17 | b | +| test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:22:262:22 | c | +| test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:263:13:263:16 | true | +| test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:265:13:265:17 | false | | test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:9:266:9 | if ... {...} else {...} | | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | +| test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:22:262:22 | c | +| test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:265:13:265:17 | false | | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | +| test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | test.rs:265:13:265:17 | false | | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | +| test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:263:13:263:16 | true | | test.rs:262:17:262:17 | b | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | | test.rs:262:17:262:17 | b | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | | test.rs:262:17:262:17 | b | test.rs:262:17:262:17 | b | +| test.rs:262:17:262:17 | b | test.rs:262:22:262:22 | c | +| test.rs:262:17:262:17 | b | test.rs:265:13:265:17 | false | +| test.rs:262:22:262:22 | c | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | +| test.rs:262:22:262:22 | c | test.rs:262:22:262:22 | c | +| test.rs:262:22:262:22 | c | test.rs:265:13:265:17 | false | +| test.rs:263:13:263:16 | true | test.rs:263:13:263:16 | true | +| test.rs:265:13:265:17 | false | test.rs:265:13:265:17 | false | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:269:5:275:5 | enter fn test_if_not_operator | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:270:9:274:9 | if ... {...} else {...} | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:270:12:270:13 | [boolean(false)] ! ... | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:270:12:270:13 | [boolean(true)] ! ... | +| test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:271:13:271:16 | true | +| test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:273:13:273:17 | false | | test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:270:9:274:9 | if ... {...} else {...} | | test.rs:270:12:270:13 | [boolean(false)] ! ... | test.rs:270:12:270:13 | [boolean(false)] ! ... | +| test.rs:270:12:270:13 | [boolean(false)] ! ... | test.rs:273:13:273:17 | false | | test.rs:270:12:270:13 | [boolean(true)] ! ... | test.rs:270:12:270:13 | [boolean(true)] ! ... | +| test.rs:270:12:270:13 | [boolean(true)] ! ... | test.rs:271:13:271:16 | true | +| test.rs:271:13:271:16 | true | test.rs:271:13:271:16 | true | +| test.rs:273:13:273:17 | false | test.rs:273:13:273:17 | false | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:277:5:279:5 | enter fn test_and_return | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:277:5:279:5 | exit fn test_and_return (normal) | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:278:9:278:19 | ... && ... | @@ -346,11 +450,21 @@ dominates | test.rs:278:14:278:19 | return | test.rs:278:14:278:19 | return | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:281:5:286:5 | enter fn test_and_true | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:281:5:286:5 | exit fn test_and_true (normal) | +| test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:9:284:9 | if ... {...} | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:13:282:21 | [boolean(false)] ... && ... | +| test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:13:282:21 | [boolean(true)] ... && ... | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:18:282:21 | true | +| test.rs:281:5:286:5 | enter fn test_and_true | test.rs:283:13:283:21 | ExprStmt | | test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:281:5:286:5 | exit fn test_and_true (normal) | +| test.rs:282:9:284:9 | if ... {...} | test.rs:282:9:284:9 | if ... {...} | +| test.rs:282:13:282:21 | [boolean(false)] ... && ... | test.rs:282:9:284:9 | if ... {...} | | test.rs:282:13:282:21 | [boolean(false)] ... && ... | test.rs:282:13:282:21 | [boolean(false)] ... && ... | +| test.rs:282:13:282:21 | [boolean(true)] ... && ... | test.rs:282:13:282:21 | [boolean(true)] ... && ... | +| test.rs:282:13:282:21 | [boolean(true)] ... && ... | test.rs:283:13:283:21 | ExprStmt | +| test.rs:282:18:282:21 | true | test.rs:282:13:282:21 | [boolean(true)] ... && ... | | test.rs:282:18:282:21 | true | test.rs:282:18:282:21 | true | +| test.rs:282:18:282:21 | true | test.rs:283:13:283:21 | ExprStmt | +| test.rs:283:13:283:21 | ExprStmt | test.rs:283:13:283:21 | ExprStmt | | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | test.rs:292:5:294:5 | exit fn test_question_mark_operator_1 (normal) | | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | test.rs:293:32:293:32 | 4 | @@ -408,12 +522,16 @@ dominates | test.rs:326:5:331:5 | enter fn test_match_and | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | | test.rs:326:5:331:5 | enter fn test_match_and | test.rs:328:18:328:18 | a | | test.rs:326:5:331:5 | enter fn test_match_and | test.rs:329:13:329:13 | _ | +| test.rs:326:5:331:5 | enter fn test_match_and | test.rs:330:15:330:18 | cond | | test.rs:327:9:330:18 | ... && ... | test.rs:327:9:330:18 | ... && ... | | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | +| test.rs:327:10:330:9 | [boolean(true)] match r { ... } | test.rs:330:15:330:18 | cond | | test.rs:328:18:328:18 | a | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | | test.rs:328:18:328:18 | a | test.rs:328:18:328:18 | a | +| test.rs:328:18:328:18 | a | test.rs:330:15:330:18 | cond | | test.rs:329:13:329:13 | _ | test.rs:329:13:329:13 | _ | +| test.rs:330:15:330:18 | cond | test.rs:330:15:330:18 | cond | | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | test.rs:334:9:337:9 | match r { ... } | | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | test.rs:335:16:335:20 | value | @@ -490,16 +608,23 @@ dominates | test.rs:411:17:411:41 | ExprStmt | test.rs:411:17:411:41 | ExprStmt | | test.rs:421:5:423:5 | enter fn add_two | test.rs:421:5:423:5 | enter fn add_two | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:427:5:435:5 | enter fn const_block_assert | +| test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:13:431:49 | ExprStmt | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:21:431:48 | [boolean(false)] ! ... | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:21:431:48 | [boolean(true)] ! ... | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:21:431:48 | if ... {...} | +| test.rs:431:13:431:49 | ExprStmt | test.rs:431:13:431:49 | ExprStmt | | test.rs:431:13:431:49 | enter fn panic_cold_explicit | test.rs:431:13:431:49 | enter fn panic_cold_explicit | | test.rs:431:21:431:48 | [boolean(false)] ! ... | test.rs:431:21:431:48 | [boolean(false)] ! ... | +| test.rs:431:21:431:48 | [boolean(true)] ! ... | test.rs:431:13:431:49 | ExprStmt | | test.rs:431:21:431:48 | [boolean(true)] ! ... | test.rs:431:21:431:48 | [boolean(true)] ! ... | | test.rs:431:21:431:48 | if ... {...} | test.rs:431:21:431:48 | if ... {...} | | test.rs:437:5:446:5 | enter fn const_block_panic | test.rs:437:5:446:5 | enter fn const_block_panic | +| test.rs:437:5:446:5 | enter fn const_block_panic | test.rs:439:9:444:9 | if false {...} | +| test.rs:439:9:444:9 | if false {...} | test.rs:439:9:444:9 | if false {...} | | test.rs:442:17:442:24 | enter fn panic_cold_explicit | test.rs:442:17:442:24 | enter fn panic_cold_explicit | | test.rs:449:1:454:1 | enter fn dead_code | test.rs:449:1:454:1 | enter fn dead_code | +| test.rs:449:1:454:1 | enter fn dead_code | test.rs:451:9:451:17 | ExprStmt | +| test.rs:451:9:451:17 | ExprStmt | test.rs:451:9:451:17 | ExprStmt | | test.rs:456:1:456:16 | enter fn do_thing | test.rs:456:1:456:16 | enter fn do_thing | | test.rs:458:1:460:1 | enter fn condition_not_met | test.rs:458:1:460:1 | enter fn condition_not_met | | test.rs:462:1:462:21 | enter fn do_next_thing | test.rs:462:1:462:21 | enter fn do_next_thing | @@ -671,6 +796,8 @@ postDominance | test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:153:39:153:48 | [boolean(false)] { ... } | | test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:153:39:153:48 | [boolean(true)] { ... } | | test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:153:41:153:41 | a | +| test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:154:13:154:13 | 1 | +| test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:156:13:156:13 | 0 | | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:153:22:153:32 | [boolean(false)] { ... } | | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:153:39:153:48 | [boolean(false)] { ... } | @@ -683,27 +810,61 @@ postDominance | test.rs:153:39:153:48 | [boolean(false)] { ... } | test.rs:153:39:153:48 | [boolean(false)] { ... } | | test.rs:153:39:153:48 | [boolean(true)] { ... } | test.rs:153:39:153:48 | [boolean(true)] { ... } | | test.rs:153:41:153:41 | a | test.rs:153:41:153:41 | a | +| test.rs:154:13:154:13 | 1 | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | +| test.rs:154:13:154:13 | 1 | test.rs:153:22:153:32 | [boolean(true)] { ... } | +| test.rs:154:13:154:13 | 1 | test.rs:153:39:153:48 | [boolean(true)] { ... } | +| test.rs:154:13:154:13 | 1 | test.rs:154:13:154:13 | 1 | +| test.rs:156:13:156:13 | 0 | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | +| test.rs:156:13:156:13 | 0 | test.rs:153:22:153:32 | [boolean(false)] { ... } | +| test.rs:156:13:156:13 | 0 | test.rs:153:39:153:48 | [boolean(false)] { ... } | +| test.rs:156:13:156:13 | 0 | test.rs:156:13:156:13 | 0 | | test.rs:160:5:169:5 | enter fn test_nested_if_match | test.rs:160:5:169:5 | enter fn test_nested_if_match | | test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:160:5:169:5 | enter fn test_nested_if_match | | test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:161:9:168:9 | if ... {...} else {...} | +| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | +| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | | test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:162:18:162:21 | true | | test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:163:13:163:13 | _ | +| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:165:13:165:13 | 1 | +| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:167:13:167:13 | 0 | +| test.rs:161:13:164:9 | [boolean(false)] match a { ... } | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | +| test.rs:161:13:164:9 | [boolean(false)] match a { ... } | test.rs:163:13:163:13 | _ | +| test.rs:161:13:164:9 | [boolean(true)] match a { ... } | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | +| test.rs:161:13:164:9 | [boolean(true)] match a { ... } | test.rs:162:18:162:21 | true | | test.rs:162:18:162:21 | true | test.rs:162:18:162:21 | true | | test.rs:163:13:163:13 | _ | test.rs:163:13:163:13 | _ | +| test.rs:165:13:165:13 | 1 | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | +| test.rs:165:13:165:13 | 1 | test.rs:162:18:162:21 | true | +| test.rs:165:13:165:13 | 1 | test.rs:165:13:165:13 | 1 | +| test.rs:167:13:167:13 | 0 | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | +| test.rs:167:13:167:13 | 0 | test.rs:163:13:163:13 | _ | +| test.rs:167:13:167:13 | 0 | test.rs:167:13:167:13 | 0 | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:171:5:180:5 | enter fn test_nested_if_block | | test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:171:5:180:5 | enter fn test_nested_if_block | | test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:172:9:179:9 | if ... {...} else {...} | | test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:172:12:175:9 | [boolean(false)] { ... } | | test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:172:12:175:9 | [boolean(true)] { ... } | +| test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:176:13:176:13 | 1 | +| test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:178:13:178:13 | 0 | | test.rs:172:12:175:9 | [boolean(false)] { ... } | test.rs:172:12:175:9 | [boolean(false)] { ... } | | test.rs:172:12:175:9 | [boolean(true)] { ... } | test.rs:172:12:175:9 | [boolean(true)] { ... } | +| test.rs:176:13:176:13 | 1 | test.rs:172:12:175:9 | [boolean(true)] { ... } | +| test.rs:176:13:176:13 | 1 | test.rs:176:13:176:13 | 1 | +| test.rs:178:13:178:13 | 0 | test.rs:172:12:175:9 | [boolean(false)] { ... } | +| test.rs:178:13:178:13 | 0 | test.rs:178:13:178:13 | 0 | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:182:5:192:5 | enter fn test_if_assignment | | test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:182:5:192:5 | enter fn test_if_assignment | | test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:184:9:191:9 | if ... {...} else {...} | | test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:184:12:187:9 | [boolean(false)] { ... } | | test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:184:12:187:9 | [boolean(true)] { ... } | +| test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:188:13:188:13 | 1 | +| test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:190:13:190:13 | 0 | | test.rs:184:12:187:9 | [boolean(false)] { ... } | test.rs:184:12:187:9 | [boolean(false)] { ... } | | test.rs:184:12:187:9 | [boolean(true)] { ... } | test.rs:184:12:187:9 | [boolean(true)] { ... } | +| test.rs:188:13:188:13 | 1 | test.rs:184:12:187:9 | [boolean(true)] { ... } | +| test.rs:188:13:188:13 | 1 | test.rs:188:13:188:13 | 1 | +| test.rs:190:13:190:13 | 0 | test.rs:184:12:187:9 | [boolean(false)] { ... } | +| test.rs:190:13:190:13 | 0 | test.rs:190:13:190:13 | 0 | | test.rs:194:5:205:5 | enter fn test_if_loop1 | test.rs:194:5:205:5 | enter fn test_if_loop1 | | test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:194:5:205:5 | enter fn test_if_loop1 | | test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:195:9:204:9 | if ... {...} else {...} | @@ -712,6 +873,8 @@ postDominance | test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:197:17:197:28 | [boolean(false)] break ... | | test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:197:17:197:28 | [boolean(true)] break ... | | test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:197:17:197:29 | ExprStmt | +| test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:201:13:201:13 | 1 | +| test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:203:13:203:13 | 0 | | test.rs:196:13:198:13 | if ... {...} | test.rs:196:13:198:13 | if ... {...} | | test.rs:196:13:198:14 | ExprStmt | test.rs:194:5:205:5 | enter fn test_if_loop1 | | test.rs:196:13:198:14 | ExprStmt | test.rs:196:13:198:13 | if ... {...} | @@ -722,6 +885,10 @@ postDominance | test.rs:197:17:197:29 | ExprStmt | test.rs:196:13:198:13 | if ... {...} | | test.rs:197:17:197:29 | ExprStmt | test.rs:196:13:198:14 | ExprStmt | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:29 | ExprStmt | +| test.rs:201:13:201:13 | 1 | test.rs:197:17:197:28 | [boolean(true)] break ... | +| test.rs:201:13:201:13 | 1 | test.rs:201:13:201:13 | 1 | +| test.rs:203:13:203:13 | 0 | test.rs:197:17:197:28 | [boolean(false)] break ... | +| test.rs:203:13:203:13 | 0 | test.rs:203:13:203:13 | 0 | | test.rs:207:5:218:5 | enter fn test_if_loop2 | test.rs:207:5:218:5 | enter fn test_if_loop2 | | test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:207:5:218:5 | enter fn test_if_loop2 | | test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:208:9:217:9 | if ... {...} else {...} | @@ -730,6 +897,8 @@ postDominance | test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | | test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | | test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:210:17:210:36 | ExprStmt | +| test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:214:13:214:13 | 1 | +| test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:216:13:216:13 | 0 | | test.rs:209:13:211:13 | if ... {...} | test.rs:209:13:211:13 | if ... {...} | | test.rs:209:13:211:14 | ExprStmt | test.rs:207:5:218:5 | enter fn test_if_loop2 | | test.rs:209:13:211:14 | ExprStmt | test.rs:209:13:211:13 | if ... {...} | @@ -740,13 +909,23 @@ postDominance | test.rs:210:17:210:36 | ExprStmt | test.rs:209:13:211:13 | if ... {...} | | test.rs:210:17:210:36 | ExprStmt | test.rs:209:13:211:14 | ExprStmt | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:36 | ExprStmt | +| test.rs:214:13:214:13 | 1 | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | +| test.rs:214:13:214:13 | 1 | test.rs:214:13:214:13 | 1 | +| test.rs:216:13:216:13 | 0 | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | +| test.rs:216:13:216:13 | 0 | test.rs:216:13:216:13 | 0 | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:220:5:228:5 | enter fn test_labelled_block | | test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:220:5:228:5 | enter fn test_labelled_block | | test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:221:9:227:9 | if ... {...} else {...} | | test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | | test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | +| test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:224:13:224:13 | 1 | +| test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:226:13:226:13 | 0 | | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | +| test.rs:224:13:224:13 | 1 | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | +| test.rs:224:13:224:13 | 1 | test.rs:224:13:224:13 | 1 | +| test.rs:226:13:226:13 | 0 | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | +| test.rs:226:13:226:13 | 0 | test.rs:226:13:226:13 | 0 | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:233:5:236:5 | enter fn test_and_operator | | test.rs:234:17:234:22 | [boolean(false)] ... && ... | test.rs:234:17:234:22 | [boolean(false)] ... && ... | | test.rs:234:17:234:22 | [boolean(true)] ... && ... | test.rs:234:17:234:22 | [boolean(true)] ... && ... | @@ -755,7 +934,10 @@ postDominance | test.rs:234:17:234:27 | ... && ... | test.rs:234:17:234:22 | [boolean(true)] ... && ... | | test.rs:234:17:234:27 | ... && ... | test.rs:234:17:234:27 | ... && ... | | test.rs:234:17:234:27 | ... && ... | test.rs:234:22:234:22 | b | +| test.rs:234:17:234:27 | ... && ... | test.rs:234:27:234:27 | c | | test.rs:234:22:234:22 | b | test.rs:234:22:234:22 | b | +| test.rs:234:27:234:27 | c | test.rs:234:17:234:22 | [boolean(true)] ... && ... | +| test.rs:234:27:234:27 | c | test.rs:234:27:234:27 | c | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:238:5:241:5 | enter fn test_or_operator | | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | @@ -764,7 +946,10 @@ postDominance | test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | | test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:17:239:27 | ... \|\| ... | | test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:22:239:22 | b | +| test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:27:239:27 | c | | test.rs:239:22:239:22 | b | test.rs:239:22:239:22 | b | +| test.rs:239:27:239:27 | c | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | +| test.rs:239:27:239:27 | c | test.rs:239:27:239:27 | c | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:243:5:246:5 | enter fn test_or_operator_2 | | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | @@ -773,7 +958,10 @@ postDominance | test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | | test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:17:244:35 | ... \|\| ... | | test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:23:244:23 | b | +| test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:35:244:35 | c | | test.rs:244:23:244:23 | b | test.rs:244:23:244:23 | b | +| test.rs:244:35:244:35 | c | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | +| test.rs:244:35:244:35 | c | test.rs:244:35:244:35 | c | | test.rs:248:5:251:5 | enter fn test_not_operator | test.rs:248:5:251:5 | enter fn test_not_operator | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:253:5:259:5 | enter fn test_if_and_operator | | test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:253:5:259:5 | enter fn test_if_and_operator | @@ -783,12 +971,22 @@ postDominance | test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:254:12:254:22 | [boolean(false)] ... && ... | | test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:254:12:254:22 | [boolean(true)] ... && ... | | test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:254:17:254:17 | b | +| test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:254:22:254:22 | c | +| test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:255:13:255:16 | true | +| test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:257:13:257:17 | false | | test.rs:254:12:254:17 | [boolean(false)] ... && ... | test.rs:254:12:254:17 | [boolean(false)] ... && ... | | test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:12:254:17 | [boolean(true)] ... && ... | | test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:254:12:254:17 | [boolean(false)] ... && ... | | test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:254:12:254:22 | [boolean(false)] ... && ... | | test.rs:254:12:254:22 | [boolean(true)] ... && ... | test.rs:254:12:254:22 | [boolean(true)] ... && ... | | test.rs:254:17:254:17 | b | test.rs:254:17:254:17 | b | +| test.rs:254:22:254:22 | c | test.rs:254:12:254:17 | [boolean(true)] ... && ... | +| test.rs:254:22:254:22 | c | test.rs:254:22:254:22 | c | +| test.rs:255:13:255:16 | true | test.rs:254:12:254:22 | [boolean(true)] ... && ... | +| test.rs:255:13:255:16 | true | test.rs:255:13:255:16 | true | +| test.rs:257:13:257:17 | false | test.rs:254:12:254:17 | [boolean(false)] ... && ... | +| test.rs:257:13:257:17 | false | test.rs:254:12:254:22 | [boolean(false)] ... && ... | +| test.rs:257:13:257:17 | false | test.rs:257:13:257:17 | false | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:261:5:267:5 | enter fn test_if_or_operator | | test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:261:5:267:5 | enter fn test_if_or_operator | | test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:9:266:9 | if ... {...} else {...} | @@ -797,19 +995,35 @@ postDominance | test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | | test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | | test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:17:262:17 | b | +| test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:22:262:22 | c | +| test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:263:13:263:16 | true | +| test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:265:13:265:17 | false | | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | | test.rs:262:17:262:17 | b | test.rs:262:17:262:17 | b | +| test.rs:262:22:262:22 | c | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | +| test.rs:262:22:262:22 | c | test.rs:262:22:262:22 | c | +| test.rs:263:13:263:16 | true | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | +| test.rs:263:13:263:16 | true | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | +| test.rs:263:13:263:16 | true | test.rs:263:13:263:16 | true | +| test.rs:265:13:265:17 | false | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | +| test.rs:265:13:265:17 | false | test.rs:265:13:265:17 | false | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:269:5:275:5 | enter fn test_if_not_operator | | test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:269:5:275:5 | enter fn test_if_not_operator | | test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:270:9:274:9 | if ... {...} else {...} | | test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:270:12:270:13 | [boolean(false)] ! ... | | test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:270:12:270:13 | [boolean(true)] ! ... | +| test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:271:13:271:16 | true | +| test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:273:13:273:17 | false | | test.rs:270:12:270:13 | [boolean(false)] ! ... | test.rs:270:12:270:13 | [boolean(false)] ! ... | | test.rs:270:12:270:13 | [boolean(true)] ! ... | test.rs:270:12:270:13 | [boolean(true)] ! ... | +| test.rs:271:13:271:16 | true | test.rs:270:12:270:13 | [boolean(true)] ! ... | +| test.rs:271:13:271:16 | true | test.rs:271:13:271:16 | true | +| test.rs:273:13:273:17 | false | test.rs:270:12:270:13 | [boolean(false)] ! ... | +| test.rs:273:13:273:17 | false | test.rs:273:13:273:17 | false | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:277:5:279:5 | enter fn test_and_return | | test.rs:277:5:279:5 | exit fn test_and_return (normal) | test.rs:277:5:279:5 | enter fn test_and_return | | test.rs:277:5:279:5 | exit fn test_and_return (normal) | test.rs:277:5:279:5 | exit fn test_and_return (normal) | @@ -820,10 +1034,20 @@ postDominance | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:281:5:286:5 | enter fn test_and_true | | test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:281:5:286:5 | enter fn test_and_true | | test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:281:5:286:5 | exit fn test_and_true (normal) | +| test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:282:9:284:9 | if ... {...} | | test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:282:13:282:21 | [boolean(false)] ... && ... | +| test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:282:13:282:21 | [boolean(true)] ... && ... | | test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:282:18:282:21 | true | +| test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:283:13:283:21 | ExprStmt | +| test.rs:282:9:284:9 | if ... {...} | test.rs:282:9:284:9 | if ... {...} | +| test.rs:282:9:284:9 | if ... {...} | test.rs:282:13:282:21 | [boolean(false)] ... && ... | | test.rs:282:13:282:21 | [boolean(false)] ... && ... | test.rs:282:13:282:21 | [boolean(false)] ... && ... | +| test.rs:282:13:282:21 | [boolean(true)] ... && ... | test.rs:282:13:282:21 | [boolean(true)] ... && ... | +| test.rs:282:13:282:21 | [boolean(true)] ... && ... | test.rs:282:18:282:21 | true | | test.rs:282:18:282:21 | true | test.rs:282:18:282:21 | true | +| test.rs:283:13:283:21 | ExprStmt | test.rs:282:13:282:21 | [boolean(true)] ... && ... | +| test.rs:283:13:283:21 | ExprStmt | test.rs:282:18:282:21 | true | +| test.rs:283:13:283:21 | ExprStmt | test.rs:283:13:283:21 | ExprStmt | | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | | test.rs:292:5:294:5 | exit fn test_question_mark_operator_1 (normal) | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | | test.rs:292:5:294:5 | exit fn test_question_mark_operator_1 (normal) | test.rs:292:5:294:5 | exit fn test_question_mark_operator_1 (normal) | @@ -879,11 +1103,14 @@ postDominance | test.rs:327:9:330:18 | ... && ... | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | | test.rs:327:9:330:18 | ... && ... | test.rs:328:18:328:18 | a | | test.rs:327:9:330:18 | ... && ... | test.rs:329:13:329:13 | _ | +| test.rs:327:9:330:18 | ... && ... | test.rs:330:15:330:18 | cond | | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | test.rs:329:13:329:13 | _ | | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | | test.rs:328:18:328:18 | a | test.rs:328:18:328:18 | a | | test.rs:329:13:329:13 | _ | test.rs:329:13:329:13 | _ | +| test.rs:330:15:330:18 | cond | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | +| test.rs:330:15:330:18 | cond | test.rs:330:15:330:18 | cond | | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | | test.rs:334:9:337:9 | match r { ... } | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | | test.rs:334:9:337:9 | match r { ... } | test.rs:334:9:337:9 | match r { ... } | @@ -943,16 +1170,23 @@ postDominance | test.rs:411:17:411:41 | ExprStmt | test.rs:411:17:411:41 | ExprStmt | | test.rs:421:5:423:5 | enter fn add_two | test.rs:421:5:423:5 | enter fn add_two | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:427:5:435:5 | enter fn const_block_assert | +| test.rs:431:13:431:49 | ExprStmt | test.rs:431:13:431:49 | ExprStmt | +| test.rs:431:13:431:49 | ExprStmt | test.rs:431:21:431:48 | [boolean(true)] ! ... | | test.rs:431:13:431:49 | enter fn panic_cold_explicit | test.rs:431:13:431:49 | enter fn panic_cold_explicit | | test.rs:431:21:431:48 | [boolean(false)] ! ... | test.rs:431:21:431:48 | [boolean(false)] ! ... | | test.rs:431:21:431:48 | [boolean(true)] ! ... | test.rs:431:21:431:48 | [boolean(true)] ! ... | | test.rs:431:21:431:48 | if ... {...} | test.rs:427:5:435:5 | enter fn const_block_assert | +| test.rs:431:21:431:48 | if ... {...} | test.rs:431:13:431:49 | ExprStmt | | test.rs:431:21:431:48 | if ... {...} | test.rs:431:21:431:48 | [boolean(false)] ! ... | | test.rs:431:21:431:48 | if ... {...} | test.rs:431:21:431:48 | [boolean(true)] ! ... | | test.rs:431:21:431:48 | if ... {...} | test.rs:431:21:431:48 | if ... {...} | | test.rs:437:5:446:5 | enter fn const_block_panic | test.rs:437:5:446:5 | enter fn const_block_panic | +| test.rs:439:9:444:9 | if false {...} | test.rs:437:5:446:5 | enter fn const_block_panic | +| test.rs:439:9:444:9 | if false {...} | test.rs:439:9:444:9 | if false {...} | | test.rs:442:17:442:24 | enter fn panic_cold_explicit | test.rs:442:17:442:24 | enter fn panic_cold_explicit | | test.rs:449:1:454:1 | enter fn dead_code | test.rs:449:1:454:1 | enter fn dead_code | +| test.rs:451:9:451:17 | ExprStmt | test.rs:449:1:454:1 | enter fn dead_code | +| test.rs:451:9:451:17 | ExprStmt | test.rs:451:9:451:17 | ExprStmt | | test.rs:456:1:456:16 | enter fn do_thing | test.rs:456:1:456:16 | enter fn do_thing | | test.rs:458:1:460:1 | enter fn condition_not_met | test.rs:458:1:460:1 | enter fn condition_not_met | | test.rs:462:1:462:21 | enter fn do_next_thing | test.rs:462:1:462:21 | enter fn do_next_thing | @@ -1042,63 +1276,93 @@ immediateDominator | test.rs:153:39:153:48 | [boolean(false)] { ... } | test.rs:153:41:153:41 | a | | test.rs:153:39:153:48 | [boolean(true)] { ... } | test.rs:153:41:153:41 | a | | test.rs:153:41:153:41 | a | test.rs:152:5:158:5 | enter fn test_nested_if | +| test.rs:154:13:154:13 | 1 | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | +| test.rs:156:13:156:13 | 0 | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | | test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:160:5:169:5 | enter fn test_nested_if_match | +| test.rs:161:13:164:9 | [boolean(false)] match a { ... } | test.rs:163:13:163:13 | _ | +| test.rs:161:13:164:9 | [boolean(true)] match a { ... } | test.rs:162:18:162:21 | true | | test.rs:162:18:162:21 | true | test.rs:160:5:169:5 | enter fn test_nested_if_match | | test.rs:163:13:163:13 | _ | test.rs:160:5:169:5 | enter fn test_nested_if_match | +| test.rs:165:13:165:13 | 1 | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | +| test.rs:167:13:167:13 | 0 | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | | test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:171:5:180:5 | enter fn test_nested_if_block | | test.rs:172:12:175:9 | [boolean(false)] { ... } | test.rs:171:5:180:5 | enter fn test_nested_if_block | | test.rs:172:12:175:9 | [boolean(true)] { ... } | test.rs:171:5:180:5 | enter fn test_nested_if_block | +| test.rs:176:13:176:13 | 1 | test.rs:172:12:175:9 | [boolean(true)] { ... } | +| test.rs:178:13:178:13 | 0 | test.rs:172:12:175:9 | [boolean(false)] { ... } | | test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:182:5:192:5 | enter fn test_if_assignment | | test.rs:184:12:187:9 | [boolean(false)] { ... } | test.rs:182:5:192:5 | enter fn test_if_assignment | | test.rs:184:12:187:9 | [boolean(true)] { ... } | test.rs:182:5:192:5 | enter fn test_if_assignment | +| test.rs:188:13:188:13 | 1 | test.rs:184:12:187:9 | [boolean(true)] { ... } | +| test.rs:190:13:190:13 | 0 | test.rs:184:12:187:9 | [boolean(false)] { ... } | | test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:197:17:197:29 | ExprStmt | | test.rs:196:13:198:13 | if ... {...} | test.rs:196:13:198:14 | ExprStmt | | test.rs:196:13:198:14 | ExprStmt | test.rs:194:5:205:5 | enter fn test_if_loop1 | | test.rs:197:17:197:28 | [boolean(false)] break ... | test.rs:197:17:197:29 | ExprStmt | | test.rs:197:17:197:28 | [boolean(true)] break ... | test.rs:197:17:197:29 | ExprStmt | | test.rs:197:17:197:29 | ExprStmt | test.rs:196:13:198:14 | ExprStmt | +| test.rs:201:13:201:13 | 1 | test.rs:197:17:197:28 | [boolean(true)] break ... | +| test.rs:203:13:203:13 | 0 | test.rs:197:17:197:28 | [boolean(false)] break ... | | test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:210:17:210:36 | ExprStmt | | test.rs:209:13:211:13 | if ... {...} | test.rs:209:13:211:14 | ExprStmt | | test.rs:209:13:211:14 | ExprStmt | test.rs:207:5:218:5 | enter fn test_if_loop2 | | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | test.rs:210:17:210:36 | ExprStmt | | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | test.rs:210:17:210:36 | ExprStmt | | test.rs:210:17:210:36 | ExprStmt | test.rs:209:13:211:14 | ExprStmt | +| test.rs:214:13:214:13 | 1 | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | +| test.rs:216:13:216:13 | 0 | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | | test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:220:5:228:5 | enter fn test_labelled_block | | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | test.rs:220:5:228:5 | enter fn test_labelled_block | | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | test.rs:220:5:228:5 | enter fn test_labelled_block | +| test.rs:224:13:224:13 | 1 | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | +| test.rs:226:13:226:13 | 0 | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | | test.rs:234:17:234:22 | [boolean(false)] ... && ... | test.rs:233:5:236:5 | enter fn test_and_operator | | test.rs:234:17:234:22 | [boolean(true)] ... && ... | test.rs:234:22:234:22 | b | | test.rs:234:17:234:27 | ... && ... | test.rs:233:5:236:5 | enter fn test_and_operator | | test.rs:234:22:234:22 | b | test.rs:233:5:236:5 | enter fn test_and_operator | +| test.rs:234:27:234:27 | c | test.rs:234:17:234:22 | [boolean(true)] ... && ... | | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | test.rs:239:22:239:22 | b | | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | test.rs:238:5:241:5 | enter fn test_or_operator | | test.rs:239:17:239:27 | ... \|\| ... | test.rs:238:5:241:5 | enter fn test_or_operator | | test.rs:239:22:239:22 | b | test.rs:238:5:241:5 | enter fn test_or_operator | +| test.rs:239:27:239:27 | c | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | test.rs:244:23:244:23 | b | | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | test.rs:243:5:246:5 | enter fn test_or_operator_2 | | test.rs:244:17:244:35 | ... \|\| ... | test.rs:243:5:246:5 | enter fn test_or_operator_2 | | test.rs:244:23:244:23 | b | test.rs:243:5:246:5 | enter fn test_or_operator_2 | +| test.rs:244:35:244:35 | c | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | | test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:253:5:259:5 | enter fn test_if_and_operator | | test.rs:254:12:254:17 | [boolean(false)] ... && ... | test.rs:253:5:259:5 | enter fn test_if_and_operator | | test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:17:254:17 | b | | test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:253:5:259:5 | enter fn test_if_and_operator | -| test.rs:254:12:254:22 | [boolean(true)] ... && ... | test.rs:254:12:254:17 | [boolean(true)] ... && ... | +| test.rs:254:12:254:22 | [boolean(true)] ... && ... | test.rs:254:22:254:22 | c | | test.rs:254:17:254:17 | b | test.rs:253:5:259:5 | enter fn test_if_and_operator | +| test.rs:254:22:254:22 | c | test.rs:254:12:254:17 | [boolean(true)] ... && ... | +| test.rs:255:13:255:16 | true | test.rs:254:12:254:22 | [boolean(true)] ... && ... | +| test.rs:257:13:257:17 | false | test.rs:254:12:254:22 | [boolean(false)] ... && ... | | test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:261:5:267:5 | enter fn test_if_or_operator | | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:17:262:17 | b | | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | test.rs:261:5:267:5 | enter fn test_if_or_operator | -| test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | +| test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | test.rs:262:22:262:22 | c | | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:261:5:267:5 | enter fn test_if_or_operator | | test.rs:262:17:262:17 | b | test.rs:261:5:267:5 | enter fn test_if_or_operator | +| test.rs:262:22:262:22 | c | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | +| test.rs:263:13:263:16 | true | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | +| test.rs:265:13:265:17 | false | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | | test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:269:5:275:5 | enter fn test_if_not_operator | | test.rs:270:12:270:13 | [boolean(false)] ! ... | test.rs:269:5:275:5 | enter fn test_if_not_operator | | test.rs:270:12:270:13 | [boolean(true)] ! ... | test.rs:269:5:275:5 | enter fn test_if_not_operator | +| test.rs:271:13:271:16 | true | test.rs:270:12:270:13 | [boolean(true)] ! ... | +| test.rs:273:13:273:17 | false | test.rs:270:12:270:13 | [boolean(false)] ! ... | | test.rs:277:5:279:5 | exit fn test_and_return (normal) | test.rs:277:5:279:5 | enter fn test_and_return | | test.rs:278:9:278:19 | ... && ... | test.rs:277:5:279:5 | enter fn test_and_return | | test.rs:278:14:278:19 | return | test.rs:277:5:279:5 | enter fn test_and_return | | test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:281:5:286:5 | enter fn test_and_true | +| test.rs:282:9:284:9 | if ... {...} | test.rs:282:13:282:21 | [boolean(false)] ... && ... | | test.rs:282:13:282:21 | [boolean(false)] ... && ... | test.rs:281:5:286:5 | enter fn test_and_true | +| test.rs:282:13:282:21 | [boolean(true)] ... && ... | test.rs:282:18:282:21 | true | | test.rs:282:18:282:21 | true | test.rs:281:5:286:5 | enter fn test_and_true | +| test.rs:283:13:283:21 | ExprStmt | test.rs:282:13:282:21 | [boolean(true)] ... && ... | | test.rs:292:5:294:5 | exit fn test_question_mark_operator_1 (normal) | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | | test.rs:293:32:293:32 | 4 | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | | test.rs:296:5:301:5 | exit fn test_question_mark_operator_2 (normal) | test.rs:296:5:301:5 | enter fn test_question_mark_operator_2 | @@ -1123,6 +1387,7 @@ immediateDominator | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | test.rs:328:18:328:18 | a | | test.rs:328:18:328:18 | a | test.rs:326:5:331:5 | enter fn test_match_and | | test.rs:329:13:329:13 | _ | test.rs:326:5:331:5 | enter fn test_match_and | +| test.rs:330:15:330:18 | cond | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | | test.rs:334:9:337:9 | match r { ... } | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | | test.rs:335:16:335:20 | value | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | | test.rs:336:13:336:22 | Err(...) | test.rs:333:5:338:5 | enter fn test_match_with_no_arms | @@ -1146,9 +1411,12 @@ immediateDominator | test.rs:409:28:414:9 | exit { ... } (normal) | test.rs:409:28:414:9 | enter { ... } | | test.rs:410:13:412:13 | if b {...} | test.rs:409:28:414:9 | enter { ... } | | test.rs:411:17:411:41 | ExprStmt | test.rs:409:28:414:9 | enter { ... } | +| test.rs:431:13:431:49 | ExprStmt | test.rs:431:21:431:48 | [boolean(true)] ! ... | | test.rs:431:21:431:48 | [boolean(false)] ! ... | test.rs:427:5:435:5 | enter fn const_block_assert | | test.rs:431:21:431:48 | [boolean(true)] ! ... | test.rs:427:5:435:5 | enter fn const_block_assert | | test.rs:431:21:431:48 | if ... {...} | test.rs:427:5:435:5 | enter fn const_block_assert | +| test.rs:439:9:444:9 | if false {...} | test.rs:437:5:446:5 | enter fn const_block_panic | +| test.rs:451:9:451:17 | ExprStmt | test.rs:449:1:454:1 | enter fn dead_code | | test.rs:467:18:478:5 | 'block: { ... } | test.rs:466:1:480:1 | enter fn labelled_block1 | | test.rs:469:9:471:9 | if ... {...} | test.rs:466:1:480:1 | enter fn labelled_block1 | | test.rs:470:13:470:27 | ExprStmt | test.rs:466:1:480:1 | enter fn labelled_block1 | @@ -1207,68 +1475,146 @@ controls | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:39:153:48 | [boolean(false)] { ... } | false | | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:39:153:48 | [boolean(true)] { ... } | false | | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:41:153:41 | a | false | +| test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:156:13:156:13 | 0 | false | +| test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | test.rs:154:13:154:13 | 1 | true | | test.rs:153:24:153:24 | a | test.rs:153:22:153:32 | [boolean(false)] { ... } | false | | test.rs:153:24:153:24 | a | test.rs:153:22:153:32 | [boolean(true)] { ... } | true | | test.rs:153:41:153:41 | a | test.rs:153:39:153:48 | [boolean(false)] { ... } | false | | test.rs:153:41:153:41 | a | test.rs:153:39:153:48 | [boolean(true)] { ... } | true | +| test.rs:161:13:164:9 | [boolean(false)] match a { ... } | test.rs:167:13:167:13 | 0 | false | +| test.rs:161:13:164:9 | [boolean(true)] match a { ... } | test.rs:165:13:165:13 | 1 | true | +| test.rs:162:18:162:21 | true | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | true | +| test.rs:162:18:162:21 | true | test.rs:165:13:165:13 | 1 | true | +| test.rs:163:13:163:13 | _ | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | false | +| test.rs:163:13:163:13 | _ | test.rs:167:13:167:13 | 0 | false | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:172:12:175:9 | [boolean(false)] { ... } | false | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:172:12:175:9 | [boolean(true)] { ... } | true | +| test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:176:13:176:13 | 1 | true | +| test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:178:13:178:13 | 0 | false | +| test.rs:172:12:175:9 | [boolean(false)] { ... } | test.rs:178:13:178:13 | 0 | false | +| test.rs:172:12:175:9 | [boolean(true)] { ... } | test.rs:176:13:176:13 | 1 | true | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:184:12:187:9 | [boolean(false)] { ... } | false | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:184:12:187:9 | [boolean(true)] { ... } | true | +| test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:188:13:188:13 | 1 | true | +| test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:190:13:190:13 | 0 | false | +| test.rs:184:12:187:9 | [boolean(false)] { ... } | test.rs:190:13:190:13 | 0 | false | +| test.rs:184:12:187:9 | [boolean(true)] { ... } | test.rs:188:13:188:13 | 1 | true | | test.rs:196:13:198:14 | ExprStmt | test.rs:195:9:204:9 | if ... {...} else {...} | true | | test.rs:196:13:198:14 | ExprStmt | test.rs:196:13:198:13 | if ... {...} | false | | test.rs:196:13:198:14 | ExprStmt | test.rs:197:17:197:28 | [boolean(false)] break ... | true | | test.rs:196:13:198:14 | ExprStmt | test.rs:197:17:197:28 | [boolean(true)] break ... | true | | test.rs:196:13:198:14 | ExprStmt | test.rs:197:17:197:29 | ExprStmt | true | +| test.rs:196:13:198:14 | ExprStmt | test.rs:201:13:201:13 | 1 | true | +| test.rs:196:13:198:14 | ExprStmt | test.rs:203:13:203:13 | 0 | true | +| test.rs:197:17:197:28 | [boolean(false)] break ... | test.rs:203:13:203:13 | 0 | false | +| test.rs:197:17:197:28 | [boolean(true)] break ... | test.rs:201:13:201:13 | 1 | true | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:28 | [boolean(false)] break ... | false | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:28 | [boolean(true)] break ... | true | +| test.rs:197:17:197:29 | ExprStmt | test.rs:201:13:201:13 | 1 | true | +| test.rs:197:17:197:29 | ExprStmt | test.rs:203:13:203:13 | 0 | false | | test.rs:209:13:211:14 | ExprStmt | test.rs:208:9:217:9 | if ... {...} else {...} | true | | test.rs:209:13:211:14 | ExprStmt | test.rs:209:13:211:13 | if ... {...} | false | | test.rs:209:13:211:14 | ExprStmt | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | true | | test.rs:209:13:211:14 | ExprStmt | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | true | | test.rs:209:13:211:14 | ExprStmt | test.rs:210:17:210:36 | ExprStmt | true | +| test.rs:209:13:211:14 | ExprStmt | test.rs:214:13:214:13 | 1 | true | +| test.rs:209:13:211:14 | ExprStmt | test.rs:216:13:216:13 | 0 | true | +| test.rs:210:17:210:35 | [boolean(false)] break ''label ... | test.rs:216:13:216:13 | 0 | false | +| test.rs:210:17:210:35 | [boolean(true)] break ''label ... | test.rs:214:13:214:13 | 1 | true | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | false | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | true | +| test.rs:210:17:210:36 | ExprStmt | test.rs:214:13:214:13 | 1 | true | +| test.rs:210:17:210:36 | ExprStmt | test.rs:216:13:216:13 | 0 | false | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | false | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | true | +| test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:224:13:224:13 | 1 | true | +| test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:226:13:226:13 | 0 | false | +| test.rs:222:13:222:30 | [boolean(false)] break ''block ... | test.rs:226:13:226:13 | 0 | false | +| test.rs:222:13:222:30 | [boolean(true)] break ''block ... | test.rs:224:13:224:13 | 1 | true | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:17:234:22 | [boolean(true)] ... && ... | true | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:22:234:22 | b | true | +| test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:27:234:27 | c | true | +| test.rs:234:17:234:22 | [boolean(true)] ... && ... | test.rs:234:27:234:27 | c | true | | test.rs:234:22:234:22 | b | test.rs:234:17:234:22 | [boolean(true)] ... && ... | true | +| test.rs:234:22:234:22 | b | test.rs:234:27:234:27 | c | true | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | false | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:22:239:22 | b | false | +| test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:27:239:27 | c | false | +| test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | test.rs:239:27:239:27 | c | false | | test.rs:239:22:239:22 | b | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | false | +| test.rs:239:22:239:22 | b | test.rs:239:27:239:27 | c | false | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | false | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:23:244:23 | b | false | +| test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:35:244:35 | c | false | +| test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | test.rs:244:35:244:35 | c | false | | test.rs:244:23:244:23 | b | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | false | +| test.rs:244:23:244:23 | b | test.rs:244:35:244:35 | c | false | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:12:254:17 | [boolean(true)] ... && ... | true | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:12:254:22 | [boolean(true)] ... && ... | true | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:17:254:17 | b | true | +| test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:22:254:22 | c | true | +| test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:255:13:255:16 | true | true | | test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:12:254:22 | [boolean(true)] ... && ... | true | +| test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:22:254:22 | c | true | +| test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:255:13:255:16 | true | true | +| test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:257:13:257:17 | false | false | +| test.rs:254:12:254:22 | [boolean(true)] ... && ... | test.rs:255:13:255:16 | true | true | | test.rs:254:17:254:17 | b | test.rs:254:12:254:17 | [boolean(true)] ... && ... | true | | test.rs:254:17:254:17 | b | test.rs:254:12:254:22 | [boolean(true)] ... && ... | true | +| test.rs:254:17:254:17 | b | test.rs:254:22:254:22 | c | true | +| test.rs:254:17:254:17 | b | test.rs:255:13:255:16 | true | true | +| test.rs:254:22:254:22 | c | test.rs:254:12:254:22 | [boolean(true)] ... && ... | true | +| test.rs:254:22:254:22 | c | test.rs:255:13:255:16 | true | true | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | false | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | false | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:17:262:17 | b | false | +| test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:22:262:22 | c | false | +| test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:265:13:265:17 | false | false | | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | false | +| test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:22:262:22 | c | false | +| test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:265:13:265:17 | false | false | +| test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | test.rs:265:13:265:17 | false | false | +| test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:263:13:263:16 | true | true | | test.rs:262:17:262:17 | b | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | false | | test.rs:262:17:262:17 | b | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | false | +| test.rs:262:17:262:17 | b | test.rs:262:22:262:22 | c | false | +| test.rs:262:17:262:17 | b | test.rs:265:13:265:17 | false | false | +| test.rs:262:22:262:22 | c | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | false | +| test.rs:262:22:262:22 | c | test.rs:265:13:265:17 | false | false | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:270:12:270:13 | [boolean(false)] ! ... | true | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:270:12:270:13 | [boolean(true)] ! ... | false | +| test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:271:13:271:16 | true | false | +| test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:273:13:273:17 | false | true | +| test.rs:270:12:270:13 | [boolean(false)] ! ... | test.rs:273:13:273:17 | false | false | +| test.rs:270:12:270:13 | [boolean(true)] ! ... | test.rs:271:13:271:16 | true | true | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:278:9:278:19 | ... && ... | false | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:278:14:278:19 | return | true | +| test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:9:284:9 | if ... {...} | false | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:13:282:21 | [boolean(false)] ... && ... | false | +| test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:13:282:21 | [boolean(true)] ... && ... | true | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:18:282:21 | true | true | +| test.rs:281:5:286:5 | enter fn test_and_true | test.rs:283:13:283:21 | ExprStmt | true | +| test.rs:282:13:282:21 | [boolean(false)] ... && ... | test.rs:282:9:284:9 | if ... {...} | false | +| test.rs:282:13:282:21 | [boolean(true)] ... && ... | test.rs:283:13:283:21 | ExprStmt | true | +| test.rs:282:18:282:21 | true | test.rs:282:13:282:21 | [boolean(true)] ... && ... | true | +| test.rs:282:18:282:21 | true | test.rs:283:13:283:21 | ExprStmt | true | | test.rs:309:26:309:26 | x | test.rs:309:42:309:42 | x | true | | test.rs:315:5:324:5 | enter fn test_match_with_return_in_scrutinee | test.rs:316:9:323:9 | match ... { ... } | false | | test.rs:315:5:324:5 | enter fn test_match_with_return_in_scrutinee | test.rs:317:13:317:21 | ExprStmt | true | | test.rs:315:5:324:5 | enter fn test_match_with_return_in_scrutinee | test.rs:319:13:319:23 | maybe_digit | false | | test.rs:315:5:324:5 | enter fn test_match_with_return_in_scrutinee | test.rs:321:26:321:26 | x | false | | test.rs:315:5:324:5 | enter fn test_match_with_return_in_scrutinee | test.rs:322:13:322:24 | ...::None | false | +| test.rs:327:10:330:9 | [boolean(true)] match r { ... } | test.rs:330:15:330:18 | cond | true | | test.rs:328:18:328:18 | a | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | true | +| test.rs:328:18:328:18 | a | test.rs:330:15:330:18 | cond | true | | test.rs:409:28:414:9 | enter { ... } | test.rs:410:13:412:13 | if b {...} | false | | test.rs:409:28:414:9 | enter { ... } | test.rs:411:17:411:41 | ExprStmt | true | +| test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:13:431:49 | ExprStmt | false | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:21:431:48 | [boolean(false)] ! ... | true | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:21:431:48 | [boolean(true)] ! ... | false | +| test.rs:431:21:431:48 | [boolean(true)] ! ... | test.rs:431:13:431:49 | ExprStmt | true | +| test.rs:437:5:446:5 | enter fn const_block_panic | test.rs:439:9:444:9 | if false {...} | false | +| test.rs:449:1:454:1 | enter fn dead_code | test.rs:451:9:451:17 | ExprStmt | true | | test.rs:466:1:480:1 | enter fn labelled_block1 | test.rs:469:9:471:9 | if ... {...} | false | | test.rs:466:1:480:1 | enter fn labelled_block1 | test.rs:470:13:470:27 | ExprStmt | true | | test.rs:466:1:480:1 | enter fn labelled_block1 | test.rs:473:9:475:9 | if ... {...} | false | @@ -1308,6 +1654,8 @@ successor | test.rs:129:5:135:5 | enter fn test_if_else | test.rs:133:13:133:13 | n | false | | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:24:153:24 | a | true | | test.rs:152:5:158:5 | enter fn test_nested_if | test.rs:153:41:153:41 | a | false | +| test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:156:13:156:13 | 0 | false | +| test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | test.rs:154:13:154:13 | 1 | true | | test.rs:153:22:153:32 | [boolean(false)] { ... } | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | false | | test.rs:153:22:153:32 | [boolean(true)] { ... } | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | true | | test.rs:153:24:153:24 | a | test.rs:153:22:153:32 | [boolean(false)] { ... } | false | @@ -1316,60 +1664,89 @@ successor | test.rs:153:39:153:48 | [boolean(true)] { ... } | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | true | | test.rs:153:41:153:41 | a | test.rs:153:39:153:48 | [boolean(false)] { ... } | false | | test.rs:153:41:153:41 | a | test.rs:153:39:153:48 | [boolean(true)] { ... } | true | +| test.rs:161:13:164:9 | [boolean(false)] match a { ... } | test.rs:167:13:167:13 | 0 | false | +| test.rs:161:13:164:9 | [boolean(true)] match a { ... } | test.rs:165:13:165:13 | 1 | true | +| test.rs:162:18:162:21 | true | test.rs:161:13:164:9 | [boolean(true)] match a { ... } | true | +| test.rs:163:13:163:13 | _ | test.rs:161:13:164:9 | [boolean(false)] match a { ... } | false | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:172:12:175:9 | [boolean(false)] { ... } | false | | test.rs:171:5:180:5 | enter fn test_nested_if_block | test.rs:172:12:175:9 | [boolean(true)] { ... } | true | +| test.rs:172:12:175:9 | [boolean(false)] { ... } | test.rs:178:13:178:13 | 0 | false | +| test.rs:172:12:175:9 | [boolean(true)] { ... } | test.rs:176:13:176:13 | 1 | true | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:184:12:187:9 | [boolean(false)] { ... } | false | | test.rs:182:5:192:5 | enter fn test_if_assignment | test.rs:184:12:187:9 | [boolean(true)] { ... } | true | +| test.rs:184:12:187:9 | [boolean(false)] { ... } | test.rs:190:13:190:13 | 0 | false | +| test.rs:184:12:187:9 | [boolean(true)] { ... } | test.rs:188:13:188:13 | 1 | true | | test.rs:196:13:198:14 | ExprStmt | test.rs:196:13:198:13 | if ... {...} | false | | test.rs:196:13:198:14 | ExprStmt | test.rs:197:17:197:29 | ExprStmt | true | +| test.rs:197:17:197:28 | [boolean(false)] break ... | test.rs:203:13:203:13 | 0 | false | +| test.rs:197:17:197:28 | [boolean(true)] break ... | test.rs:201:13:201:13 | 1 | true | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:28 | [boolean(false)] break ... | false | | test.rs:197:17:197:29 | ExprStmt | test.rs:197:17:197:28 | [boolean(true)] break ... | true | | test.rs:209:13:211:14 | ExprStmt | test.rs:209:13:211:13 | if ... {...} | false | | test.rs:209:13:211:14 | ExprStmt | test.rs:210:17:210:36 | ExprStmt | true | +| test.rs:210:17:210:35 | [boolean(false)] break ''label ... | test.rs:216:13:216:13 | 0 | false | +| test.rs:210:17:210:35 | [boolean(true)] break ''label ... | test.rs:214:13:214:13 | 1 | true | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | false | | test.rs:210:17:210:36 | ExprStmt | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | true | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | false | | test.rs:220:5:228:5 | enter fn test_labelled_block | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | true | +| test.rs:222:13:222:30 | [boolean(false)] break ''block ... | test.rs:226:13:226:13 | 0 | false | +| test.rs:222:13:222:30 | [boolean(true)] break ''block ... | test.rs:224:13:224:13 | 1 | true | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:17:234:22 | [boolean(false)] ... && ... | false | | test.rs:233:5:236:5 | enter fn test_and_operator | test.rs:234:22:234:22 | b | true | | test.rs:234:17:234:22 | [boolean(false)] ... && ... | test.rs:234:17:234:27 | ... && ... | false | +| test.rs:234:17:234:22 | [boolean(true)] ... && ... | test.rs:234:27:234:27 | c | true | | test.rs:234:22:234:22 | b | test.rs:234:17:234:22 | [boolean(false)] ... && ... | false | | test.rs:234:22:234:22 | b | test.rs:234:17:234:22 | [boolean(true)] ... && ... | true | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | true | | test.rs:238:5:241:5 | enter fn test_or_operator | test.rs:239:22:239:22 | b | false | +| test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | test.rs:239:27:239:27 | c | false | | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | test.rs:239:17:239:27 | ... \|\| ... | true | | test.rs:239:22:239:22 | b | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | false | | test.rs:239:22:239:22 | b | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | true | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | true | | test.rs:243:5:246:5 | enter fn test_or_operator_2 | test.rs:244:23:244:23 | b | false | +| test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | test.rs:244:35:244:35 | c | false | | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | test.rs:244:17:244:35 | ... \|\| ... | true | | test.rs:244:23:244:23 | b | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | false | | test.rs:244:23:244:23 | b | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | true | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:12:254:17 | [boolean(false)] ... && ... | false | | test.rs:253:5:259:5 | enter fn test_if_and_operator | test.rs:254:17:254:17 | b | true | | test.rs:254:12:254:17 | [boolean(false)] ... && ... | test.rs:254:12:254:22 | [boolean(false)] ... && ... | false | -| test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:12:254:22 | [boolean(false)] ... && ... | false | -| test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:12:254:22 | [boolean(true)] ... && ... | true | +| test.rs:254:12:254:17 | [boolean(true)] ... && ... | test.rs:254:22:254:22 | c | true | +| test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:257:13:257:17 | false | false | +| test.rs:254:12:254:22 | [boolean(true)] ... && ... | test.rs:255:13:255:16 | true | true | | test.rs:254:17:254:17 | b | test.rs:254:12:254:17 | [boolean(false)] ... && ... | false | | test.rs:254:17:254:17 | b | test.rs:254:12:254:17 | [boolean(true)] ... && ... | true | +| test.rs:254:22:254:22 | c | test.rs:254:12:254:22 | [boolean(false)] ... && ... | false | +| test.rs:254:22:254:22 | c | test.rs:254:12:254:22 | [boolean(true)] ... && ... | true | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | true | | test.rs:261:5:267:5 | enter fn test_if_or_operator | test.rs:262:17:262:17 | b | false | -| test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | false | -| test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | true | +| test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | test.rs:262:22:262:22 | c | false | | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | true | +| test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | test.rs:265:13:265:17 | false | false | +| test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:263:13:263:16 | true | true | | test.rs:262:17:262:17 | b | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | false | | test.rs:262:17:262:17 | b | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | true | +| test.rs:262:22:262:22 | c | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | false | +| test.rs:262:22:262:22 | c | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | true | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:270:12:270:13 | [boolean(false)] ! ... | true | | test.rs:269:5:275:5 | enter fn test_if_not_operator | test.rs:270:12:270:13 | [boolean(true)] ! ... | false | +| test.rs:270:12:270:13 | [boolean(false)] ! ... | test.rs:273:13:273:17 | false | false | +| test.rs:270:12:270:13 | [boolean(true)] ! ... | test.rs:271:13:271:16 | true | true | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:278:9:278:19 | ... && ... | false | | test.rs:277:5:279:5 | enter fn test_and_return | test.rs:278:14:278:19 | return | true | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:13:282:21 | [boolean(false)] ... && ... | false | | test.rs:281:5:286:5 | enter fn test_and_true | test.rs:282:18:282:21 | true | true | +| test.rs:282:13:282:21 | [boolean(false)] ... && ... | test.rs:282:9:284:9 | if ... {...} | false | +| test.rs:282:13:282:21 | [boolean(true)] ... && ... | test.rs:283:13:283:21 | ExprStmt | true | +| test.rs:282:18:282:21 | true | test.rs:282:13:282:21 | [boolean(true)] ... && ... | true | | test.rs:309:26:309:26 | x | test.rs:309:42:309:42 | x | true | | test.rs:309:26:309:26 | x | test.rs:310:13:310:27 | ...::Some(...) | false | | test.rs:315:5:324:5 | enter fn test_match_with_return_in_scrutinee | test.rs:317:13:317:21 | ExprStmt | true | | test.rs:315:5:324:5 | enter fn test_match_with_return_in_scrutinee | test.rs:319:13:319:23 | maybe_digit | false | | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | test.rs:327:9:330:18 | ... && ... | false | +| test.rs:327:10:330:9 | [boolean(true)] match r { ... } | test.rs:330:15:330:18 | cond | true | | test.rs:328:18:328:18 | a | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | false | | test.rs:328:18:328:18 | a | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | true | | test.rs:329:13:329:13 | _ | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | false | @@ -1378,6 +1755,9 @@ successor | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:21:431:48 | [boolean(false)] ! ... | true | | test.rs:427:5:435:5 | enter fn const_block_assert | test.rs:431:21:431:48 | [boolean(true)] ! ... | false | | test.rs:431:21:431:48 | [boolean(false)] ! ... | test.rs:431:21:431:48 | if ... {...} | false | +| test.rs:431:21:431:48 | [boolean(true)] ! ... | test.rs:431:13:431:49 | ExprStmt | true | +| test.rs:437:5:446:5 | enter fn const_block_panic | test.rs:439:9:444:9 | if false {...} | false | +| test.rs:449:1:454:1 | enter fn dead_code | test.rs:451:9:451:17 | ExprStmt | true | | test.rs:466:1:480:1 | enter fn labelled_block1 | test.rs:469:9:471:9 | if ... {...} | false | | test.rs:466:1:480:1 | enter fn labelled_block1 | test.rs:470:13:470:27 | ExprStmt | true | | test.rs:469:9:471:9 | if ... {...} | test.rs:473:9:475:9 | if ... {...} | false | @@ -1421,58 +1801,58 @@ joinBlockPredecessor | test.rs:138:9:142:9 | if ... {...} else {...} | test.rs:141:13:141:13 | 0 | 1 | | test.rs:145:5:150:5 | exit fn test_if_let (normal) | test.rs:146:9:148:9 | if ... {...} | 1 | | test.rs:145:5:150:5 | exit fn test_if_let (normal) | test.rs:146:21:146:21 | n | 0 | -| test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | 0 | -| test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | 1 | +| test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:154:13:154:13 | 1 | 1 | +| test.rs:153:9:157:9 | if ... {...} else {...} | test.rs:156:13:156:13 | 0 | 0 | | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:153:22:153:32 | [boolean(false)] { ... } | 1 | | test.rs:153:13:153:48 | [boolean(false)] if ... {...} else {...} | test.rs:153:39:153:48 | [boolean(false)] { ... } | 0 | | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | test.rs:153:22:153:32 | [boolean(true)] { ... } | 1 | | test.rs:153:13:153:48 | [boolean(true)] if ... {...} else {...} | test.rs:153:39:153:48 | [boolean(true)] { ... } | 0 | -| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:162:18:162:21 | true | 0 | -| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:163:13:163:13 | _ | 1 | -| test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:172:12:175:9 | [boolean(false)] { ... } | 0 | -| test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:172:12:175:9 | [boolean(true)] { ... } | 1 | -| test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:184:12:187:9 | [boolean(false)] { ... } | 0 | -| test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:184:12:187:9 | [boolean(true)] { ... } | 1 | -| test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:197:17:197:28 | [boolean(false)] break ... | 0 | -| test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:197:17:197:28 | [boolean(true)] break ... | 1 | +| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:165:13:165:13 | 1 | 1 | +| test.rs:161:9:168:9 | if ... {...} else {...} | test.rs:167:13:167:13 | 0 | 0 | +| test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:176:13:176:13 | 1 | 1 | +| test.rs:172:9:179:9 | if ... {...} else {...} | test.rs:178:13:178:13 | 0 | 0 | +| test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:188:13:188:13 | 1 | 1 | +| test.rs:184:9:191:9 | if ... {...} else {...} | test.rs:190:13:190:13 | 0 | 0 | +| test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:201:13:201:13 | 1 | 1 | +| test.rs:195:9:204:9 | if ... {...} else {...} | test.rs:203:13:203:13 | 0 | 0 | | test.rs:196:13:198:14 | ExprStmt | test.rs:194:5:205:5 | enter fn test_if_loop1 | 1 | | test.rs:196:13:198:14 | ExprStmt | test.rs:196:13:198:13 | if ... {...} | 0 | -| test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:210:17:210:35 | [boolean(false)] break ''label ... | 0 | -| test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:210:17:210:35 | [boolean(true)] break ''label ... | 1 | +| test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:214:13:214:13 | 1 | 1 | +| test.rs:208:9:217:9 | if ... {...} else {...} | test.rs:216:13:216:13 | 0 | 0 | | test.rs:209:13:211:14 | ExprStmt | test.rs:207:5:218:5 | enter fn test_if_loop2 | 1 | | test.rs:209:13:211:14 | ExprStmt | test.rs:209:13:211:13 | if ... {...} | 0 | -| test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:222:13:222:30 | [boolean(false)] break ''block ... | 0 | -| test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:222:13:222:30 | [boolean(true)] break ''block ... | 1 | +| test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:224:13:224:13 | 1 | 1 | +| test.rs:221:9:227:9 | if ... {...} else {...} | test.rs:226:13:226:13 | 0 | 0 | | test.rs:234:17:234:22 | [boolean(false)] ... && ... | test.rs:233:5:236:5 | enter fn test_and_operator | 1 | | test.rs:234:17:234:22 | [boolean(false)] ... && ... | test.rs:234:22:234:22 | b | 0 | | test.rs:234:17:234:27 | ... && ... | test.rs:234:17:234:22 | [boolean(false)] ... && ... | 0 | -| test.rs:234:17:234:27 | ... && ... | test.rs:234:17:234:22 | [boolean(true)] ... && ... | 1 | +| test.rs:234:17:234:27 | ... && ... | test.rs:234:27:234:27 | c | 1 | | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | test.rs:238:5:241:5 | enter fn test_or_operator | 1 | | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | test.rs:239:22:239:22 | b | 0 | -| test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:17:239:22 | [boolean(false)] ... \|\| ... | 0 | -| test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | 1 | +| test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:17:239:22 | [boolean(true)] ... \|\| ... | 0 | +| test.rs:239:17:239:27 | ... \|\| ... | test.rs:239:27:239:27 | c | 1 | | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | test.rs:243:5:246:5 | enter fn test_or_operator_2 | 1 | | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | test.rs:244:23:244:23 | b | 0 | -| test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:17:244:30 | [boolean(false)] ... \|\| ... | 0 | -| test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | 1 | -| test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:254:12:254:22 | [boolean(false)] ... && ... | 0 | -| test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:254:12:254:22 | [boolean(true)] ... && ... | 1 | +| test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:17:244:30 | [boolean(true)] ... \|\| ... | 0 | +| test.rs:244:17:244:35 | ... \|\| ... | test.rs:244:35:244:35 | c | 1 | +| test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:255:13:255:16 | true | 1 | +| test.rs:254:9:258:9 | if ... {...} else {...} | test.rs:257:13:257:17 | false | 0 | | test.rs:254:12:254:17 | [boolean(false)] ... && ... | test.rs:253:5:259:5 | enter fn test_if_and_operator | 1 | | test.rs:254:12:254:17 | [boolean(false)] ... && ... | test.rs:254:17:254:17 | b | 0 | | test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:254:12:254:17 | [boolean(false)] ... && ... | 0 | -| test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:254:12:254:17 | [boolean(true)] ... && ... | 1 | -| test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:12:262:22 | [boolean(false)] ... \|\| ... | 0 | -| test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | 1 | +| test.rs:254:12:254:22 | [boolean(false)] ... && ... | test.rs:254:22:254:22 | c | 1 | +| test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:263:13:263:16 | true | 1 | +| test.rs:262:9:266:9 | if ... {...} else {...} | test.rs:265:13:265:17 | false | 0 | | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | test.rs:261:5:267:5 | enter fn test_if_or_operator | 1 | | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | test.rs:262:17:262:17 | b | 0 | -| test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(false)] ... \|\| ... | 0 | -| test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | 1 | -| test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:270:12:270:13 | [boolean(false)] ! ... | 0 | -| test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:270:12:270:13 | [boolean(true)] ! ... | 1 | +| test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:262:12:262:17 | [boolean(true)] ... \|\| ... | 0 | +| test.rs:262:12:262:22 | [boolean(true)] ... \|\| ... | test.rs:262:22:262:22 | c | 1 | +| test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:271:13:271:16 | true | 1 | +| test.rs:270:9:274:9 | if ... {...} else {...} | test.rs:273:13:273:17 | false | 0 | | test.rs:277:5:279:5 | exit fn test_and_return (normal) | test.rs:278:9:278:19 | ... && ... | 1 | | test.rs:277:5:279:5 | exit fn test_and_return (normal) | test.rs:278:14:278:19 | return | 0 | -| test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:282:13:282:21 | [boolean(false)] ... && ... | 1 | -| test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:282:18:282:21 | true | 0 | +| test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:282:9:284:9 | if ... {...} | 1 | +| test.rs:281:5:286:5 | exit fn test_and_true (normal) | test.rs:283:13:283:21 | ExprStmt | 0 | | test.rs:292:5:294:5 | exit fn test_question_mark_operator_1 (normal) | test.rs:292:5:294:5 | enter fn test_question_mark_operator_1 | 1 | | test.rs:292:5:294:5 | exit fn test_question_mark_operator_1 (normal) | test.rs:293:32:293:32 | 4 | 0 | | test.rs:296:5:301:5 | exit fn test_question_mark_operator_2 (normal) | test.rs:296:5:301:5 | enter fn test_question_mark_operator_2 | 1 | @@ -1489,7 +1869,7 @@ joinBlockPredecessor | test.rs:316:9:323:9 | match ... { ... } | test.rs:321:26:321:26 | x | 0 | | test.rs:316:9:323:9 | match ... { ... } | test.rs:322:13:322:24 | ...::None | 1 | | test.rs:327:9:330:18 | ... && ... | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | 0 | -| test.rs:327:9:330:18 | ... && ... | test.rs:327:10:330:9 | [boolean(true)] match r { ... } | 1 | +| test.rs:327:9:330:18 | ... && ... | test.rs:330:15:330:18 | cond | 1 | | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | test.rs:328:18:328:18 | a | 0 | | test.rs:327:10:330:9 | [boolean(false)] match r { ... } | test.rs:329:13:329:13 | _ | 1 | | test.rs:334:9:337:9 | match r { ... } | test.rs:335:16:335:20 | value | 0 | @@ -1511,8 +1891,8 @@ joinBlockPredecessor | test.rs:385:13:385:14 | TupleExpr | test.rs:385:13:385:14 | TupleExpr | 0 | | test.rs:409:28:414:9 | exit { ... } (normal) | test.rs:410:13:412:13 | if b {...} | 1 | | test.rs:409:28:414:9 | exit { ... } (normal) | test.rs:411:17:411:41 | ExprStmt | 0 | +| test.rs:431:21:431:48 | if ... {...} | test.rs:431:13:431:49 | ExprStmt | 1 | | test.rs:431:21:431:48 | if ... {...} | test.rs:431:21:431:48 | [boolean(false)] ! ... | 0 | -| test.rs:431:21:431:48 | if ... {...} | test.rs:431:21:431:48 | [boolean(true)] ! ... | 1 | | test.rs:467:18:478:5 | 'block: { ... } | test.rs:470:13:470:27 | ExprStmt | 0 | | test.rs:467:18:478:5 | 'block: { ... } | test.rs:473:9:475:9 | if ... {...} | 2 | | test.rs:467:18:478:5 | 'block: { ... } | test.rs:474:13:474:27 | ExprStmt | 1 | diff --git a/rust/ql/test/library-tests/controlflow/BasicBlocks.ql b/rust/ql/test/library-tests/controlflow/BasicBlocks.ql index 07045268d834..28083ef14785 100644 --- a/rust/ql/test/library-tests/controlflow/BasicBlocks.ql +++ b/rust/ql/test/library-tests/controlflow/BasicBlocks.ql @@ -10,14 +10,14 @@ query predicate immediateDominator(BasicBlock bb1, BasicBlock bb2) { bb1.getImmediateDominator() = bb2 } -query predicate controls(ConditionBlock bb1, BasicBlock bb2, SuccessorType t) { +query predicate controls(ConditionBasicBlock bb1, BasicBlock bb2, SuccessorType t) { bb1.controls(bb2, t) } -query predicate successor(ConditionBlock bb1, BasicBlock bb2, SuccessorType t) { +query predicate successor(ConditionBasicBlock bb1, BasicBlock bb2, SuccessorType t) { bb1.getASuccessor(t) = bb2 } -query predicate joinBlockPredecessor(JoinBlock bb1, BasicBlock bb2, int i) { +query predicate joinBlockPredecessor(JoinBasicBlock bb1, BasicBlock bb2, int i) { bb1.getJoinBlockPredecessor(i) = bb2 } diff --git a/shared/controlflow/codeql/controlflow/BasicBlock.qll b/shared/controlflow/codeql/controlflow/BasicBlock.qll new file mode 100644 index 000000000000..e5c9f9c0f102 --- /dev/null +++ b/shared/controlflow/codeql/controlflow/BasicBlock.qll @@ -0,0 +1,242 @@ +/** + * This modules provides an implementation of a basic block class based based + * on a control flow graph implementation. + * + * INTERNAL use only. This is an experimental API subject to change without + * notice. + */ + +/** Provides the language-specific input specification. */ +signature module InputSig { + class SuccessorType; + + /** Hold if `t` represents a conditional successor type. */ + predicate successorTypeIsCondition(SuccessorType t); + + /** The class of control flow nodes. */ + class Node { + string toString(); + } + + Node nodeGetASuccessor(Node node, SuccessorType t); + + /** Holds if `node` is the beginning of an entry basic block. */ + predicate nodeIsEntry(Node node); + + /** Holds if `node` is the beginning of an entry basic block. */ + predicate nodeIsExit(Node node); +} + +/** + * Provides a basic block construction on top of a control flow graph. + */ +module Make { + private import Input + + final class BasicBlock = BasicBlockImpl; + + private Node nodeGetAPredecessor(Node node, SuccessorType s) { + nodeGetASuccessor(result, s) = node + } + + /** Holds if this node has more than one predecessor. */ + private predicate nodeIsJoin(Node node) { strictcount(nodeGetAPredecessor(node, _)) > 1 } + + private predicate nodeIsBranch(Node node) { strictcount(nodeGetASuccessor(node, _)) > 1 } + + /** + * A basic block, that is, a maximal straight-line sequence of control flow nodes + * without branches or joins. + */ + private class BasicBlockImpl extends TBasicBlockStart { + /** Gets an immediate successor of this basic block, if any. */ + BasicBlock getASuccessor() { result = this.getASuccessor(_) } + + /** Gets an immediate successor of this basic block of a given type, if any. */ + BasicBlock getASuccessor(SuccessorType t) { + result.getFirstNode() = nodeGetASuccessor(this.getLastNode(), t) + } + + /** Gets an immediate predecessor of this basic block, if any. */ + BasicBlock getAPredecessor() { result.getASuccessor(_) = this } + + /** Gets an immediate predecessor of this basic block of a given type, if any. */ + BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } + + /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ + Node getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } + + /** Gets a control flow node in this basic block. */ + Node getANode() { result = this.getNode(_) } + + /** Gets the first control flow node in this basic block. */ + Node getFirstNode() { this = TBasicBlockStart(result) } + + /** Gets the last control flow node in this basic block. */ + Node getLastNode() { result = this.getNode(this.length() - 1) } + + /** Gets the length of this basic block. */ + int length() { result = strictcount(this.getANode()) } + + /** + * Holds if this basic block immediately dominates basic block `bb`. + * + * That is, `bb` is an immediate successor of this basic block and all + * paths reaching basic block `bb` from some entry point basic block must + * go through this basic block. + */ + predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } + + /** + * Holds if this basic block strictly dominates basic block `bb`. + * + * That is, all paths reaching `bb` from some entry point basic block must + * go through this basic block and this basic block is different from `bb`. + */ + predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } + + /** + * Holds if this basic block dominates basic block `bb`. + * + * That is, all paths reaching `bb` from some entry point basic block must + * go through this basic block. + */ + predicate dominates(BasicBlock bb) { + bb = this or + this.strictlyDominates(bb) + } + + /** + * Holds if `df` is in the dominance frontier of this basic block. That is, + * this basic block dominates a predecessor of `df`, but does not dominate + * `df` itself. + */ + predicate inDominanceFrontier(BasicBlock df) { + this.dominatesPredecessor(df) and + not this.strictlyDominates(df) + } + + /** + * Holds if this basic block dominates a predecessor of `df`. + */ + private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } + + /** + * Gets the basic block that immediately dominates this basic block, if any. + * + * That is, all paths reaching this basic block from some entry point + * basic block must go through the result, which is an immediate basic block + * predecessor of this basic block. + */ + BasicBlock getImmediateDominator() { bbIDominates(result, this) } + + /** + * Holds if this basic block strictly post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block and this basic block is + * different from `bb`. + */ + predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } + + /** + * Holds if this basic block post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block. + */ + predicate postDominates(BasicBlock bb) { + this.strictlyPostDominates(bb) or + this = bb + } + + /** Holds if this basic block is in a loop in the control flow graph. */ + predicate inLoop() { this.getASuccessor+() = this } + + /** Gets a textual representation of this basic block. */ + string toString() { result = this.getFirstNode().toString() } + } + + cached + private module Cached { + /** + * Internal representation of basic blocks. A basic block is represented + * by its first CFG node. + */ + cached + newtype TBasicBlock = TBasicBlockStart(Node cfn) { startsBB(cfn) } + + /** Holds if `cfn` starts a new basic block. */ + private predicate startsBB(Node cfn) { + not exists(nodeGetAPredecessor(cfn, _)) and exists(nodeGetASuccessor(cfn, _)) + or + nodeIsJoin(cfn) + or + nodeIsBranch(nodeGetAPredecessor(cfn, _)) + or + // In cases such as + // + // ```rb + // if x or y + // foo + // else + // bar + // ``` + // + // we have a CFG that looks like + // + // x --false--> [false] x or y --false--> bar + // \ | + // --true--> y --false-- + // \ + // --true--> [true] x or y --true--> foo + // + // and we want to ensure that both `foo` and `bar` start a new basic block. + exists(nodeGetAPredecessor(cfn, any(SuccessorType s | successorTypeIsCondition(s)))) + } + + /** + * Holds if `succ` is a control flow successor of `pred` within + * the same basic block. + */ + private predicate intraBBSucc(Node pred, Node succ) { + succ = nodeGetASuccessor(pred, _) and + not startsBB(succ) + } + + /** + * Holds if `bbStart` is the first node in a basic block and `cfn2` is the + * `i`th node in the same basic block. + */ + cached + predicate bbIndex(Node bbStart, Node cfn, int i) = + shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) + + /** + * Holds if the first node of basic block `succ` is a control flow + * successor of the last node of basic block `pred`. + */ + private predicate succBB(BasicBlock pred, BasicBlock succ) { pred.getASuccessor(_) = succ } + + /** Holds if `dom` is an immediate dominator of `bb`. */ + cached + predicate bbIDominates(BasicBlock dom, BasicBlock bb) = + idominance(entryBB/1, succBB/2)(_, dom, bb) + + /** Holds if `pred` is a basic block predecessor of `succ`. */ + private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } + + /** Holds if `bb` is an exit basic block that represents normal exit. */ + private predicate exitBB(BasicBlock bb) { nodeIsExit(bb.getANode()) } + + /** Holds if `dom` is an immediate post-dominator of `bb`. */ + cached + predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = + idominance(exitBB/1, predBB/2)(_, dom, bb) + } + + private import Cached + + /** Holds if `bb` is an entry basic block. */ + private predicate entryBB(BasicBlock bb) { nodeIsEntry(bb.getFirstNode()) } +} diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index 4130920daf67..998e6a7dbbf1 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -48,7 +48,7 @@ signature module InputSig { Location getLocation(); } - /** Gets the CFG scope of AST node `n`. */ + /** Gets the CFG scope of the AST node `n`. */ CfgScope getCfgScope(AstNode n); /** Holds if `first` is executed first when entering `scope`. */ @@ -77,6 +77,20 @@ signature module InputSig { /** Holds if `t` is an abnormal exit type out of a CFG scope. */ predicate isAbnormalExitType(SuccessorType t); + + /** + * Gets an `id` of `node`. This is used to order the predecessors of a join + * basic block. + */ + bindingset[node] + default predicate idOfAstNode(AstNode node, int id) { none() } + + /** + * Gets an `id` of `scope`. This is used to order the predecessors of a join + * basic block. + */ + bindingset[scope] + default predicate idOfCfgScope(CfgScope scope, int id) { none() } } /** Provides input needed for CFG splitting. */ @@ -1514,6 +1528,261 @@ module MakeWithSplitting< /** Holds if CFG scope `scope` lacks an initial AST node. */ query predicate scopeNoFirst(CfgScope scope) { not scopeFirst(scope, _) } } + + /** + * Provides a basic block construction on top of the control flow graph. + */ + module BasicBlocks { + private import codeql.controlflow.BasicBlock as BB + + private class NodeAlias = Node; + + private module BasicBlockInputSig implements BB::InputSig { + class SuccessorType = Input::SuccessorType; + + predicate successorTypeIsCondition = Input::successorTypeIsCondition/1; + + class Node = NodeAlias; + + Node nodeGetASuccessor(Node node, SuccessorType t) { result = node.getASuccessor(t) } + + predicate nodeIsEntry(Node node) { node instanceof EntryNode } + + predicate nodeIsExit(Node node) { node.(AnnotatedExitNode).isNormal() } + } + + private module BasicBlockImpl = BB::Make; + + /** + * A basic block, that is, a maximal straight-line sequence of control flow nodes + * without branches or joins. + */ + final class BasicBlock extends BasicBlockImpl::BasicBlock { + // We extend `BasicBlockImpl::BasicBlock` to add the `getScope` and + // `getLocation`. + /** Gets the scope of this basic block. */ + CfgScope getScope() { + if this instanceof EntryBasicBlock + then result = this.getFirstNode().getScope() + else result = this.getAPredecessor().getScope() + } + + /** Gets the location of this basic block. */ + Location getLocation() { result = this.getFirstNode().getLocation() } + + /** Gets an immediate successor of this basic block, if any. */ + BasicBlock getASuccessor() { result = super.getASuccessor() } + + /** Gets an immediate successor of this basic block of a given type, if any. */ + BasicBlock getASuccessor(SuccessorType t) { + result.getFirstNode() = this.getLastNode().getASuccessor(t) + } + + /** Gets an immediate predecessor of this basic block, if any. */ + BasicBlock getAPredecessor() { result.getASuccessor() = this } + + /** Gets an immediate predecessor of this basic block of a given type, if any. */ + BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } + + /** + * Holds if this basic block immediately dominates basic block `bb`. + * + * That is, `bb` is an immediate successor of this basic block and all + * paths reaching basic block `bb` from some entry point basic block must + * go through this basic block. + */ + predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) } + + /** + * Holds if this basic block strictly dominates basic block `bb`. + * + * That is, all paths reaching `bb` from some entry point basic block must + * go through this basic block and this basic block is different from `bb`. + */ + predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) } + + /** + * Holds if this basic block dominates basic block `bb`. + * + * That is, all paths reaching `bb` from some entry point basic block must + * go through this basic block. + */ + predicate dominates(BasicBlock bb) { super.dominates(bb) } + + /** + * Holds if `df` is in the dominance frontier of this basic block. That is, + * this basic block dominates a predecessor of `df`, but does not dominate + * `df` itself. + */ + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } + + /** + * Gets the basic block that immediately dominates this basic block, if any. + * + * That is, all paths reaching this basic block from some entry point + * basic block must go through the result, which is an immediate basic block + * predecessor of this basic block. + */ + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } + + /** + * Holds if this basic block strictly post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block and this basic block is + * different from `bb`. + */ + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) } + + /** + * Holds if this basic block post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block. + */ + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } + } + + /** + * An entry basic block, that is, a basic block whose first node is + * an entry node. + */ + final class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { this.getFirstNode() instanceof EntryNode } + } + + /** + * An annotated exit basic block, that is, a basic block whose last node is + * an annotated exit node. + */ + final class AnnotatedExitBasicBlock extends BasicBlock { + private boolean normal; + + AnnotatedExitBasicBlock() { + exists(AnnotatedExitNode n | + n = this.getANode() and + if n.isNormal() then normal = true else normal = false + ) + } + + /** Holds if this block represent a normal exit. */ + final predicate isNormal() { normal = true } + } + + /** + * An exit basic block, that is, a basic block whose last node is + * an exit node. + */ + final class ExitBasicBlock extends BasicBlock { + ExitBasicBlock() { this.getLastNode() instanceof ExitNode } + } + + private module JoinBlockPredecessors { + int getId(JoinPredecessorBasicBlock jbp) { + idOfAstNode(jbp.getFirstNode().(AstCfgNode).getAstNode(), result) + or + idOfCfgScope(jbp.(EntryBasicBlock).getScope(), result) + } + + string getSplitString(JoinPredecessorBasicBlock jbp) { + result = jbp.getFirstNode().(AstCfgNode).getSplitsString() + or + not exists(jbp.getFirstNode().(AstCfgNode).getSplitsString()) and + result = "" + } + } + + /** + * Gets the `i`th predecessor of join block `jb`, with respect to some + * arbitrary order. + */ + cached + JoinPredecessorBasicBlock getJoinBlockPredecessor(JoinBasicBlock jb, int i) { + result = + rank[i + 1](JoinPredecessorBasicBlock jbp | + jbp = jb.getAPredecessor() + | + jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) + ) + } + + /** A basic block with more than one predecessor. */ + final class JoinBasicBlock extends BasicBlock { + JoinBasicBlock() { this.getFirstNode().isJoin() } + + /** + * Gets the `i`th predecessor of this join block, with respect to some + * arbitrary order. + */ + JoinPredecessorBasicBlock getJoinBlockPredecessor(int i) { + result = getJoinBlockPredecessor(this, i) + } + } + + /** A basic block that is an immediate predecessor of a join block. */ + final class JoinPredecessorBasicBlock extends BasicBlock { + JoinPredecessorBasicBlock() { this.getASuccessor() instanceof JoinBasicBlock } + } + + /** A basic block that terminates in a condition, splitting the subsequent control flow. */ + final class ConditionBasicBlock extends BasicBlock { + ConditionBasicBlock() { this.getLastNode().isCondition() } + + /** + * Holds if basic block `succ` is immediately controlled by this basic + * block with conditional value `s`. That is, `succ` is an immediate + * successor of this block, and `succ` can only be reached from + * the callable entry point by going via the `s` edge out of this basic block. + */ + pragma[nomagic] + predicate immediatelyControls(BasicBlock succ, SuccessorType s) { + succ = this.getASuccessor(s) and + forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | + succ.dominates(pred) + ) + } + + /** + * Holds if basic block `controlled` is controlled by this basic block with + * conditional value `s`. That is, `controlled` can only be reached from + * the callable entry point by going via the `s` edge out of this basic block. + */ + predicate controls(BasicBlock controlled, SuccessorType s) { + // For this block to control the block `controlled` with `testIsTrue` the following must be true: + // 1/ Execution must have passed through the test i.e. `this` must strictly dominate `controlled`. + // 2/ Execution must have passed through the `testIsTrue` edge leaving `this`. + // + // Although "passed through the true edge" implies that `this.getATrueSuccessor()` dominates `controlled`, + // the reverse is not true, as flow may have passed through another edge to get to `this.getATrueSuccessor()` + // so we need to assert that `this.getATrueSuccessor()` dominates `controlled` *and* that + // all predecessors of `this.getATrueSuccessor()` are either `this` or dominated by `this.getATrueSuccessor()`. + // + // For example, in the following C# snippet: + // ```csharp + // if (x) + // controlled; + // false_successor; + // uncontrolled; + // ``` + // `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`) + // or dominated by itself. Whereas in the following code: + // ```csharp + // if (x) + // while (controlled) + // also_controlled; + // false_successor; + // uncontrolled; + // ``` + // the block `while controlled` is controlled because all of its predecessors are `this` (`if (x)`) + // or (in the case of `also_controlled`) dominated by itself. + // + // The additional constraint on the predecessors of the test successor implies + // that `this` strictly dominates `controlled` so that isn't necessary to check + // directly. + exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled)) + } + } + } } /** Provides a disabled `SplittingInputSig` implementation. */ diff --git a/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll b/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll index dfe18e4ceded..9127d014ea3a 100644 --- a/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll +++ b/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll @@ -1,279 +1,81 @@ /** Provides classes representing basic blocks. */ -private import swift private import ControlFlowGraph -private import internal.ControlFlowGraphImpl as Impl -private import internal.ControlFlowElements -private import CfgNodes +private import internal.ControlFlowGraphImpl as CfgImpl private import SuccessorTypes -private import codeql.swift.generated.Raw -private import codeql.swift.generated.Synth +private import CfgImpl::BasicBlocks as BasicBlocksImpl /** * A basic block, that is, a maximal straight-line sequence of control flow nodes * without branches or joins. */ -class BasicBlock extends TBasicBlockStart { - /** Gets the scope of this basic block. */ - CfgScope getScope() { result = this.getAPredecessor().getScope() } +final class BasicBlock extends BasicBlocksImpl::BasicBlock { + BasicBlock getASuccessor() { result = super.getASuccessor() } - /** Gets an immediate successor of this basic block, if any. */ - BasicBlock getASuccessor() { result = this.getASuccessor(_) } + BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor(t) } - /** Gets an immediate successor of this basic block of a given type, if any. */ - BasicBlock getASuccessor(SuccessorType t) { - result.getFirstNode() = this.getLastNode().getASuccessor(t) - } - - /** Gets an immediate predecessor of this basic block, if any. */ - BasicBlock getAPredecessor() { result.getASuccessor() = this } - - /** Gets an immediate predecessor of this basic block of a given type, if any. */ - BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } - - /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ - ControlFlowNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } - - /** Gets a control flow node in this basic block. */ - ControlFlowNode getANode() { result = this.getNode(_) } - - /** Gets the first control flow node in this basic block. */ - ControlFlowNode getFirstNode() { this = TBasicBlockStart(result) } + BasicBlock getAPredecessor() { result = super.getAPredecessor() } - /** Gets the last control flow node in this basic block. */ - ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) } + BasicBlock getAPredecessor(SuccessorType t) { result = super.getAPredecessor(t) } - /** Gets the length of this basic block. */ - int length() { result = strictcount(this.getANode()) } + ControlFlowNode getNode(int pos) { result = super.getNode(pos) } - predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } + ControlFlowNode getANode() { result = super.getANode() } - predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } + ControlFlowNode getFirstNode() { result = super.getFirstNode() } - predicate dominates(BasicBlock bb) { - bb = this or - this.strictlyDominates(bb) - } - - predicate inDominanceFrontier(BasicBlock df) { - this.dominatesPredecessor(df) and - not this.strictlyDominates(df) - } + ControlFlowNode getLastNode() { result = super.getLastNode() } - /** - * Holds if this basic block dominates a predecessor of `df`. - */ - private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } + predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) } - BasicBlock getImmediateDominator() { bbIDominates(result, this) } + predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) } - predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } + predicate dominates(BasicBlock bb) { super.dominates(bb) } - predicate postDominates(BasicBlock bb) { - this.strictlyPostDominates(bb) or - this = bb - } + predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) } - /** Holds if this basic block is in a loop in the control flow graph. */ - predicate inLoop() { this.getASuccessor+() = this } + BasicBlock getImmediateDominator() { result = super.getImmediateDominator() } - /** Gets a textual representation of this basic block. */ - string toString() { result = this.getFirstNode().toString() } + predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) } - /** Gets the location of this basic block. */ - Location getLocation() { result = this.getFirstNode().getLocation() } + predicate postDominates(BasicBlock bb) { super.postDominates(bb) } } -cached -private module Cached { - /** Internal representation of basic blocks. */ - cached - newtype TBasicBlock = TBasicBlockStart(ControlFlowNode cfn) { startsBB(cfn) } - - /** Holds if `cfn` starts a new basic block. */ - private predicate startsBB(ControlFlowNode cfn) { - not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor()) - or - cfn.isJoin() - or - cfn.getAPredecessor().isBranch() - } - - /** - * Holds if `succ` is a control flow successor of `pred` within - * the same basic block. - */ - private predicate intraBBSucc(ControlFlowNode pred, ControlFlowNode succ) { - succ = pred.getASuccessor() and - not startsBB(succ) - } - - /** - * Holds if `cfn` is the `i`th node in basic block `bb`. - * - * In other words, `i` is the shortest distance from a node `bb` - * that starts a basic block to `cfn` along the `intraBBSucc` relation. - */ - cached - predicate bbIndex(ControlFlowNode bbStart, ControlFlowNode cfn, int i) = - shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) - - /** - * Holds if the first node of basic block `succ` is a control flow - * successor of the last node of basic block `pred`. - */ - private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() } - - /** Holds if `dom` is an immediate dominator of `bb`. */ - cached - predicate bbIDominates(BasicBlock dom, BasicBlock bb) = - idominance(entryBB/1, succBB/2)(_, dom, bb) - - /** Holds if `pred` is a basic block predecessor of `succ`. */ - private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } - - /** Holds if `bb` is an exit basic block that represents normal exit. */ - private predicate normalExitBB(BasicBlock bb) { - bb.getANode().(Impl::AnnotatedExitNode).isNormal() - } - - /** Holds if `dom` is an immediate post-dominator of `bb`. */ - cached - predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = - idominance(normalExitBB/1, predBB/2)(_, dom, bb) - - /** - * Gets the `i`th predecessor of join block `jb`, with respect to some - * arbitrary order. - */ - cached - JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) { - result = - rank[i + 1](JoinBlockPredecessor jbp | - jbp = jb.getAPredecessor() - | - jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) - ) - } -} - -private import Cached - -/** Holds if `bb` is an entry basic block. */ -private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode } - /** * An entry basic block, that is, a basic block whose first node is * an entry node. */ -class EntryBasicBlock extends BasicBlock { - EntryBasicBlock() { entryBB(this) } - - override CfgScope getScope() { - this.getFirstNode() = any(EntryNode node | node.getScope() = result) - } -} +final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { } /** * An annotated exit basic block, that is, a basic block whose last node is * an annotated exit node. */ -class AnnotatedExitBasicBlock extends BasicBlock { - private boolean normal; - - AnnotatedExitBasicBlock() { - exists(Impl::AnnotatedExitNode n | - n = this.getANode() and - if n.isNormal() then normal = true else normal = false - ) - } - - /** Holds if this block represent a normal exit. */ - final predicate isNormal() { normal = true } -} +final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { } /** * An exit basic block, that is, a basic block whose last node is * an exit node. */ -class ExitBasicBlock extends BasicBlock { - ExitBasicBlock() { this.getLastNode() instanceof ExitNode } -} - -private module JoinBlockPredecessors { - private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y } - - private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y) - - // TODO does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block - private predicate idOf(AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) } - - private AstNode projectToAst(ControlFlowElement n) { - result = n.asAstNode() - or - isPropertyGetterElement(n, _, result) - or - isPropertySetterElement(n, _, result) - or - isPropertyObserverElement(n, _, result) - or - result = n.(KeyPathElement).getAst() - or - result = n.(FuncDeclElement).getAst() - } - - int getId(JoinBlockPredecessor jbp) { - idOf(projectToAst(jbp.getFirstNode().(CfgNode).getNode()), result) - or - idOf(jbp.(EntryBasicBlock).getScope(), result) - } - - string getSplitString(JoinBlockPredecessor jbp) { - result = jbp.getFirstNode().(CfgNode).getSplitsString() - or - not exists(jbp.getFirstNode().(CfgNode).getSplitsString()) and - result = "" - } -} +final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { } /** A basic block with more than one predecessor. */ -class JoinBlock extends BasicBlock { +final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock { JoinBlock() { this.getFirstNode().isJoin() } - /** - * Gets the `i`th predecessor of this join block, with respect to some - * arbitrary order. - */ - JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) } + JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) } } /** A basic block that is an immediate predecessor of a join block. */ -class JoinBlockPredecessor extends BasicBlock { - JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock } -} +class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { } /** A basic block that terminates in a condition, splitting the subsequent control flow. */ -class ConditionBlock extends BasicBlock { - ConditionBlock() { this.getLastNode().isCondition() } - - /** - * Holds if basic block `succ` is immediately controlled by this basic - * block with conditional value `s`. That is, `succ` is an immediate - * successor of this block, and `succ` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ - pragma[nomagic] - predicate immediatelyControls(BasicBlock succ, SuccessorType s) { - succ = this.getASuccessor(s) and - forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred)) +final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock { + predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) { + super.immediatelyControls(succ, s) } - /** - * Holds if basic block `controlled` is controlled by this basic block with - * conditional value `s`. That is, `controlled` can only be reached from - * the callable entry point by going via the `s` edge out of this basic block. - */ - predicate controls(BasicBlock controlled, BooleanSuccessor s) { - exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled)) + predicate controls(BasicBlock controlled, ConditionalSuccessor s) { + super.controls(controlled, s) } } diff --git a/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll b/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll index 96a005c22b63..4d1be28b0012 100644 --- a/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll +++ b/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll @@ -3,18 +3,18 @@ private import swift private import BasicBlocks private import SuccessorTypes -private import internal.ControlFlowGraphImpl +private import internal.ControlFlowGraphImpl as CfgImpl private import internal.Completion private import internal.Scope private import internal.ControlFlowElements /** An AST node with an associated control-flow graph. */ -class CfgScope extends Scope instanceof CfgScope::Range_ { +class CfgScope extends Scope instanceof CfgImpl::CfgScope::Range_ { /** Gets the CFG scope that this scope is nested under, if any. */ final CfgScope getOuterCfgScope() { exists(ControlFlowElement parent | parent.asAstNode() = getParentOfAst(this) and - result = getCfgScope(parent) + result = CfgImpl::getCfgScope(parent) ) } } @@ -27,7 +27,7 @@ class CfgScope extends Scope instanceof CfgScope::Range_ { * * Only nodes that can be reached from an entry point are included in the CFG. */ -class ControlFlowNode extends Node { +class ControlFlowNode extends CfgImpl::Node { /** Gets the AST node that this node corresponds to, if any. */ ControlFlowElement getNode() { none() } @@ -63,7 +63,7 @@ class ControlFlowNode extends Node { } /** The type of a control flow successor. */ -class SuccessorType extends TSuccessorType { +class SuccessorType extends CfgImpl::TSuccessorType { /** Gets a textual representation of successor type. */ string toString() { none() } } @@ -71,7 +71,7 @@ class SuccessorType extends TSuccessorType { /** Provides different types of control flow successor types. */ module SuccessorTypes { /** A normal control flow successor. */ - class NormalSuccessor extends SuccessorType, TSuccessorSuccessor { + class NormalSuccessor extends SuccessorType, CfgImpl::TSuccessorSuccessor { final override string toString() { result = "successor" } } @@ -89,24 +89,24 @@ module SuccessorTypes { } /** A Boolean control flow successor. */ - class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor { - BooleanSuccessor() { this = TBooleanSuccessor(value) } + class BooleanSuccessor extends ConditionalSuccessor, CfgImpl::TBooleanSuccessor { + BooleanSuccessor() { this = CfgImpl::TBooleanSuccessor(value) } } - class BreakSuccessor extends SuccessorType, TBreakSuccessor { + class BreakSuccessor extends SuccessorType, CfgImpl::TBreakSuccessor { final override string toString() { result = "break" } } - class ContinueSuccessor extends SuccessorType, TContinueSuccessor { + class ContinueSuccessor extends SuccessorType, CfgImpl::TContinueSuccessor { final override string toString() { result = "continue" } } - class ReturnSuccessor extends SuccessorType, TReturnSuccessor { + class ReturnSuccessor extends SuccessorType, CfgImpl::TReturnSuccessor { final override string toString() { result = "return" } } - class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor { - MatchingSuccessor() { this = TMatchingSuccessor(value) } + class MatchingSuccessor extends ConditionalSuccessor, CfgImpl::TMatchingSuccessor { + MatchingSuccessor() { this = CfgImpl::TMatchingSuccessor(value) } /** Holds if this is a match successor. */ predicate isMatch() { value = true } @@ -114,19 +114,19 @@ module SuccessorTypes { override string toString() { if this.isMatch() then result = "match" else result = "no-match" } } - class FallthroughSuccessor extends SuccessorType, TFallthroughSuccessor { + class FallthroughSuccessor extends SuccessorType, CfgImpl::TFallthroughSuccessor { final override string toString() { result = "fallthrough" } } - class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor { - EmptinessSuccessor() { this = TEmptinessSuccessor(value) } + class EmptinessSuccessor extends ConditionalSuccessor, CfgImpl::TEmptinessSuccessor { + EmptinessSuccessor() { this = CfgImpl::TEmptinessSuccessor(value) } predicate isEmpty() { value = true } override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" } } - class ExceptionSuccessor extends SuccessorType, TExceptionSuccessor { + class ExceptionSuccessor extends SuccessorType, CfgImpl::TExceptionSuccessor { override string toString() { result = "exception" } } } diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll index 362537068a63..b74ca9d07990 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll @@ -15,6 +15,8 @@ import Completion private import codeql.swift.controlflow.ControlFlowGraph as Cfg private import Splitting as Splitting private import Scope +private import codeql.swift.generated.Raw +private import codeql.swift.generated.Synth import ControlFlowElements import AstControlFlowTrees @@ -82,6 +84,31 @@ module CfgInput implements InputSig { predicate scopeLast(CfgScope scope, AstNode last, Completion c) { scope.(Impl::CfgScope::Range_).exit(last, c) } + + private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y } + + private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y) + + // TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block + private predicate idOf(S::AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) } + + private S::AstNode projectToAst(ControlFlowElement n) { + result = n.asAstNode() + or + isPropertyGetterElement(n, _, result) + or + isPropertySetterElement(n, _, result) + or + isPropertyObserverElement(n, _, result) + or + result = n.(KeyPathElement).getAst() + or + result = n.(FuncDeclElement).getAst() + } + + predicate idOfAstNode(AstNode node, int id) { idOf(projectToAst(node), id) } + + predicate idOfCfgScope(CfgScope node, int id) { idOf(node, id) } } private module CfgSplittingInput implements SplittingInputSig { diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index 971cc8cc705c..e8ad8cbdcebf 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -21,7 +21,7 @@ module Ssa { BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } - class ExitBasicBlock extends BasicBlock, BasicBlocks::ExitBasicBlock { } + class ExitBasicBlock = BasicBlocks::ExitBasicBlock; private newtype TSourceVariable = TNormalSourceVariable(VarDecl v) or