From 443429a7ebd88ed35dcdf0ed9516ac27971888cf Mon Sep 17 00:00:00 2001 From: zhuyunxing Date: Wed, 10 Jul 2024 10:31:28 +0800 Subject: [PATCH] coverage. Instrument mcdc for pattern matching --- compiler/rustc_middle/src/mir/coverage.rs | 2 + .../src/build/coverageinfo/mcdc.rs | 29 +- .../src/build/coverageinfo/mcdc/matching.rs | 846 ++++++++++++++++++ .../rustc_mir_build/src/build/matches/mod.rs | 45 +- .../rustc_mir_build/src/build/matches/test.rs | 37 +- tests/coverage/mcdc/if_let.cov-map | 292 ++++++ tests/coverage/mcdc/if_let.coverage | 272 ++++++ tests/coverage/mcdc/if_let.rs | 83 ++ tests/coverage/mcdc/match_misc.cov-map | 445 +++++++++ tests/coverage/mcdc/match_misc.coverage | 511 +++++++++++ tests/coverage/mcdc/match_misc.rs | 176 ++++ tests/coverage/mcdc/match_pattern.cov-map | 334 +++++++ tests/coverage/mcdc/match_pattern.coverage | 392 ++++++++ tests/coverage/mcdc/match_pattern.rs | 100 +++ 14 files changed, 3552 insertions(+), 12 deletions(-) create mode 100644 compiler/rustc_mir_build/src/build/coverageinfo/mcdc/matching.rs create mode 100644 tests/coverage/mcdc/if_let.cov-map create mode 100644 tests/coverage/mcdc/if_let.coverage create mode 100644 tests/coverage/mcdc/if_let.rs create mode 100644 tests/coverage/mcdc/match_misc.cov-map create mode 100644 tests/coverage/mcdc/match_misc.coverage create mode 100644 tests/coverage/mcdc/match_misc.rs create mode 100644 tests/coverage/mcdc/match_pattern.cov-map create mode 100644 tests/coverage/mcdc/match_pattern.coverage create mode 100644 tests/coverage/mcdc/match_pattern.rs diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 75c5e01c5c753..464d0f8714021 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -398,6 +398,8 @@ impl Default for CandidateCovId { } impl CandidateCovId { + /// Return `true` is this `CandidateCovId` is assigned properly through coverage mechanism + /// and can be mapped as a decision. pub fn is_valid(&self) -> bool { *self != Self::default() } diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs index ba97609c80152..023408eb6ccc3 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs @@ -1,3 +1,5 @@ +mod matching; +use matching::{LateMatchingState, MatchingDecisionCtx}; use std::cell::Cell; use std::collections::VecDeque; use std::rc::Rc; @@ -174,14 +176,17 @@ impl BooleanDecisionCtx { #[derive(Debug)] enum DecisionCtx { Boolean(BooleanDecisionCtx), - #[allow(unused)] - Matching, + Matching(MatchingDecisionCtx), } impl DecisionCtx { fn new_boolean(id: DecisionId) -> Self { Self::Boolean(BooleanDecisionCtx::new(id)) } + + fn new_matching(ids: &[DecisionId]) -> Self { + Self::Matching(MatchingDecisionCtx::new(ids)) + } } pub(crate) struct MCDCStateGuard { @@ -270,6 +275,7 @@ pub(crate) struct MCDCInfoBuilder { normal_branch_spans: Vec, mcdc_targets: FxIndexMap, state_stack: Vec, + late_matching_state: LateMatchingState, decision_id_gen: DecisionIdGen, } @@ -279,6 +285,7 @@ impl MCDCInfoBuilder { normal_branch_spans: vec![], mcdc_targets: FxIndexMap::default(), state_stack: vec![], + late_matching_state: Default::default(), decision_id_gen: DecisionIdGen::default(), } } @@ -293,6 +300,11 @@ impl MCDCInfoBuilder { &mut self.state_stack[current_idx] } + fn current_processing_ctx_mut(&mut self) -> Option<&mut DecisionCtx> { + self.ensure_active_state(); + self.state_stack.last_mut().and_then(|state| state.current_ctx.as_mut()) + } + fn ensure_active_state(&mut self) { let mut active_state_idx = None; // Down to the first non-stashed state or non-empty state, which can be ensured to be @@ -445,17 +457,26 @@ impl MCDCInfoBuilder { let (id, decision, conditions) = ctx.into_done(); let info = MCDCTargetInfo { decision, conditions, nested_decisions_id }; - let entry_decision_id = self.append_mcdc_info(tcx, id, info).then_some(id); - self.on_ctx_finished(tcx, entry_decision_id); + if self.late_matching_state.is_guard_decision(id) { + self.late_matching_state.add_guard_decision(id, info); + } else { + let entry_id = self.append_mcdc_info(tcx, id, info).then_some(id); + self.on_ctx_finished(tcx, entry_id) + } } pub(crate) fn into_done( self, ) -> (Vec, Vec<(MCDCDecisionSpan, Vec)>) { + assert!( + !self.has_processing_decision() && self.late_matching_state.is_empty(), + "has unfinished decisions" + ); let MCDCInfoBuilder { normal_branch_spans, mcdc_targets, state_stack: _, + late_matching_state: _, decision_id_gen: _, } = self; diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc/matching.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc/matching.rs new file mode 100644 index 0000000000000..e7f4787c24835 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc/matching.rs @@ -0,0 +1,846 @@ +use std::collections::VecDeque; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_middle::bug; +use rustc_middle::mir::coverage::{ + BlockMarkerId, CandidateCovId, ConditionId, ConditionInfo, DecisionId, MCDCBranchSpan, + MCDCDecisionSpan, MatchCoverageInfo, MatchKey, MatchPairId, SubcandidateId, +}; +use rustc_middle::mir::{BasicBlock, SourceInfo, TerminatorKind}; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; + +use crate::build::coverageinfo::mcdc::{DecisionCtx, MCDCInfoBuilder, MCDCTargetInfo}; +use crate::build::matches::Candidate; +use crate::build::{Builder, CFG}; + +/// Represent a matched target. This target might contain several +/// patterns from different candidates. +/// +/// We represents the process of matching as a graph. +/// +/// For instance, pattern decision `(A, B, C | D(E), F | G)` generates graph like: +/// * +/// | +/// A +/// | +/// B +/// ∨------∧------∨ +/// C D +/// | | +/// | E +/// ∧------∨------∧ +/// * +/// ∨------∧------∨ +/// F G +/// `*` represents a virtual node. Matching graph have following properties: +/// * Every subcandidate is represented as a branch in the graph. +/// * Start with a virtual node, because we always start from the root subcandidate. (Remember that the root subcandidate might have no pattern) +/// * If two or more branches are merged, they are merged into a virtual node. +/// +/// With the help of matching graph we can construct decision tree of the pattern: +/// * The true next of a node is its leftmost successor. +/// * The false next of a node is its sibling node at right sharing same predecssor. +/// * If (true/false) next of a node is a virtual node, the actual next is the (true/false) next of the virtual node. +/// +/// Specific to this graph, +/// * `A` and `B` are located in root subcandidate. +/// * `C` is located in subcandidate 1. +/// * `D`, `E` are located in subcandidate 2. +/// * `F` is located in subcandidate 3. +/// * `G` is located in subcandidate 4. +/// +/// Thus the true next of `A` is `B`, the false next is none (this pattern is not matched if failed to match `A`). +/// The true next of `B` is `C`, the false next is none. +/// The true next of `C` is `F` (the true next of a virtual node), the false next is `D`. +/// The true next of `D` is `E`, the false next is none. +/// The true next of `E` is `F` (through a virtual node), the false next is none. +/// The true next of `F` is none (end matching here), the false next is `G`. +/// The true and false next of `G` both are none. +#[derive(Debug, Default)] +struct MatchNode { + matched_keys: Vec, + // Index in matching graph of the next tested node if this node is evaluated to true. + true_next: Option, + // Index in matching graph of the next tested node if this node is evaluated to false. + false_next: Option, + // A match node is virtual if it is not related to any patterns. + is_virtual: bool, +} + +impl MatchNode { + fn real_node(matched_keys: Vec) -> Self { + Self { matched_keys, true_next: None, false_next: None, is_virtual: false } + } + + fn virtual_node(subcandidate_id: SubcandidateId) -> Self { + let key = MatchKey { + decision_id: DecisionId::ZERO, + subcandidate_id, + match_id: MatchPairId(usize::MAX), + }; + Self { matched_keys: vec![key], true_next: None, false_next: None, is_virtual: true } + } + + fn node_id(&self) -> MatchPairId { + self.matched_keys.first().expect("MatchNode must have at least one key").match_id + } +} + +// Information about patterns generating conditions. +#[derive(Debug)] +struct MatchPairInfo { + span: Span, + fully_matched_block_pairs: Vec<(BasicBlock, BasicBlock)>, +} + +// Context to process a pattern decision (without guard). +#[derive(Debug)] +struct CandidateCtx { + latest_subcandidate_id: SubcandidateId, + parent_subcandidate_map: FxHashMap, + last_child_node_map: FxHashMap, + match_pairs_info: FxHashMap, + matching_graph: Vec, +} + +impl CandidateCtx { + fn new() -> Self { + Self { + latest_subcandidate_id: SubcandidateId::root(), + parent_subcandidate_map: FxHashMap::default(), + last_child_node_map: FxHashMap::default(), + match_pairs_info: FxHashMap::default(), + matching_graph: vec![MatchNode::virtual_node(SubcandidateId::root())], + } + } + fn next_subcandidate_in(&mut self, parent_subcandidate_id: SubcandidateId) -> SubcandidateId { + let id = self.latest_subcandidate_id.next_subcandidate_id(); + self.latest_subcandidate_id = id; + self.parent_subcandidate_map.insert(id, parent_subcandidate_id); + id + } + + fn on_visiting_patterns(&mut self, matched_info: Vec) { + let keys = matched_info + .into_iter() + .filter_map(|info| info.fully_matched.then_some(info.key)) + .collect::>(); + + if keys.is_empty() { + return; + } + + let parent_subcandidate_id = + self.parent_subcandidate_map.get(&keys[0].subcandidate_id).copied(); + assert!( + keys.iter().skip(1).all(|key| self + .parent_subcandidate_map + .get(&key.subcandidate_id) + .copied() + == parent_subcandidate_id), + "Patterns simultaneously matched should have same parent subcandidate" + ); + let current_node = MatchNode::real_node(keys); + let is_predecessor_of_current = |node: &MatchNode| { + if current_node.matched_keys.iter().all(|this_matched| { + node.matched_keys + .iter() + .any(|prev| prev.subcandidate_id == this_matched.subcandidate_id) + }) { + return true; + } + parent_subcandidate_id.is_some_and(|parent_subcandidate| { + node.matched_keys.iter().any(|prev| prev.subcandidate_id == parent_subcandidate) + }) + }; + if let Some(predecessor_idx) = self + .matching_graph + .iter() + .rev() + .position(is_predecessor_of_current) + .map(|rev_pos| self.matching_graph.len() - (1 + rev_pos)) + { + let current_idx = self.matching_graph.len(); + if self.matching_graph[predecessor_idx].true_next.is_none() { + self.matching_graph[predecessor_idx].true_next = Some(current_idx); + } + if let Some(elder_sibling_idx) = + self.last_child_node_map.insert(predecessor_idx, current_idx) + { + self.matching_graph[elder_sibling_idx].false_next = Some(current_idx); + } + } + self.matching_graph.push(current_node); + } + + fn on_matching_patterns( + &mut self, + test_block: BasicBlock, + matched_block: BasicBlock, + matched_infos: Vec, + ) { + for matched in matched_infos { + let info = self.match_pairs_info.entry(matched.key.match_id).or_insert_with(|| { + MatchPairInfo { span: matched.span, fully_matched_block_pairs: vec![] } + }); + if matched.fully_matched { + info.fully_matched_block_pairs.push((test_block, matched_block)); + } + } + } + + fn on_merging_subcandidates( + &mut self, + next_subcandidate_id: SubcandidateId, + merging_subcandidate_ids: impl Iterator, + ) { + let merged_node = MatchNode::virtual_node(next_subcandidate_id); + let current_idx = self.matching_graph.len(); + let mut merging_subcandidates = merging_subcandidate_ids.collect::>(); + 'r: for prev_node in self.matching_graph.iter_mut().rev() { + for key in &prev_node.matched_keys { + if merging_subcandidates.remove(&key.subcandidate_id) { + assert!( + prev_node.true_next.is_none(), + "merged node must be the tail of its branch" + ); + prev_node.true_next = Some(current_idx); + if merging_subcandidates.is_empty() { + break 'r; + } + } + } + } + self.matching_graph.push(merged_node); + } + + fn generate_condition_info(&self) -> FxIndexMap { + let mut condition_infos_map = FxIndexMap::::default(); + let mut condition_counter = 0; + let mut new_condition_info = || { + let condition_id = ConditionId::from_usize(condition_counter); + condition_counter += 1; + ConditionInfo { condition_id, true_next_id: None, false_next_id: None } + }; + let find_next_node = |next_idx: Option, branch: bool| -> Option<&MatchNode> { + let mut next_node = &self.matching_graph[next_idx?]; + while next_node.is_virtual { + let next_idx = if branch { next_node.true_next } else { next_node.false_next }; + next_node = &self.matching_graph[next_idx?]; + } + Some(next_node) + }; + + for node in &self.matching_graph { + if node.is_virtual { + continue; + } + condition_infos_map.entry(node.node_id()).or_insert_with(&mut new_condition_info); + + let [true_next_id, false_next_id] = [(true, node.true_next), (false, node.false_next)] + .map(|(branch, next_idx)| { + find_next_node(next_idx, branch).map(|next_node| { + condition_infos_map + .entry(next_node.node_id()) + .or_insert_with(&mut new_condition_info) + .condition_id + }) + }); + + let condition_info = + condition_infos_map.get_mut(&node.node_id()).expect("ensured to be inserted above"); + + assert!( + condition_info.true_next_id.is_none() + || condition_info.true_next_id == true_next_id, + "only has one true next" + ); + assert!( + condition_info.false_next_id.is_none() + || condition_info.false_next_id == false_next_id, + "only has one false next" + ); + + condition_info.true_next_id = true_next_id; + condition_info.false_next_id = false_next_id; + } + condition_infos_map + } + + fn into_matching_decision( + self, + mut into_branch_blocks: impl FnMut( + Span, + &[(BasicBlock, BasicBlock)], + &FxIndexSet, + ) -> (Vec, Vec), + ) -> MCDCTargetInfo { + // Note. Conditions tested in same blocks are from different subcandidates. + // Since one condition might be the `false next` of another, it can not update + // condbitmap in another's matched block as if it is evaluated to `false` (though we know it is in fact), + // otherwise we may not update test vector index right. `matched_blocks_in_decision` here is used to record + // such blocks. + // In future we could differentiate blocks updating condbitmap from blocks increment counts to get + // better results. + let mut matched_blocks_in_decision = FxIndexSet::default(); + let conditions: Vec<_> = self + .generate_condition_info() + .into_iter() + .map(|(match_id, condition_info)| { + let &MatchPairInfo { span, ref fully_matched_block_pairs } = + self.match_pairs_info.get(&match_id).expect("all match pairs are recorded"); + + matched_blocks_in_decision + .extend(fully_matched_block_pairs.iter().map(|pair| pair.1)); + let (true_markers, false_markers) = into_branch_blocks( + span, + fully_matched_block_pairs, + &matched_blocks_in_decision, + ); + + MCDCBranchSpan { span, condition_info, true_markers, false_markers } + }) + .collect(); + let decision = MCDCDecisionSpan::new(Span::default()); + MCDCTargetInfo { decision, conditions, nested_decisions_id: vec![] } + } +} + +#[derive(Debug)] +pub struct MatchingDecisionCtx { + candidates: FxIndexMap, + test_blocks: FxIndexMap>, +} + +impl MatchingDecisionCtx { + pub fn new(candidates: &[DecisionId]) -> Self { + Self { + candidates: candidates.into_iter().map(|&id| (id, CandidateCtx::new())).collect(), + test_blocks: FxIndexMap::default(), + } + } + + fn visit_conditions(&mut self, patterns: &Vec) { + for (decision_id, infos) in group_match_info_by_decision(patterns) { + let candidate_ctx = self.candidates.get_mut(&decision_id).expect("unknown candidate"); + candidate_ctx.on_visiting_patterns(infos); + } + } + + fn match_conditions( + &mut self, + cfg: &mut CFG<'_>, + test_block: BasicBlock, + target_patterns: impl Iterator)>, + ) { + let mut otherwise_blocks = FxIndexSet::default(); + let mut matched_blocks = Vec::with_capacity(target_patterns.size_hint().0); + for (block, patterns) in target_patterns { + if patterns.is_empty() { + otherwise_blocks.insert(block); + continue; + } + for (decision_id, infos) in group_match_info_by_decision(&patterns) { + let candidate_ctx = + self.candidates.get_mut(&decision_id).expect("unknown candidate"); + candidate_ctx.on_matching_patterns(test_block, block, infos); + } + matched_blocks.push(block); + } + let fail_block = + find_fail_block(cfg, test_block, matched_blocks.iter().copied(), otherwise_blocks); + self.test_blocks + .insert(test_block, matched_blocks.into_iter().chain(fail_block.into_iter()).collect()); + } + + fn finish_matching_tree( + self, + mut inject_block_marker: impl FnMut(Span, BasicBlock) -> BlockMarkerId, + ) -> FxIndexMap { + let mut block_markers_map = FxHashMap::::default(); + + let mut into_branch_blocks = + |span: Span, + test_match_pairs: &[(BasicBlock, BasicBlock)], + excluded_unmatched_blocks: &FxIndexSet| { + let mut true_markers = Vec::with_capacity(test_match_pairs.len()); + let mut false_markers = Vec::with_capacity(test_match_pairs.len()); + let mut into_marker = |block: BasicBlock| { + *block_markers_map + .entry(block) + .or_insert_with(|| inject_block_marker(span, block)) + }; + for &(test_block, matched_block) in test_match_pairs { + true_markers.push(into_marker(matched_block)); + false_markers.extend( + self.test_blocks + .get(&test_block) + .expect("all test blocks must be recorded") + .into_iter() + .copied() + .filter_map(|block| { + (block != matched_block + && !excluded_unmatched_blocks.contains(&block)) + .then(|| into_marker(block)) + }), + ); + } + (true_markers, false_markers) + }; + self.candidates + .into_iter() + .map(|(decision_id, candidate_ctx)| { + (decision_id, candidate_ctx.into_matching_decision(&mut into_branch_blocks)) + }) + .collect() + } +} + +fn group_match_info_by_decision( + matched_infos: &[MatchCoverageInfo], +) -> impl IntoIterator)> { + let mut keys_by_decision = FxIndexMap::>::default(); + for pattern_info in matched_infos { + keys_by_decision + .entry(pattern_info.key.decision_id) + .or_default() + .push(pattern_info.clone()); + } + keys_by_decision +} + +/// Upon testing there probably is a block to go if all patterns failed to match. +/// We should increment false count and update condbitmap index of all tested conditions in this block. +/// Considering the `otherwise_block` might be reused by several tests, we inject a `fail_block` +/// here to do such stuff. +fn find_fail_block( + cfg: &mut CFG<'_>, + test_block: BasicBlock, + matched_blocks: impl Iterator, + otherwise_blocks: FxIndexSet, +) -> Vec { + let matched_blocks = FxIndexSet::from_iter(matched_blocks); + let mut prev_fail_blocks = vec![]; + let mut blocks = VecDeque::from([test_block]); + // Some tests might contain multiple sub tests. For example, to test range pattern `0..5`, first + // test if the value >= 0, then test if the value < 5. So `matched_block` might not be a successor of + // the `test_block` and there are two blocks which both are predecessors of `fail_block`. + while let Some(block) = blocks.pop_front() { + for successor in cfg.block_data(block).terminator().successors() { + if matched_blocks.contains(&successor) { + continue; + } else if otherwise_blocks.contains(&successor) { + prev_fail_blocks.push(block); + } else { + blocks.push_back(successor); + } + } + } + otherwise_blocks + .into_iter() + .map(|otherwise_block| { + let fail_block = cfg.start_new_block(); + cfg.terminate( + fail_block, + cfg.block_data(test_block).terminator().source_info, + TerminatorKind::Goto { target: otherwise_block }, + ); + + for &prev_block in &prev_fail_blocks { + let otherwise_ref = cfg + .block_data_mut(prev_block) + .terminator_mut() + .successors_mut() + .find(|block| **block == otherwise_block) + .expect("otherwise_block is ensured to be one of successors above"); + *otherwise_ref = fail_block; + } + fail_block + }) + .collect() +} + +/// Context handling matching decisions with if guards. +/// After lowering matching tree, rustc build mir for if guards and code in arms candidate by candidate. +/// In case decisions in arm blocks are taken as nested decisions, the unfinished candidates are moved into +/// this context to wait for their guards. +#[derive(Debug)] +struct LateMatchingCtx { + candidates: FxIndexMap, + finished_arms_count: usize, + otherwise_block: Option, + nested_decisions_in_guards: Vec, +} + +impl LateMatchingCtx { + fn finish_arm( + &mut self, + decision_id: DecisionId, + decision_span_hint: Span, + arm_block: BlockMarkerId, + guard_info: Option, + ) { + let Some(MCDCTargetInfo { decision, conditions, .. }) = + self.candidates.get_mut(&decision_id) + else { + return; + }; + + decision.span = decision_span_hint; + decision.update_end_markers.push(arm_block); + if let Some(mut guard) = guard_info { + decision.span = decision.span.to(guard.decision.span); + let rebase_condition_id = + |id: ConditionId| ConditionId::from_usize(id.as_usize() + conditions.len()); + for branch in &mut guard.conditions { + let ConditionInfo { condition_id, true_next_id, false_next_id } = + &mut branch.condition_info; + *condition_id = rebase_condition_id(*condition_id); + *true_next_id = true_next_id.map(rebase_condition_id); + *false_next_id = false_next_id.map(rebase_condition_id); + } + let guard_entry_id = rebase_condition_id(ConditionId::START); + conditions + .iter_mut() + .filter(|branch| branch.condition_info.true_next_id.is_none()) + .for_each(|branch| branch.condition_info.true_next_id = Some(guard_entry_id)); + conditions.extend(guard.conditions); + self.nested_decisions_in_guards.extend(guard.nested_decisions_id); + } + self.finished_arms_count += 1; + } + + fn all_arms_finished(&self) -> bool { + self.finished_arms_count == self.candidates.len() + } + + fn into_done(mut self) -> (FxIndexMap, Vec) { + let Some(all_unmatched_block) = self.otherwise_block.or_else(|| { + self.candidates.pop().map(|(_, target_info)| target_info.decision.update_end_markers[0]) + }) else { + return (Default::default(), vec![]); + }; + // Update test vector bits of a candidate in arm blocks of it and all candidates below it. + let mut unmatched_blocks: Vec<_> = self + .candidates + .values() + .skip(1) + .map(|target| target.decision.update_end_markers[0]) + .chain(std::iter::once(all_unmatched_block)) + .rev() + .collect(); + // Discard condition bitmap of a candidate in arm blocks of all candidates above it. + // This is to avoid weird result if multiple candidates all match the value. + let mut discard_blocks = vec![]; + for target in self.candidates.values_mut() { + target.decision.update_end_markers.extend(unmatched_blocks.clone()); + target.decision.discard_end_markers.extend(discard_blocks.clone()); + unmatched_blocks.pop(); + discard_blocks.push(target.decision.update_end_markers[0]); + } + (self.candidates, self.nested_decisions_in_guards) + } +} + +#[derive(Debug, Default)] +pub struct LateMatchingState { + matching_ctx: Vec, + matching_guard_map: FxIndexMap, + guard_decisions_info: FxIndexMap>, +} + +impl LateMatchingState { + pub fn is_empty(&self) -> bool { + self.matching_ctx.is_empty() + && self.guard_decisions_info.is_empty() + && self.matching_guard_map.is_empty() + } + + fn push_ctx(&mut self, ctx: LateMatchingCtx) { + self.matching_ctx.push(ctx); + } + + fn check_decision_exist(&self, decision_id: DecisionId) -> bool { + let Some(ctx) = self.matching_ctx.last() else { return false }; + let [min, max] = [ctx.candidates.first(), ctx.candidates.last()] + .map(|opt| *opt.expect("ctx must have candidates").0); + min <= decision_id && decision_id <= max + } + + fn declare_guard_for( + &mut self, + decision: DecisionId, + new_guard_id: impl FnOnce() -> DecisionId, + ) -> Option { + if !self.check_decision_exist(decision) { + return None; + } + + let guard_id = self.matching_guard_map.entry(decision).or_insert_with(|| { + let guard_id = new_guard_id(); + self.guard_decisions_info.insert(guard_id, None); + guard_id + }); + + Some(*guard_id) + } + + pub fn is_guard_decision(&self, boolean_decision_id: DecisionId) -> bool { + self.guard_decisions_info.contains_key(&boolean_decision_id) + } + + pub fn add_guard_decision(&mut self, boolean_decision_id: DecisionId, info: MCDCTargetInfo) { + self.guard_decisions_info.insert(boolean_decision_id, Some(info)); + } + + fn finish_arm( + &mut self, + id: DecisionId, + candidate_span: Span, + arm_block: BlockMarkerId, + ) -> Option { + let ctx = self.matching_ctx.last_mut()?; + let guard = self + .matching_guard_map + .swap_remove(&id) + .and_then(|guard_id| self.guard_decisions_info.swap_remove(&guard_id)) + .flatten(); + ctx.finish_arm(id, candidate_span, arm_block, guard); + if ctx.all_arms_finished() { self.matching_ctx.pop() } else { None } + } +} + +impl MCDCInfoBuilder { + fn create_pattern_decision(&mut self, num_candidates: usize) -> Vec { + self.ensure_active_state(); + let state = self.state_stack.last_mut().expect("ensured just now"); + let decision_ids: Vec<_> = + (0..num_candidates).map(|_| self.decision_id_gen.next_decision_id()).collect(); + assert!(state.is_empty(), "active state for new pattern decision must be empty"); + state.current_ctx = Some(DecisionCtx::new_matching(&decision_ids)); + decision_ids + } + + fn finish_matching_decision_tree( + &mut self, + otherwise_block: Option, + mut inject_block_marker: impl FnMut(Span, BasicBlock) -> BlockMarkerId, + ) { + let state = self.current_state_mut(); + + let Some((DecisionCtx::Matching(ctx), nested_decisions_id)) = state.take_ctx() else { + bug!("no processing pattern decision") + }; + assert!( + nested_decisions_id.is_empty(), + "no other decisions can be nested in matching tree" + ); + let otherwise_block = + otherwise_block.map(|block| inject_block_marker(Span::default(), block)); + let candidates = ctx.finish_matching_tree(inject_block_marker); + let late_ctx = LateMatchingCtx { + candidates, + finished_arms_count: 0, + otherwise_block, + nested_decisions_in_guards: nested_decisions_id, + }; + + self.late_matching_state.push_ctx(late_ctx); + } + + fn prepare_matching_guard(&mut self, decision_id: DecisionId) { + assert!( + self.current_state_mut().current_ctx.is_none(), + "When visit matching guard there should be no processing decisions" + ); + if let Some(guard_id) = self + .late_matching_state + .declare_guard_for(decision_id, || self.decision_id_gen.next_decision_id()) + { + self.current_state_mut().current_ctx = Some(DecisionCtx::new_boolean(guard_id)); + } + } + + fn visit_evaluated_matching_candidate( + &mut self, + tcx: TyCtxt<'_>, + decision_id: DecisionId, + candidate_span: Span, + arm_block: BlockMarkerId, + ) { + let Some(ctx) = self.late_matching_state.finish_arm(decision_id, candidate_span, arm_block) + else { + return; + }; + + let (targets, mut nested_decisions_id) = ctx.into_done(); + let mut entry_id = None; + for (id, mut info) in targets.into_iter().rev() { + info.nested_decisions_id = nested_decisions_id.clone(); + if self.append_mcdc_info(tcx, id, info) { + nested_decisions_id = vec![id]; + entry_id = Some(id); + } + } + self.on_ctx_finished(tcx, entry_id); + } +} + +impl Builder<'_, '_> { + /// Prepare for matching decisions if mcdc is enabled. The returned decision ids should be used to generate [`CandidateCovId`] for candidates. + /// Do nothing if mcdc is not enabled or the candidates is not proper for mcdc. + pub(crate) fn mcdc_create_matching_decisions( + &mut self, + candidates: &mut [&mut Candidate<'_, '_>], + refutable: bool, + ) { + let can_mapped_decisions = refutable || candidates.len() > 1; + if can_mapped_decisions + && let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + { + let ids = mcdc_info.create_pattern_decision(candidates.len()); + assert_eq!(ids.len(), candidates.len(), "every candidate must get a decision id"); + candidates.iter_mut().zip(ids.into_iter()).for_each(|(candidate, decision_id)| { + candidate.set_coverage_id(CandidateCovId { + decision_id, + subcandidate_id: SubcandidateId::root(), + }) + }); + } + } + + /// Create and assign [`CandidateCovId`] for subcandidates if mcdc is enabled. + /// Do nothing if mcdc is not enabled or the candidate is ignored. + pub(crate) fn mcdc_create_subcandidates( + &mut self, + candidate_id: CandidateCovId, + subcandidates: &mut [Candidate<'_, '_>], + ) { + if candidate_id.is_valid() + && let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + && let Some(DecisionCtx::Matching(ctx)) = mcdc_info.current_processing_ctx_mut() + && let Some(candidate_ctx) = ctx.candidates.get_mut(&candidate_id.decision_id) + { + subcandidates.iter_mut().for_each(|subcandidate| { + let id = CandidateCovId { + decision_id: candidate_id.decision_id, + subcandidate_id: candidate_ctx + .next_subcandidate_in(candidate_id.subcandidate_id), + }; + subcandidate.set_coverage_id(id); + }); + } + } + + /// Notify the mcdc builder that some candidates are merged. This can happen on or patterns without bindings. + /// Do nothing if mcdc is not enabled or the candidates is not proper for mcdc. + pub(crate) fn mcdc_merge_subcandidates( + &mut self, + candidate_id: CandidateCovId, + subcandidates: impl Iterator, + ) { + if candidate_id.is_valid() + && let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + && let Some(DecisionCtx::Matching(ctx)) = mcdc_info.current_processing_ctx_mut() + && let Some(candidate_ctx) = ctx.candidates.get_mut(&candidate_id.decision_id) + { + candidate_ctx.on_merging_subcandidates(candidate_id.subcandidate_id, subcandidates); + } + } + + /// Notify the mcdc builder some patterns are preparing for test. This function must be called in same order as match targets are determined. + /// Do nothing if mcdc is not enabled. + pub(crate) fn mcdc_visit_pattern_conditions<'a>( + &mut self, + patterns: impl Iterator>, + ) { + if let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + && let Some(DecisionCtx::Matching(ctx)) = mcdc_info.current_processing_ctx_mut() + { + patterns.for_each(|infos| ctx.visit_conditions(infos)); + } + } + + /// Inform the mcdc builder where the patterns are tested and the blocks to go if the patterns are matched. + /// Before this call `test_block` must be injected terminator. + /// If mcdc is not enabled do nothing. + pub(crate) fn mcdc_match_pattern_conditions( + &mut self, + test_block: BasicBlock, + target_patterns: impl Iterator)>, + ) { + if let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + && let Some(DecisionCtx::Matching(ctx)) = mcdc_info.current_processing_ctx_mut() + { + ctx.match_conditions(&mut self.cfg, test_block, target_patterns); + } + } + + /// Notify the mcdc builder matching tree is finished lowering. + /// This function should be called before diving into arms and guards. + /// The `otherwise_block` should be provided if and only if the candidates are from refutable statements (`if let` or `let else`). + /// Do nothing if mcdc is not enabled. + pub(crate) fn mcdc_finish_matching_tree( + &mut self, + candidates: &mut [&mut Candidate<'_, '_>], + otherwise_block: Option, + ) { + let has_decision = candidates.iter().any(|candidate| candidate.coverage_id().is_valid()); + if has_decision + && let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + { + let inject_block_marker = |span: Span, block: BasicBlock| { + coverage_info.markers.inject_block_marker( + &mut self.cfg, + SourceInfo { span, scope: self.source_scope }, + block, + ) + }; + mcdc_info.finish_matching_decision_tree(otherwise_block, inject_block_marker); + } + } + + /// Notify the mcdc builder a guard is to be lowered. + /// Do nothing if mcdc is not enabled or the candidates is not proper for mcdc. + pub(crate) fn mcdc_visit_matching_guard(&mut self, coverage_id: CandidateCovId) { + if coverage_id.is_valid() + && let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + { + mcdc_info.prepare_matching_guard(coverage_id.decision_id); + } + } + + /// Notify the mcdc builder a candidate has been totally processed. + /// Do nothing if mcdc is not enabled or the candidates is not proper for mcdc. + pub(crate) fn mcdc_visit_matching_decision_end( + &mut self, + coverage_id: CandidateCovId, + candidate_span: Span, + arm_block: BasicBlock, + ) { + if coverage_id.is_valid() + && let Some(coverage_info) = self.coverage_info.as_mut() + && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut() + { + let arm_marker = coverage_info.markers.inject_block_marker( + &mut self.cfg, + SourceInfo { span: candidate_span, scope: self.source_scope }, + arm_block, + ); + mcdc_info.visit_evaluated_matching_candidate( + self.tcx, + coverage_id.decision_id, + candidate_span, + arm_marker, + ); + } + } +} diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 67c0838dba6d2..8bc6ac36f33f5 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -483,6 +483,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_scrutinee_place, ); + let (coverage_id, candidate_span) = + (candidate.coverage_id, candidate.extra_data.span); + let arm_block = this.bind_pattern( outer_source_info, candidate, @@ -492,6 +495,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { EmitStorageLive::Yes, ); + this.mcdc_visit_matching_decision_end(coverage_id, candidate_span, arm_block); + this.fixed_temps_scope = old_dedup_scope; if let Some(source_scope) = scope { @@ -1056,7 +1061,7 @@ impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { } #[derive(Debug)] -struct Candidate<'pat, 'tcx> { +pub(crate) struct Candidate<'pat, 'tcx> { /// For the candidate to match, all of these must be satisfied... // Invariant: all the `MatchPair`s are recursively simplified. // Invariant: or-patterns must be sorted at the end. @@ -1089,7 +1094,7 @@ struct Candidate<'pat, 'tcx> { /// The `false_edge_start_block` of the next candidate. next_candidate_start_block: Option, - /// The id to identify the candidate in coverage instrument. + /// Identify the candidate in coverage instrument. coverage_id: coverage::CandidateCovId, } @@ -1133,6 +1138,10 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { ); } + pub(crate) fn coverage_id(&self) -> coverage::CandidateCovId { + self.coverage_id + } + pub(crate) fn set_coverage_id(&mut self, coverage_id: coverage::CandidateCovId) { self.coverage_id = coverage_id; // Assign id for match pairs only if this candidate is the root. @@ -1339,9 +1348,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // See the doc comment on `match_candidates` for why we have an otherwise block. let otherwise_block = self.cfg.start_new_block(); + self.mcdc_create_matching_decisions(candidates, refutable); + // This will generate code to test scrutinee_place and branch to the appropriate arm block self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates); + self.mcdc_finish_matching_tree(candidates, refutable.then_some(otherwise_block)); + // Link each leaf candidate to the `false_edge_start_block` of the next one. let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; for candidate in candidates { @@ -1769,6 +1782,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) .collect(); candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; + + self.mcdc_create_subcandidates(candidate.coverage_id, &mut candidate.subcandidates); } /// Try to merge all of the subcandidates of the given candidate into one. This avoids @@ -1793,6 +1808,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.false_edge_start_block = candidate.subcandidates[0].false_edge_start_block; } + self.mcdc_merge_subcandidates( + candidate.coverage_id, + candidate.subcandidates.iter().map(|cand| cand.coverage_id.subcandidate_id), + ); for subcandidate in mem::take(&mut candidate.subcandidates) { let or_block = subcandidate.pre_binding_block.unwrap(); self.cfg.goto(or_block, source_info, any_matches); @@ -1894,10 +1913,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> ( &'b mut [&'c mut Candidate<'pat, 'tcx>], FxIndexMap, Vec<&'b mut Candidate<'pat, 'tcx>>>, + Option, Vec>>, ) { // For each of the possible outcomes, collect vector of candidates that apply if the test // has that particular outcome. let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_, '_>>> = Default::default(); + let mut mcdc_match_records = (self.tcx.sess.instrument_coverage_mcdc() + && candidates.first().is_some_and(|candidate| candidate.coverage_id.is_valid())) + .then(FxIndexMap::default); let total_candidate_count = candidates.len(); @@ -1905,13 +1928,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // point we may encounter a candidate where the test is not relevant; at that point, we stop // sorting. while let Some(candidate) = candidates.first_mut() { - let Some(branch) = + let Some((branch, match_cov_info)) = self.sort_candidate(match_place, test, candidate, &target_candidates) else { break; }; let (candidate, rest) = candidates.split_first_mut().unwrap(); target_candidates.entry(branch).or_insert_with(Vec::new).push(candidate); + if let Some(records) = mcdc_match_records.as_mut() { + records.entry(branch).or_insert_with(Vec::new).push(match_cov_info); + } candidates = rest; } @@ -1923,7 +1949,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("tested_candidates: {}", total_candidate_count - candidates.len()); debug!("untested_candidates: {}", candidates.len()); - (candidates, target_candidates) + (candidates, target_candidates, mcdc_match_records) } /// This is the most subtle part of the match lowering algorithm. At this point, the input @@ -2033,9 +2059,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // For each of the N possible test outcomes, build the vector of candidates that applies if // the test has that particular outcome. - let (remaining_candidates, target_candidates) = + let (remaining_candidates, target_candidates, mcdc_match_records) = self.sort_candidates(match_place, &test, candidates); + if let Some(match_records) = mcdc_match_records.as_ref() { + self.mcdc_visit_pattern_conditions(match_records.values()); + } + // The block that we should branch to if none of the // `target_candidates` match. let remainder_start = if !remaining_candidates.is_empty() { @@ -2077,6 +2107,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_place, &test, target_blocks, + mcdc_match_records, ); } } @@ -2135,6 +2166,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + let coverage_id = candidate.coverage_id; let success = self.bind_pattern( self.source_info(pat.span), candidate, @@ -2144,6 +2176,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { emit_storage_live, ); + self.mcdc_visit_matching_decision_end(coverage_id, pat.span, success); + // If branch coverage is enabled, record this branch. self.visit_coverage_conditional_let(pat, success, otherwise_block); @@ -2310,6 +2344,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let (post_guard_block, otherwise_post_guard_block) = self.in_if_then_scope(match_scope, guard_span, |this| { guard_span = this.thir[guard].span; + this.mcdc_visit_matching_guard(candidate.coverage_id); this.then_else_break( block, guard, diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index d29874a5ad4ba..73779913a3bdf 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -70,11 +70,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place: Place<'tcx>, test: &Test<'tcx>, target_blocks: FxIndexMap, BasicBlock>, + mut target_coverage_info: Option< + FxIndexMap, Vec>, + >, ) { let place_ty = place.ty(&self.local_decls, self.tcx); debug!(?place, ?place_ty); let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block); - + let mut target_coverage_blocks = + FxIndexMap::>::default(); + let mut mcdc_add_matched_block = |branch: &TestBranch<'tcx>, success_block| { + let Some(coverage_info) = target_coverage_info.as_mut() else { + return; + }; + target_coverage_blocks + .insert(success_block, coverage_info.swap_remove(branch).unwrap_or_default()); + }; let source_info = self.source_info(test.span); match test.kind { TestKind::Switch { adt_def } => { @@ -82,6 +93,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let switch_targets = SwitchTargets::new( adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| { if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) { + mcdc_add_matched_block(&TestBranch::Variant(idx), block); Some((discr.val, block)) } else { None @@ -114,6 +126,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let switch_targets = SwitchTargets::new( target_blocks.iter().filter_map(|(&branch, &block)| { if let TestBranch::Constant(_, bits) = branch { + mcdc_add_matched_block(&branch, block); Some((bits, block)) } else { None @@ -131,6 +144,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestKind::If => { let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); + mcdc_add_matched_block(&TestBranch::Success, success_block); let terminator = TerminatorKind::if_(Operand::Copy(place), success_block, fail_block); self.cfg.terminate(block, self.source_info(match_start_span), terminator); @@ -140,6 +154,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); + mcdc_add_matched_block(&TestBranch::Success, success_block); if let ty::Adt(def, _) = ty.kind() && tcx.is_lang_item(def.did(), LangItem::String) { @@ -202,6 +217,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestKind::Range(ref range) => { let success = target_block(TestBranch::Success); let fail = target_block(TestBranch::Failure); + mcdc_add_matched_block(&TestBranch::Success, success); // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. let val = Operand::Copy(place); @@ -248,6 +264,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); + mcdc_add_matched_block(&TestBranch::Success, success_block); // result = actual == expected OR result = actual < expected // branch based on result self.compare( @@ -281,6 +298,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); } } + if let Some(mut coverage_info) = target_coverage_info { + // This argument should have been `fail_block` from each `TestKind` arm, but they all are the same now. + target_coverage_blocks.insert( + target_block(TestBranch::Failure), + coverage_info.swap_remove(&TestBranch::Failure).unwrap_or_default(), + ); + self.mcdc_match_pattern_conditions(block, target_coverage_blocks.into_iter()); + } } /// Perform `let temp = ::deref(&place)`. @@ -539,7 +564,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { test: &Test<'tcx>, candidate: &mut Candidate<'_, 'tcx>, sorted_candidates: &FxIndexMap, Vec<&mut Candidate<'_, 'tcx>>>, - ) -> Option> { + ) -> Option<(TestBranch<'tcx>, coverage::MatchCoverageInfo)> { // Find the match_pair for this place (if any). At present, // afaik, there can be at most one. (In the future, if we // adopted a more general `@` operator, there might be more @@ -749,6 +774,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }; + let match_info = candidate.coverage_id.new_match_info( + match_pair.coverage_id, + match_pair.pattern.span, + fully_matched, + ); + if fully_matched { // Replace the match pair by its sub-pairs. let match_pair = candidate.match_pairs.remove(match_pair_index); @@ -757,7 +788,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); } - ret + ret.map(|branch| (branch, match_info)) } } diff --git a/tests/coverage/mcdc/if_let.cov-map b/tests/coverage/mcdc/if_let.cov-map new file mode 100644 index 0000000000000..f002a7899a477 --- /dev/null +++ b/tests/coverage/mcdc/if_let.cov-map @@ -0,0 +1,292 @@ +Function name: if_let::joint_or_patterns +Raw bytes (332): 0x[01, 01, 38, 35, 3e, 5b, 57, 05, 5e, 62, 11, 01, 67, 05, 09, 35, 0d, 6f, 11, 09, 0d, 01, 67, 05, 09, 05, 09, 62, 11, 01, 67, 05, 09, 5b, 57, 05, 5e, 62, 11, 01, 67, 05, 09, 35, 0d, 35, 0d, 05, 5e, 62, 11, 01, 67, 05, 09, 6f, 11, 09, 0d, db, 01, 31, df, 01, 2d, 25, 29, cf, 01, 21, d3, 01, 1d, 15, 19, 3d, 15, 39, 1d, 31, 29, 9b, 01, 19, 9f, 01, 25, 2d, 21, 2d, 25, 21, 19, 39, 1d, db, 01, 31, df, 01, 2d, 25, 29, cf, 01, 21, d3, 01, 1d, 15, 19, cb, 01, d7, 01, cf, 01, 21, d3, 01, 1d, 15, 19, db, 01, 31, df, 01, 2d, 25, 29, 1a, 01, 26, 01, 00, 27, 28, 08, 05, 01, 0c, 00, 40, 20, 03, 6b, 00, 0c, 00, 40, 30, 62, 67, 01, 02, 03, 00, 0d, 00, 1c, 30, 5e, 11, 02, 04, 00, 00, 14, 00, 1b, 30, 05, 09, 03, 04, 00, 00, 1f, 00, 28, 30, 3e, 57, 04, 00, 05, 00, 2a, 00, 33, 30, 35, 0d, 05, 00, 00, 00, 36, 00, 3f, 5b, 00, 43, 00, 46, 03, 00, 47, 02, 06, 6b, 02, 06, 00, 07, 28, 10, 05, 03, 0c, 00, 40, 20, d7, 01, cb, 01, 00, 0c, 00, 40, 30, 39, 8b, 01, 01, 02, 03, 00, 0d, 00, 1c, 30, aa, 01, 1d, 02, 04, 00, 00, 14, 00, 1b, 31, 00, 19, 00, 1a, 30, 3d, 15, 03, 04, 00, 00, 1f, 00, 28, 29, 00, 26, 00, 27, 30, 93, 01, 97, 01, 04, 00, 05, 00, 2a, 00, 33, 31, 00, 31, 00, 32, 30, a3, 01, a7, 01, 05, 00, 00, 00, 36, 00, 3f, 2d, 00, 3d, 00, 3e, aa, 01, 00, 43, 00, 46, d7, 01, 00, 47, 02, 06, cb, 01, 02, 06, 00, 07, c7, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 56 +- expression 0 operands: lhs = Counter(13), rhs = Expression(15, Sub) +- expression 1 operands: lhs = Expression(22, Add), rhs = Expression(21, Add) +- expression 2 operands: lhs = Counter(1), rhs = Expression(23, Sub) +- expression 3 operands: lhs = Expression(24, Sub), rhs = Counter(4) +- expression 4 operands: lhs = Counter(0), rhs = Expression(25, Add) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(13), rhs = Counter(3) +- expression 7 operands: lhs = Expression(27, Add), rhs = Counter(4) +- expression 8 operands: lhs = Counter(2), rhs = Counter(3) +- expression 9 operands: lhs = Counter(0), rhs = Expression(25, Add) +- expression 10 operands: lhs = Counter(1), rhs = Counter(2) +- expression 11 operands: lhs = Counter(1), rhs = Counter(2) +- expression 12 operands: lhs = Expression(24, Sub), rhs = Counter(4) +- expression 13 operands: lhs = Counter(0), rhs = Expression(25, Add) +- expression 14 operands: lhs = Counter(1), rhs = Counter(2) +- expression 15 operands: lhs = Expression(22, Add), rhs = Expression(21, Add) +- expression 16 operands: lhs = Counter(1), rhs = Expression(23, Sub) +- expression 17 operands: lhs = Expression(24, Sub), rhs = Counter(4) +- expression 18 operands: lhs = Counter(0), rhs = Expression(25, Add) +- expression 19 operands: lhs = Counter(1), rhs = Counter(2) +- expression 20 operands: lhs = Counter(13), rhs = Counter(3) +- expression 21 operands: lhs = Counter(13), rhs = Counter(3) +- expression 22 operands: lhs = Counter(1), rhs = Expression(23, Sub) +- expression 23 operands: lhs = Expression(24, Sub), rhs = Counter(4) +- expression 24 operands: lhs = Counter(0), rhs = Expression(25, Add) +- expression 25 operands: lhs = Counter(1), rhs = Counter(2) +- expression 26 operands: lhs = Expression(27, Add), rhs = Counter(4) +- expression 27 operands: lhs = Counter(2), rhs = Counter(3) +- expression 28 operands: lhs = Expression(54, Add), rhs = Counter(12) +- expression 29 operands: lhs = Expression(55, Add), rhs = Counter(11) +- expression 30 operands: lhs = Counter(9), rhs = Counter(10) +- expression 31 operands: lhs = Expression(51, Add), rhs = Counter(8) +- expression 32 operands: lhs = Expression(52, Add), rhs = Counter(7) +- expression 33 operands: lhs = Counter(5), rhs = Counter(6) +- expression 34 operands: lhs = Counter(15), rhs = Counter(5) +- expression 35 operands: lhs = Counter(14), rhs = Counter(7) +- expression 36 operands: lhs = Counter(12), rhs = Counter(10) +- expression 37 operands: lhs = Expression(38, Add), rhs = Counter(6) +- expression 38 operands: lhs = Expression(39, Add), rhs = Counter(9) +- expression 39 operands: lhs = Counter(11), rhs = Counter(8) +- expression 40 operands: lhs = Counter(11), rhs = Counter(9) +- expression 41 operands: lhs = Counter(8), rhs = Counter(6) +- expression 42 operands: lhs = Counter(14), rhs = Counter(7) +- expression 43 operands: lhs = Expression(54, Add), rhs = Counter(12) +- expression 44 operands: lhs = Expression(55, Add), rhs = Counter(11) +- expression 45 operands: lhs = Counter(9), rhs = Counter(10) +- expression 46 operands: lhs = Expression(51, Add), rhs = Counter(8) +- expression 47 operands: lhs = Expression(52, Add), rhs = Counter(7) +- expression 48 operands: lhs = Counter(5), rhs = Counter(6) +- expression 49 operands: lhs = Expression(50, Add), rhs = Expression(53, Add) +- expression 50 operands: lhs = Expression(51, Add), rhs = Counter(8) +- expression 51 operands: lhs = Expression(52, Add), rhs = Counter(7) +- expression 52 operands: lhs = Counter(5), rhs = Counter(6) +- expression 53 operands: lhs = Expression(54, Add), rhs = Counter(12) +- expression 54 operands: lhs = Expression(55, Add), rhs = Counter(11) +- expression 55 operands: lhs = Counter(9), rhs = Counter(10) +Number of file 0 mappings: 26 +- Code(Counter(0)) at (prev + 38, 1) to (start + 0, 39) +- MCDCDecision { bitmap_idx: 8, conditions_num: 5 } at (prev + 1, 12) to (start + 0, 64) +- Branch { true: Expression(0, Add), false: Expression(26, Add) } at (prev + 0, 12) to (start + 0, 64) + true = (c13 + ((c1 + ((c0 - (c1 + c2)) - c4)) - (c13 + c3))) + false = ((c2 + c3) + c4) +- MCDCBranch { true: Expression(24, Sub), false: Expression(25, Add), condition_id: 1, true_next_id: 2, false_next_id: 3 } at (prev + 0, 13) to (start + 0, 28) + true = (c0 - (c1 + c2)) + false = (c1 + c2) +- MCDCBranch { true: Expression(23, Sub), false: Counter(4), condition_id: 2, true_next_id: 4, false_next_id: 0 } at (prev + 0, 20) to (start + 0, 27) + true = ((c0 - (c1 + c2)) - c4) + false = c4 +- MCDCBranch { true: Counter(1), false: Counter(2), condition_id: 3, true_next_id: 4, false_next_id: 0 } at (prev + 0, 31) to (start + 0, 40) + true = c1 + false = c2 +- MCDCBranch { true: Expression(15, Sub), false: Expression(21, Add), condition_id: 4, true_next_id: 0, false_next_id: 5 } at (prev + 0, 42) to (start + 0, 51) + true = ((c1 + ((c0 - (c1 + c2)) - c4)) - (c13 + c3)) + false = (c13 + c3) +- MCDCBranch { true: Counter(13), false: Counter(3), condition_id: 5, true_next_id: 0, false_next_id: 0 } at (prev + 0, 54) to (start + 0, 63) + true = c13 + false = c3 +- Code(Expression(22, Add)) at (prev + 0, 67) to (start + 0, 70) + = (c1 + ((c0 - (c1 + c2)) - c4)) +- Code(Expression(0, Add)) at (prev + 0, 71) to (start + 2, 6) + = (c13 + ((c1 + ((c0 - (c1 + c2)) - c4)) - (c13 + c3))) +- Code(Expression(26, Add)) at (prev + 2, 6) to (start + 0, 7) + = ((c2 + c3) + c4) +- MCDCDecision { bitmap_idx: 16, conditions_num: 5 } at (prev + 3, 12) to (start + 0, 64) +- Branch { true: Expression(53, Add), false: Expression(50, Add) } at (prev + 0, 12) to (start + 0, 64) + true = (((c9 + c10) + c11) + c12) + false = (((c5 + c6) + c7) + c8) +- MCDCBranch { true: Counter(14), false: Expression(34, Add), condition_id: 1, true_next_id: 2, false_next_id: 3 } at (prev + 0, 13) to (start + 0, 28) + true = c14 + false = (c15 + c5) +- MCDCBranch { true: Expression(42, Sub), false: Counter(7), condition_id: 2, true_next_id: 4, false_next_id: 0 } at (prev + 0, 20) to (start + 0, 27) + true = (c14 - c7) + false = c7 +- Code(Counter(12)) at (prev + 0, 25) to (start + 0, 26) +- MCDCBranch { true: Counter(15), false: Counter(5), condition_id: 3, true_next_id: 4, false_next_id: 0 } at (prev + 0, 31) to (start + 0, 40) + true = c15 + false = c5 +- Code(Counter(10)) at (prev + 0, 38) to (start + 0, 39) +- MCDCBranch { true: Expression(36, Add), false: Expression(37, Add), condition_id: 4, true_next_id: 0, false_next_id: 5 } at (prev + 0, 42) to (start + 0, 51) + true = (c12 + c10) + false = (((c11 + c8) + c9) + c6) +- Code(Counter(12)) at (prev + 0, 49) to (start + 0, 50) +- MCDCBranch { true: Expression(40, Add), false: Expression(41, Add), condition_id: 5, true_next_id: 0, false_next_id: 0 } at (prev + 0, 54) to (start + 0, 63) + true = (c11 + c9) + false = (c8 + c6) +- Code(Counter(11)) at (prev + 0, 61) to (start + 0, 62) +- Code(Expression(42, Sub)) at (prev + 0, 67) to (start + 0, 70) + = (c14 - c7) +- Code(Expression(53, Add)) at (prev + 0, 71) to (start + 2, 6) + = (((c9 + c10) + c11) + c12) +- Code(Expression(50, Add)) at (prev + 2, 6) to (start + 0, 7) + = (((c5 + c6) + c7) + c8) +- Code(Expression(49, Add)) at (prev + 1, 1) to (start + 0, 2) + = ((((c5 + c6) + c7) + c8) + (((c9 + c10) + c11) + c12)) + +Function name: if_let::joint_pattern_with_or +Raw bytes (115): 0x[01, 01, 10, 11, 15, 3f, 0d, 05, 09, 26, 2b, 01, 05, 11, 09, 11, 09, 01, 05, 26, 2b, 01, 05, 11, 09, 3f, 0d, 05, 09, 3b, 03, 3f, 0d, 05, 09, 0b, 01, 20, 01, 00, 2b, 28, 05, 04, 01, 0c, 00, 34, 20, 03, 3b, 00, 0c, 00, 34, 30, 22, 2b, 02, 03, 04, 00, 0d, 00, 1c, 30, 15, 0d, 03, 00, 00, 00, 14, 00, 1b, 30, 11, 09, 04, 00, 00, 00, 1f, 00, 28, 30, 26, 05, 01, 02, 00, 00, 2a, 00, 33, 22, 00, 37, 00, 3a, 03, 00, 3b, 02, 06, 3b, 02, 06, 00, 07, 37, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 16 +- expression 0 operands: lhs = Counter(4), rhs = Counter(5) +- expression 1 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Expression(9, Sub), rhs = Expression(10, Add) +- expression 4 operands: lhs = Counter(0), rhs = Counter(1) +- expression 5 operands: lhs = Counter(4), rhs = Counter(2) +- expression 6 operands: lhs = Counter(4), rhs = Counter(2) +- expression 7 operands: lhs = Counter(0), rhs = Counter(1) +- expression 8 operands: lhs = Expression(9, Sub), rhs = Expression(10, Add) +- expression 9 operands: lhs = Counter(0), rhs = Counter(1) +- expression 10 operands: lhs = Counter(4), rhs = Counter(2) +- expression 11 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 12 operands: lhs = Counter(1), rhs = Counter(2) +- expression 13 operands: lhs = Expression(14, Add), rhs = Expression(0, Add) +- expression 14 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 15 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 11 +- Code(Counter(0)) at (prev + 32, 1) to (start + 0, 43) +- MCDCDecision { bitmap_idx: 5, conditions_num: 4 } at (prev + 1, 12) to (start + 0, 52) +- Branch { true: Expression(0, Add), false: Expression(14, Add) } at (prev + 0, 12) to (start + 0, 52) + true = (c4 + c5) + false = ((c1 + c2) + c3) +- MCDCBranch { true: Expression(8, Sub), false: Expression(10, Add), condition_id: 2, true_next_id: 3, false_next_id: 4 } at (prev + 0, 13) to (start + 0, 28) + true = ((c0 - c1) - (c4 + c2)) + false = (c4 + c2) +- MCDCBranch { true: Counter(5), false: Counter(3), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 20) to (start + 0, 27) + true = c5 + false = c3 +- MCDCBranch { true: Counter(4), false: Counter(2), condition_id: 4, true_next_id: 0, false_next_id: 0 } at (prev + 0, 31) to (start + 0, 40) + true = c4 + false = c2 +- MCDCBranch { true: Expression(9, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 42) to (start + 0, 51) + true = (c0 - c1) + false = c1 +- Code(Expression(8, Sub)) at (prev + 0, 55) to (start + 0, 58) + = ((c0 - c1) - (c4 + c2)) +- Code(Expression(0, Add)) at (prev + 0, 59) to (start + 2, 6) + = (c4 + c5) +- Code(Expression(14, Add)) at (prev + 2, 6) to (start + 0, 7) + = ((c1 + c2) + c3) +- Code(Expression(13, Add)) at (prev + 1, 1) to (start + 0, 2) + = (((c1 + c2) + c3) + (c4 + c5)) + +Function name: if_let::let_else +Raw bytes (71): 0x[01, 01, 04, 05, 09, 01, 05, 01, 05, 03, 0d, 09, 01, 31, 01, 00, 19, 28, 03, 02, 01, 09, 00, 18, 20, 0d, 03, 00, 09, 00, 18, 30, 0a, 05, 01, 02, 00, 00, 09, 00, 18, 30, 0d, 09, 02, 00, 00, 00, 10, 00, 17, 0a, 00, 1b, 00, 20, 03, 00, 28, 00, 2e, 0d, 01, 05, 00, 13, 0f, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(3) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 49, 1) to (start + 0, 25) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 24) +- Branch { true: Counter(3), false: Expression(0, Add) } at (prev + 0, 9) to (start + 0, 24) + true = c3 + false = (c1 + c2) +- MCDCBranch { true: Expression(2, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 24) + true = (c0 - c1) + false = c1 +- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 16) to (start + 0, 23) + true = c3 + false = c2 +- Code(Expression(2, Sub)) at (prev + 0, 27) to (start + 0, 32) + = (c0 - c1) +- Code(Expression(0, Add)) at (prev + 0, 40) to (start + 0, 46) + = (c1 + c2) +- Code(Counter(3)) at (prev + 1, 5) to (start + 0, 19) +- Code(Expression(3, Add)) at (prev + 1, 1) to (start + 0, 2) + = ((c1 + c2) + c3) + +Function name: if_let::simple_joint_pattern +Raw bytes (89): 0x[01, 01, 08, 07, 0d, 05, 09, 01, 05, 1a, 09, 01, 05, 1a, 09, 01, 05, 03, 11, 0a, 01, 1a, 01, 00, 2a, 28, 04, 03, 01, 0c, 00, 28, 20, 11, 03, 00, 0c, 00, 28, 30, 1a, 05, 01, 02, 00, 00, 0d, 00, 1c, 30, 11, 0d, 03, 00, 00, 00, 14, 00, 1b, 30, 16, 09, 02, 03, 00, 00, 1e, 00, 27, 16, 00, 2b, 00, 2e, 11, 00, 2f, 02, 06, 03, 02, 06, 00, 07, 1f, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 8 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Expression(6, Sub), rhs = Counter(2) +- expression 4 operands: lhs = Counter(0), rhs = Counter(1) +- expression 5 operands: lhs = Expression(6, Sub), rhs = Counter(2) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Expression(0, Add), rhs = Counter(4) +Number of file 0 mappings: 10 +- Code(Counter(0)) at (prev + 26, 1) to (start + 0, 42) +- MCDCDecision { bitmap_idx: 4, conditions_num: 3 } at (prev + 1, 12) to (start + 0, 40) +- Branch { true: Counter(4), false: Expression(0, Add) } at (prev + 0, 12) to (start + 0, 40) + true = c4 + false = ((c1 + c2) + c3) +- MCDCBranch { true: Expression(6, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 28) + true = (c0 - c1) + false = c1 +- MCDCBranch { true: Counter(4), false: Counter(3), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 20) to (start + 0, 27) + true = c4 + false = c3 +- MCDCBranch { true: Expression(5, Sub), false: Counter(2), condition_id: 2, true_next_id: 3, false_next_id: 0 } at (prev + 0, 30) to (start + 0, 39) + true = ((c0 - c1) - c2) + false = c2 +- Code(Expression(5, Sub)) at (prev + 0, 43) to (start + 0, 46) + = ((c0 - c1) - c2) +- Code(Counter(4)) at (prev + 0, 47) to (start + 2, 6) +- Code(Expression(0, Add)) at (prev + 2, 6) to (start + 0, 7) + = ((c1 + c2) + c3) +- Code(Expression(7, Add)) at (prev + 1, 1) to (start + 0, 2) + = (((c1 + c2) + c3) + c4) + +Function name: if_let::simple_or_pattern +Raw bytes (72): 0x[01, 01, 07, 05, 0e, 01, 17, 05, 09, 01, 17, 05, 09, 05, 09, 0d, 03, 08, 01, 14, 01, 01, 27, 28, 03, 02, 01, 0c, 00, 21, 20, 03, 0d, 00, 0c, 00, 21, 30, 0e, 17, 01, 00, 02, 00, 0c, 00, 15, 30, 05, 09, 02, 00, 00, 00, 18, 00, 21, 03, 00, 28, 02, 06, 0d, 02, 06, 00, 07, 1b, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 7 +- expression 0 operands: lhs = Counter(1), rhs = Expression(3, Sub) +- expression 1 operands: lhs = Counter(0), rhs = Expression(5, Add) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Counter(0), rhs = Expression(5, Add) +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Counter(3), rhs = Expression(0, Add) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 20, 1) to (start + 1, 39) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 12) to (start + 0, 33) +- Branch { true: Expression(0, Add), false: Counter(3) } at (prev + 0, 12) to (start + 0, 33) + true = (c1 + (c0 - (c1 + c2))) + false = c3 +- MCDCBranch { true: Expression(3, Sub), false: Expression(5, Add), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 12) to (start + 0, 21) + true = (c0 - (c1 + c2)) + false = (c1 + c2) +- MCDCBranch { true: Counter(1), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 24) to (start + 0, 33) + true = c1 + false = c2 +- Code(Expression(0, Add)) at (prev + 0, 40) to (start + 2, 6) + = (c1 + (c0 - (c1 + c2))) +- Code(Counter(3)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(6, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c3 + (c1 + (c0 - (c1 + c2)))) + +Function name: if_let::single_nested_pattern +Raw bytes (71): 0x[01, 01, 04, 05, 09, 01, 05, 01, 05, 03, 0d, 09, 01, 0e, 01, 00, 24, 28, 03, 02, 01, 0c, 00, 1b, 20, 0d, 03, 00, 0c, 00, 1b, 30, 0a, 05, 01, 02, 00, 00, 0c, 00, 1b, 30, 0d, 09, 02, 00, 00, 00, 13, 00, 1a, 0a, 00, 1e, 00, 21, 0d, 00, 22, 02, 06, 03, 02, 06, 00, 07, 0f, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(0), rhs = Counter(1) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(3) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 14, 1) to (start + 0, 36) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 12) to (start + 0, 27) +- Branch { true: Counter(3), false: Expression(0, Add) } at (prev + 0, 12) to (start + 0, 27) + true = c3 + false = (c1 + c2) +- MCDCBranch { true: Expression(2, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 12) to (start + 0, 27) + true = (c0 - c1) + false = c1 +- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 19) to (start + 0, 26) + true = c3 + false = c2 +- Code(Expression(2, Sub)) at (prev + 0, 30) to (start + 0, 33) + = (c0 - c1) +- Code(Counter(3)) at (prev + 0, 34) to (start + 2, 6) +- Code(Expression(0, Add)) at (prev + 2, 6) to (start + 0, 7) + = (c1 + c2) +- Code(Expression(3, Add)) at (prev + 1, 1) to (start + 0, 2) + = ((c1 + c2) + c3) + diff --git a/tests/coverage/mcdc/if_let.coverage b/tests/coverage/mcdc/if_let.coverage new file mode 100644 index 0000000000000..3fd32f204e4d3 --- /dev/null +++ b/tests/coverage/mcdc/if_let.coverage @@ -0,0 +1,272 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ min-llvm-version: 19 + LL| |//@ compile-flags: -Zcoverage-options=mcdc + LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc + LL| | + LL| |#[derive(Clone, Copy)] + LL| |enum Pat { + LL| | A(Option), + LL| | B(i32), + LL| | C(i32), + LL| |} + LL| | + LL| 2|fn single_nested_pattern(pat: Pat) { + LL| 1| if let Pat::A(Some(_)) = pat { + ------------------ + | Branch (LL:12): [True: 1, False: 1] + | Branch (LL:12): [True: 1, False: 1] + | Branch (LL:19): [True: 1, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:12) to (LL:27) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:12) + | Condition C2 --> (LL:19) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| say("matched"); + LL| 1| } + LL| 2|} + LL| | + LL| 2|fn simple_or_pattern(pat: Pat) { + LL| 2| if let Pat::B(_) | Pat::C(_) = pat { + ------------------ + | Branch (LL:12): [True: 1, False: 1] + | Branch (LL:12): [True: 0, False: 2] + | Branch (LL:24): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:12) to (LL:33) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:12) + | Condition C2 --> (LL:24) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, F = F } + | 2 { F, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| say("matched"); + LL| 1| } + LL| 2|} + LL| | + LL| 3|fn simple_joint_pattern(pat: (Pat, Pat)) { + LL| 1| if let (Pat::A(Some(_)), Pat::B(_)) = pat { + ------------------ + | Branch (LL:12): [True: 1, False: 2] + | Branch (LL:13): [True: 2, False: 1] + | Branch (LL:20): [True: 1, False: 0] + | Branch (LL:30): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:12) to (LL:40) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:20) + | Condition C3 --> (LL:30) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { F, -, - = F } + | 2 { T, -, F = F } + | 3 { T, T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: not covered + | C3-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 66.67% + | + ------------------ + LL| 1| say("matched"); + LL| 2| } + LL| 3|} + LL| | + LL| 4|fn joint_pattern_with_or(pat: (Pat, Pat)) { + LL| 2| if let (Pat::A(Some(_)) | Pat::C(_), Pat::B(_)) = pat { + ^1 + ------------------ + | Branch (LL:12): [True: 2, False: 2] + | Branch (LL:13): [True: 1, False: 2] + | Branch (LL:20): [True: 1, False: 0] + | Branch (LL:31): [True: 1, False: 1] + | Branch (LL:42): [True: 3, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:12) to (LL:52) + | + | Number of Conditions: 4 + | Condition C1 --> (LL:42) + | Condition C2 --> (LL:13) + | Condition C3 --> (LL:20) + | Condition C4 --> (LL:31) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4 Result + | 1 { F, -, -, - = F } + | 2 { T, F, -, F = F } + | 3 { T, F, -, T = T } + | 4 { T, T, T, - = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,4) + | C3-Pair: not covered + | C4-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 75.00% + | + ------------------ + LL| 2| say("matched"); + LL| 2| } + LL| 4|} + LL| | + LL| 4|fn joint_or_patterns(pat: (Pat, Pat)) { + LL| 2| if let (Pat::A(Some(_)) | Pat::C(_), Pat::B(_) | Pat::C(_)) = pat { + ------------------ + | Branch (LL:12): [True: 2, False: 2] + | Branch (LL:13): [True: 1, False: 3] + | Branch (LL:20): [True: 1, False: 0] + | Branch (LL:31): [True: 1, False: 2] + | Branch (LL:42): [True: 2, False: 0] + | Branch (LL:54): [True: 0, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:12) to (LL:64) + | + | Number of Conditions: 5 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:20) + | Condition C3 --> (LL:31) + | Condition C4 --> (LL:42) + | Condition C5 --> (LL:54) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4, C5 Result + | 1 { F, -, F, -, - = F } + | 2 { F, -, T, T, - = T } + | 3 { T, T, -, T, - = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: not covered + | C3-Pair: covered: (1,2) + | C4-Pair: not covered + | C5-Pair: not covered + | MC/DC Coverage for Decision: 40.00% + | + ------------------ + LL| 2| say("matched"); + LL| 2| } + LL| | + LL| | // Try to use the matched value + LL| 2| if let (Pat::A(Some(a)) | Pat::C(a), Pat::B(b) | Pat::C(b)) = pat { + ^1 ^1 ^1 ^0 ^1 + ------------------ + | Branch (LL:12): [True: 2, False: 2] + | Branch (LL:13): [True: 1, False: 3] + | Branch (LL:20): [True: 1, False: 0] + | Branch (LL:31): [True: 1, False: 2] + | Branch (LL:42): [True: 2, False: 0] + | Branch (LL:54): [True: 0, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:12) to (LL:64) + | + | Number of Conditions: 5 + | Condition C1 --> (LL:13) + | Condition C2 --> (LL:20) + | Condition C3 --> (LL:31) + | Condition C4 --> (LL:42) + | Condition C5 --> (LL:54) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4, C5 Result + | 1 { F, -, F, -, - = F } + | 2 { F, -, T, T, - = T } + | 3 { T, T, -, T, - = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: not covered + | C3-Pair: covered: (1,2) + | C4-Pair: not covered + | C5-Pair: not covered + | MC/DC Coverage for Decision: 40.00% + | + ------------------ + LL| 2| say(&format!("matched {a} and {b}")); + LL| 2| } + LL| 4|} + LL| | + LL| 2|fn let_else(value: Pat) { + LL| 1| let Pat::A(Some(_)) = value else { return }; + ------------------ + | Branch (LL:9): [True: 1, False: 1] + | Branch (LL:9): [True: 1, False: 1] + | Branch (LL:16): [True: 1, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:24) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:16) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| say("matched"); + LL| 2|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | single_nested_pattern(Pat::A(Some(5))); + LL| | single_nested_pattern(Pat::B(5)); + LL| | + LL| | simple_or_pattern(Pat::A(None)); + LL| | simple_or_pattern(Pat::C(3)); + LL| | + LL| | simple_joint_pattern((Pat::A(Some(1)), Pat::B(2))); + LL| | simple_joint_pattern((Pat::A(Some(1)), Pat::C(2))); + LL| | simple_joint_pattern((Pat::B(1), Pat::B(2))); + LL| | + LL| | joint_pattern_with_or((Pat::A(Some(1)), Pat::B(2))); + LL| | joint_pattern_with_or((Pat::B(1), Pat::C(2))); + LL| | joint_pattern_with_or((Pat::B(1), Pat::B(2))); + LL| | joint_pattern_with_or((Pat::C(1), Pat::B(2))); + LL| | + LL| | joint_or_patterns((Pat::A(Some(1)), Pat::B(2))); + LL| | joint_or_patterns((Pat::B(1), Pat::C(2))); + LL| | joint_or_patterns((Pat::B(1), Pat::B(2))); + LL| | joint_or_patterns((Pat::C(1), Pat::B(2))); + LL| | + LL| | let_else(Pat::A(Some(5))); + LL| | let_else(Pat::B(3)); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + diff --git a/tests/coverage/mcdc/if_let.rs b/tests/coverage/mcdc/if_let.rs new file mode 100644 index 0000000000000..629f922d88edd --- /dev/null +++ b/tests/coverage/mcdc/if_let.rs @@ -0,0 +1,83 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ min-llvm-version: 19 +//@ compile-flags: -Zcoverage-options=mcdc +//@ llvm-cov-flags: --show-branches=count --show-mcdc + +#[derive(Clone, Copy)] +enum Pat { + A(Option), + B(i32), + C(i32), +} + +fn single_nested_pattern(pat: Pat) { + if let Pat::A(Some(_)) = pat { + say("matched"); + } +} + +fn simple_or_pattern(pat: Pat) { + if let Pat::B(_) | Pat::C(_) = pat { + say("matched"); + } +} + +fn simple_joint_pattern(pat: (Pat, Pat)) { + if let (Pat::A(Some(_)), Pat::B(_)) = pat { + say("matched"); + } +} + +fn joint_pattern_with_or(pat: (Pat, Pat)) { + if let (Pat::A(Some(_)) | Pat::C(_), Pat::B(_)) = pat { + say("matched"); + } +} + +fn joint_or_patterns(pat: (Pat, Pat)) { + if let (Pat::A(Some(_)) | Pat::C(_), Pat::B(_) | Pat::C(_)) = pat { + say("matched"); + } + + // Try to use the matched value + if let (Pat::A(Some(a)) | Pat::C(a), Pat::B(b) | Pat::C(b)) = pat { + say(&format!("matched {a} and {b}")); + } +} + +fn let_else(value: Pat) { + let Pat::A(Some(_)) = value else { return }; + say("matched"); +} + +#[coverage(off)] +fn main() { + single_nested_pattern(Pat::A(Some(5))); + single_nested_pattern(Pat::B(5)); + + simple_or_pattern(Pat::A(None)); + simple_or_pattern(Pat::C(3)); + + simple_joint_pattern((Pat::A(Some(1)), Pat::B(2))); + simple_joint_pattern((Pat::A(Some(1)), Pat::C(2))); + simple_joint_pattern((Pat::B(1), Pat::B(2))); + + joint_pattern_with_or((Pat::A(Some(1)), Pat::B(2))); + joint_pattern_with_or((Pat::B(1), Pat::C(2))); + joint_pattern_with_or((Pat::B(1), Pat::B(2))); + joint_pattern_with_or((Pat::C(1), Pat::B(2))); + + joint_or_patterns((Pat::A(Some(1)), Pat::B(2))); + joint_or_patterns((Pat::B(1), Pat::C(2))); + joint_or_patterns((Pat::B(1), Pat::B(2))); + joint_or_patterns((Pat::C(1), Pat::B(2))); + + let_else(Pat::A(Some(5))); + let_else(Pat::B(3)); +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +} diff --git a/tests/coverage/mcdc/match_misc.cov-map b/tests/coverage/mcdc/match_misc.cov-map new file mode 100644 index 0000000000000..6267626e41a87 --- /dev/null +++ b/tests/coverage/mcdc/match_misc.cov-map @@ -0,0 +1,445 @@ +Function name: match_misc::empty_matching_decision +Raw bytes (125): 0x[01, 01, 06, 01, 05, 05, 09, 15, 19, 19, 0d, 17, 11, 09, 0d, 11, 01, 22, 01, 01, 0e, 09, 02, 09, 00, 0a, 28, 06, 02, 00, 09, 00, 1d, 01, 00, 0e, 00, 13, 30, 05, 02, 01, 02, 00, 00, 0e, 00, 13, 05, 00, 17, 00, 1d, 30, 09, 06, 02, 00, 00, 00, 17, 00, 1d, 09, 00, 21, 00, 32, 0d, 01, 09, 00, 0a, 28, 03, 02, 00, 09, 00, 1c, 15, 00, 0e, 00, 13, 30, 19, 0a, 01, 02, 00, 00, 0e, 00, 13, 19, 00, 17, 00, 1c, 30, 0d, 0e, 02, 00, 00, 00, 17, 00, 1c, 0d, 00, 20, 00, 30, 11, 01, 0e, 00, 1a, 13, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 6 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(5), rhs = Counter(6) +- expression 3 operands: lhs = Counter(6), rhs = Counter(3) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(4) +- expression 5 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 17 +- Code(Counter(0)) at (prev + 34, 1) to (start + 1, 14) +- Code(Counter(2)) at (prev + 2, 9) to (start + 0, 10) +- MCDCDecision { bitmap_idx: 6, conditions_num: 2 } at (prev + 0, 9) to (start + 0, 29) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 19) +- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 14) to (start + 0, 19) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 23) to (start + 0, 29) +- MCDCBranch { true: Counter(2), false: Expression(1, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 29) + true = c2 + false = (c1 - c2) +- Code(Counter(2)) at (prev + 0, 33) to (start + 0, 50) +- Code(Counter(3)) at (prev + 1, 9) to (start + 0, 10) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 0, 9) to (start + 0, 28) +- Code(Counter(5)) at (prev + 0, 14) to (start + 0, 19) +- MCDCBranch { true: Counter(6), false: Expression(2, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 14) to (start + 0, 19) + true = c6 + false = (c5 - c6) +- Code(Counter(6)) at (prev + 0, 23) to (start + 0, 28) +- MCDCBranch { true: Counter(3), false: Expression(3, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 28) + true = c3 + false = (c6 - c3) +- Code(Counter(3)) at (prev + 0, 32) to (start + 0, 48) +- Code(Counter(4)) at (prev + 1, 14) to (start + 0, 26) +- Code(Expression(4, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((c2 + c3) + c4) + +Function name: match_misc::empty_subcandidate +Raw bytes (83): 0x[01, 01, 0b, 01, 0b, 05, 00, 05, 00, 11, 0d, 11, 15, 27, 0d, 00, 05, 23, 2b, 27, 0d, 00, 05, 11, 15, 08, 01, 78, 01, 01, 0e, 28, 04, 03, 02, 09, 00, 1e, 30, 02, 0b, 01, 02, 00, 00, 09, 00, 12, 30, 15, 0f, 02, 00, 03, 00, 10, 00, 11, 30, 11, 0d, 03, 00, 00, 00, 1c, 00, 1d, 2b, 00, 22, 00, 2e, 23, 04, 26, 00, 32, 1f, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 11 +- expression 0 operands: lhs = Counter(0), rhs = Expression(2, Add) +- expression 1 operands: lhs = Counter(1), rhs = Zero +- expression 2 operands: lhs = Counter(1), rhs = Zero +- expression 3 operands: lhs = Counter(4), rhs = Counter(3) +- expression 4 operands: lhs = Counter(4), rhs = Counter(5) +- expression 5 operands: lhs = Expression(9, Add), rhs = Counter(3) +- expression 6 operands: lhs = Zero, rhs = Counter(1) +- expression 7 operands: lhs = Expression(8, Add), rhs = Expression(10, Add) +- expression 8 operands: lhs = Expression(9, Add), rhs = Counter(3) +- expression 9 operands: lhs = Zero, rhs = Counter(1) +- expression 10 operands: lhs = Counter(4), rhs = Counter(5) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 120, 1) to (start + 1, 14) +- MCDCDecision { bitmap_idx: 4, conditions_num: 3 } at (prev + 2, 9) to (start + 0, 30) +- MCDCBranch { true: Expression(0, Sub), false: Expression(2, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 18) + true = (c0 - (c1 + Zero)) + false = (c1 + Zero) +- MCDCBranch { true: Counter(5), false: Expression(3, Add), condition_id: 2, true_next_id: 0, false_next_id: 3 } at (prev + 0, 16) to (start + 0, 17) + true = c5 + false = (c4 + c3) +- MCDCBranch { true: Counter(4), false: Counter(3), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 29) + true = c4 + false = c3 +- Code(Expression(10, Add)) at (prev + 0, 34) to (start + 0, 46) + = (c4 + c5) +- Code(Expression(8, Add)) at (prev + 4, 38) to (start + 0, 50) + = ((Zero + c1) + c3) +- Code(Expression(7, Add)) at (prev + 2, 1) to (start + 0, 2) + = (((Zero + c1) + c3) + (c4 + c5)) + +Function name: match_misc::loop_over_iterator +Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 09, 01, 00, 19, 05, 01, 09, 00, 0c, 03, 00, 10, 00, 19, 05, 00, 1a, 02, 06, 01, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 1 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 9, 1) to (start + 0, 25) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Expression(0, Add)) at (prev + 0, 16) to (start + 0, 25) + = (c0 + c1) +- Code(Counter(1)) at (prev + 0, 26) to (start + 2, 6) +- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2) + +Function name: match_misc::match_failure_test_kind +Raw bytes (97): 0x[01, 01, 07, 01, 05, 0d, 00, 11, 00, 05, 00, 17, 11, 1b, 0d, 05, 00, 0b, 01, 51, 01, 01, 14, 28, 06, 02, 02, 09, 00, 16, 30, 02, 05, 01, 02, 00, 00, 0a, 00, 0f, 30, 11, 07, 02, 00, 00, 00, 11, 00, 15, 11, 00, 1a, 00, 25, 28, 03, 02, 01, 09, 00, 19, 30, 02, 05, 01, 02, 00, 00, 0a, 00, 0f, 30, 0d, 0b, 02, 00, 00, 00, 11, 00, 18, 0d, 00, 1d, 00, 28, 1b, 01, 0e, 00, 1a, 13, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 7 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(3), rhs = Zero +- expression 2 operands: lhs = Counter(4), rhs = Zero +- expression 3 operands: lhs = Counter(1), rhs = Zero +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(4) +- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(3) +- expression 6 operands: lhs = Counter(1), rhs = Zero +Number of file 0 mappings: 11 +- Code(Counter(0)) at (prev + 81, 1) to (start + 1, 20) +- MCDCDecision { bitmap_idx: 6, conditions_num: 2 } at (prev + 2, 9) to (start + 0, 22) +- MCDCBranch { true: Expression(0, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 15) + true = (c0 - c1) + false = c1 +- MCDCBranch { true: Counter(4), false: Expression(1, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 17) to (start + 0, 21) + true = c4 + false = (c3 + Zero) +- Code(Counter(4)) at (prev + 0, 26) to (start + 0, 37) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 25) +- MCDCBranch { true: Expression(0, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 15) + true = (c0 - c1) + false = c1 +- MCDCBranch { true: Counter(3), false: Expression(2, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 17) to (start + 0, 24) + true = c3 + false = (c4 + Zero) +- Code(Counter(3)) at (prev + 0, 29) to (start + 0, 40) +- Code(Expression(6, Add)) at (prev + 1, 14) to (start + 0, 26) + = (c1 + Zero) +- Code(Expression(4, Add)) at (prev + 2, 1) to (start + 0, 2) + = (((c1 + Zero) + c3) + c4) + +Function name: match_misc::match_with_macros +Raw bytes (36): 0x[01, 01, 06, 07, 0e, 0b, 05, 11, 09, 01, 13, 17, 0d, 05, 09, 04, 01, 11, 01, 00, 20, 11, 0d, 05, 00, 18, 01, 00, 19, 00, 1c, 03, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 6 +- expression 0 operands: lhs = Expression(1, Add), rhs = Expression(3, Sub) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(1) +- expression 2 operands: lhs = Counter(4), rhs = Counter(2) +- expression 3 operands: lhs = Counter(0), rhs = Expression(4, Add) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(3) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 32) +- Code(Counter(4)) at (prev + 13, 5) to (start + 0, 24) +- Code(Counter(0)) at (prev + 0, 25) to (start + 0, 28) +- Code(Expression(0, Add)) at (prev + 1, 1) to (start + 0, 2) + = (((c4 + c2) + c1) + (c0 - ((c1 + c2) + c3))) + +Function name: match_misc::multi_matched_candidates +Raw bytes (129): 0x[01, 01, 08, 01, 05, 02, 11, 1f, 0d, 05, 09, 17, 15, 1b, 11, 1f, 0d, 05, 09, 10, 01, 70, 01, 00, 2f, 19, 01, 0b, 00, 0e, 28, 07, 02, 01, 09, 00, 1c, 30, 02, 05, 01, 02, 00, 00, 09, 00, 12, 11, 00, 10, 00, 11, 02, 00, 16, 00, 17, 30, 11, 06, 02, 00, 00, 00, 16, 00, 1c, 11, 00, 1b, 00, 2c, 28, 04, 03, 01, 09, 00, 1b, 30, 02, 05, 01, 02, 00, 00, 09, 00, 12, 30, 19, 09, 02, 03, 00, 00, 10, 00, 11, 19, 00, 16, 00, 17, 30, 15, 0d, 03, 00, 00, 00, 16, 00, 1b, 15, 00, 1a, 00, 2c, 1b, 01, 0e, 00, 1a, 13, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 8 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(0, Sub), rhs = Counter(4) +- expression 2 operands: lhs = Expression(7, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(5) +- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(4) +- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(3) +- expression 7 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 16 +- Code(Counter(0)) at (prev + 112, 1) to (start + 0, 47) +- Code(Counter(6)) at (prev + 1, 11) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 7, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 28) +- MCDCBranch { true: Expression(0, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 18) + true = (c0 - c1) + false = c1 +- Code(Counter(4)) at (prev + 0, 16) to (start + 0, 17) +- Code(Expression(0, Sub)) at (prev + 0, 22) to (start + 0, 23) + = (c0 - c1) +- MCDCBranch { true: Counter(4), false: Expression(1, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 22) to (start + 0, 28) + true = c4 + false = ((c0 - c1) - c4) +- Code(Counter(4)) at (prev + 0, 27) to (start + 0, 44) +- MCDCDecision { bitmap_idx: 4, conditions_num: 3 } at (prev + 1, 9) to (start + 0, 27) +- MCDCBranch { true: Expression(0, Sub), false: Counter(1), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 18) + true = (c0 - c1) + false = c1 +- MCDCBranch { true: Counter(6), false: Counter(2), condition_id: 2, true_next_id: 3, false_next_id: 0 } at (prev + 0, 16) to (start + 0, 17) + true = c6 + false = c2 +- Code(Counter(6)) at (prev + 0, 22) to (start + 0, 23) +- MCDCBranch { true: Counter(5), false: Counter(3), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 22) to (start + 0, 27) + true = c5 + false = c3 +- Code(Counter(5)) at (prev + 0, 26) to (start + 0, 44) +- Code(Expression(6, Add)) at (prev + 1, 14) to (start + 0, 26) + = ((c1 + c2) + c3) +- Code(Expression(4, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((((c1 + c2) + c3) + c4) + c5) + +Function name: match_misc::nested_matching +Raw bytes (85): 0x[01, 01, 06, 01, 05, 05, 11, 05, 11, 09, 02, 0d, 17, 09, 02, 0b, 01, 66, 01, 01, 09, 28, 03, 02, 01, 08, 03, 06, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 03, 06, 05, 00, 13, 00, 16, 20, 0a, 11, 01, 09, 00, 12, 0a, 00, 10, 00, 1c, 15, 01, 0e, 00, 13, 0d, 01, 07, 02, 06, 17, 02, 06, 00, 07, 13, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 6 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Counter(4) +- expression 2 operands: lhs = Counter(1), rhs = Counter(4) +- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 4 operands: lhs = Counter(3), rhs = Expression(5, Add) +- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 11 +- Code(Counter(0)) at (prev + 102, 1) to (start + 1, 9) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 8) to (start + 3, 6) +- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) + true = c1 + false = (c0 - c1) +- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 3, 6) + true = c3 + false = c2 +- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 22) +- Branch { true: Expression(2, Sub), false: Counter(4) } at (prev + 1, 9) to (start + 0, 18) + true = (c1 - c4) + false = c4 +- Code(Expression(2, Sub)) at (prev + 0, 16) to (start + 0, 28) + = (c1 - c4) +- Code(Counter(5)) at (prev + 1, 14) to (start + 0, 19) +- Code(Counter(3)) at (prev + 1, 7) to (start + 2, 6) +- Code(Expression(5, Add)) at (prev + 2, 6) to (start + 0, 7) + = (c2 + (c0 - c1)) +- Code(Expression(4, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c3 + (c2 + (c0 - c1))) + +Function name: match_misc::overlapping_decisions +Raw bytes (150): 0x[01, 01, 15, 01, 07, 05, 00, 02, 00, 15, 22, 05, 27, 15, 19, 02, 00, 19, 22, 05, 27, 15, 19, 4e, 00, 02, 53, 0d, 00, 15, 0d, 02, 53, 0d, 00, 47, 4e, 19, 4b, 15, 0d, 02, 53, 0d, 00, 0e, 01, 34, 01, 00, 3b, 02, 01, 0b, 00, 0e, 28, 08, 02, 01, 09, 00, 1b, 30, 05, 1b, 01, 02, 00, 00, 0a, 00, 11, 30, 19, 0f, 02, 00, 00, 00, 13, 00, 1a, 19, 00, 1f, 00, 2f, 28, 05, 04, 01, 09, 00, 2a, 30, 05, 1b, 01, 02, 03, 00, 0a, 00, 11, 30, 15, 1f, 02, 00, 00, 00, 13, 00, 17, 30, 02, 00, 03, 04, 00, 00, 1c, 00, 20, 30, 0d, 2b, 04, 00, 00, 00, 22, 00, 29, 4b, 00, 2e, 00, 4a, 4e, 01, 19, 00, 24, 43, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 21 +- expression 0 operands: lhs = Counter(0), rhs = Expression(1, Add) +- expression 1 operands: lhs = Counter(1), rhs = Zero +- expression 2 operands: lhs = Expression(0, Sub), rhs = Zero +- expression 3 operands: lhs = Counter(5), rhs = Expression(8, Sub) +- expression 4 operands: lhs = Counter(1), rhs = Expression(9, Add) +- expression 5 operands: lhs = Counter(5), rhs = Counter(6) +- expression 6 operands: lhs = Expression(0, Sub), rhs = Zero +- expression 7 operands: lhs = Counter(6), rhs = Expression(8, Sub) +- expression 8 operands: lhs = Counter(1), rhs = Expression(9, Add) +- expression 9 operands: lhs = Counter(5), rhs = Counter(6) +- expression 10 operands: lhs = Expression(19, Sub), rhs = Zero +- expression 11 operands: lhs = Expression(0, Sub), rhs = Expression(20, Add) +- expression 12 operands: lhs = Counter(3), rhs = Zero +- expression 13 operands: lhs = Counter(5), rhs = Counter(3) +- expression 14 operands: lhs = Expression(0, Sub), rhs = Expression(20, Add) +- expression 15 operands: lhs = Counter(3), rhs = Zero +- expression 16 operands: lhs = Expression(17, Add), rhs = Expression(19, Sub) +- expression 17 operands: lhs = Counter(6), rhs = Expression(18, Add) +- expression 18 operands: lhs = Counter(5), rhs = Counter(3) +- expression 19 operands: lhs = Expression(0, Sub), rhs = Expression(20, Add) +- expression 20 operands: lhs = Counter(3), rhs = Zero +Number of file 0 mappings: 14 +- Code(Counter(0)) at (prev + 52, 1) to (start + 0, 59) +- Code(Expression(0, Sub)) at (prev + 1, 11) to (start + 0, 14) + = (c0 - (c1 + Zero)) +- MCDCDecision { bitmap_idx: 8, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 27) +- MCDCBranch { true: Counter(1), false: Expression(6, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 17) + true = c1 + false = ((c0 - (c1 + Zero)) + Zero) +- MCDCBranch { true: Counter(6), false: Expression(3, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 19) to (start + 0, 26) + true = c6 + false = (c5 + (c1 - (c5 + c6))) +- Code(Counter(6)) at (prev + 0, 31) to (start + 0, 47) +- MCDCDecision { bitmap_idx: 5, conditions_num: 4 } at (prev + 1, 9) to (start + 0, 42) +- MCDCBranch { true: Counter(1), false: Expression(6, Add), condition_id: 1, true_next_id: 2, false_next_id: 3 } at (prev + 0, 10) to (start + 0, 17) + true = c1 + false = ((c0 - (c1 + Zero)) + Zero) +- MCDCBranch { true: Counter(5), false: Expression(7, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 19) to (start + 0, 23) + true = c5 + false = (c6 + (c1 - (c5 + c6))) +- MCDCBranch { true: Expression(0, Sub), false: Zero, condition_id: 3, true_next_id: 4, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 32) + true = (c0 - (c1 + Zero)) + false = Zero +- MCDCBranch { true: Counter(3), false: Expression(10, Add), condition_id: 4, true_next_id: 0, false_next_id: 0 } at (prev + 0, 34) to (start + 0, 41) + true = c3 + false = (((c0 - (c1 + Zero)) - (c3 + Zero)) + Zero) +- Code(Expression(18, Add)) at (prev + 0, 46) to (start + 0, 74) + = (c5 + c3) +- Code(Expression(19, Sub)) at (prev + 1, 25) to (start + 0, 36) + = ((c0 - (c1 + Zero)) - (c3 + Zero)) +- Code(Expression(16, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((c6 + (c5 + c3)) + ((c0 - (c1 + Zero)) - (c3 + Zero))) + +Function name: match_misc::partial_matched_decision +Raw bytes (192): 0x[01, 01, 22, 01, 17, 1b, 15, 1f, 11, 23, 0d, 05, 09, 1b, 15, 1f, 11, 23, 0d, 05, 09, 2b, 15, 2f, 11, 09, 0d, 05, 02, 3a, 25, 15, 21, 43, 15, 47, 11, 6b, 0d, 02, 05, 19, 09, 57, 15, 67, 11, 6b, 09, 02, 05, 67, 15, 6b, 09, 02, 05, 11, 0d, 77, 87, 01, 7b, 83, 01, 7f, 1d, 19, 09, 11, 0d, 05, 02, 10, 01, 3c, 01, 03, 0e, 28, 09, 02, 04, 09, 00, 14, 30, 02, 17, 01, 00, 02, 00, 09, 00, 0d, 30, 05, 27, 02, 00, 00, 00, 10, 00, 14, 87, 01, 00, 18, 00, 29, 21, 01, 09, 00, 14, 28, 06, 02, 00, 09, 00, 1b, 30, 19, 37, 02, 00, 00, 00, 09, 00, 14, 30, 09, 3f, 01, 00, 02, 00, 17, 00, 1b, 7f, 00, 1f, 00, 31, 28, 03, 02, 01, 09, 00, 14, 30, 0d, 53, 01, 00, 02, 00, 09, 00, 0d, 30, 11, 63, 02, 00, 00, 00, 10, 00, 14, 83, 01, 00, 18, 00, 25, 1d, 01, 0e, 00, 10, 73, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 34 +- expression 0 operands: lhs = Counter(0), rhs = Expression(5, Add) +- expression 1 operands: lhs = Expression(6, Add), rhs = Counter(5) +- expression 2 operands: lhs = Expression(7, Add), rhs = Counter(4) +- expression 3 operands: lhs = Expression(8, Add), rhs = Counter(3) +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) +- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(5) +- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(4) +- expression 7 operands: lhs = Expression(8, Add), rhs = Counter(3) +- expression 8 operands: lhs = Counter(1), rhs = Counter(2) +- expression 9 operands: lhs = Expression(10, Add), rhs = Counter(5) +- expression 10 operands: lhs = Expression(11, Add), rhs = Counter(4) +- expression 11 operands: lhs = Counter(2), rhs = Counter(3) +- expression 12 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 13 operands: lhs = Expression(14, Sub), rhs = Counter(9) +- expression 14 operands: lhs = Counter(5), rhs = Counter(8) +- expression 15 operands: lhs = Expression(16, Add), rhs = Counter(5) +- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(4) +- expression 17 operands: lhs = Expression(26, Add), rhs = Counter(3) +- expression 18 operands: lhs = Expression(0, Sub), rhs = Counter(1) +- expression 19 operands: lhs = Counter(6), rhs = Counter(2) +- expression 20 operands: lhs = Expression(21, Add), rhs = Counter(5) +- expression 21 operands: lhs = Expression(25, Add), rhs = Counter(4) +- expression 22 operands: lhs = Expression(26, Add), rhs = Counter(2) +- expression 23 operands: lhs = Expression(0, Sub), rhs = Counter(1) +- expression 24 operands: lhs = Expression(25, Add), rhs = Counter(5) +- expression 25 operands: lhs = Expression(26, Add), rhs = Counter(2) +- expression 26 operands: lhs = Expression(0, Sub), rhs = Counter(1) +- expression 27 operands: lhs = Counter(4), rhs = Counter(3) +- expression 28 operands: lhs = Expression(29, Add), rhs = Expression(33, Add) +- expression 29 operands: lhs = Expression(30, Add), rhs = Expression(32, Add) +- expression 30 operands: lhs = Expression(31, Add), rhs = Counter(7) +- expression 31 operands: lhs = Counter(6), rhs = Counter(2) +- expression 32 operands: lhs = Counter(4), rhs = Counter(3) +- expression 33 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 16 +- Code(Counter(0)) at (prev + 60, 1) to (start + 3, 14) +- MCDCDecision { bitmap_idx: 9, conditions_num: 2 } at (prev + 4, 9) to (start + 0, 20) +- MCDCBranch { true: Expression(0, Sub), false: Expression(5, Add), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 9) to (start + 0, 13) + true = (c0 - ((((c1 + c2) + c3) + c4) + c5)) + false = ((((c1 + c2) + c3) + c4) + c5) +- MCDCBranch { true: Counter(1), false: Expression(9, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 16) to (start + 0, 20) + true = c1 + false = (((c2 + c3) + c4) + c5) +- Code(Expression(33, Add)) at (prev + 0, 24) to (start + 0, 41) + = (c1 + (c0 - ((((c1 + c2) + c3) + c4) + c5))) +- Code(Counter(8)) at (prev + 1, 9) to (start + 0, 20) +- MCDCDecision { bitmap_idx: 6, conditions_num: 2 } at (prev + 0, 9) to (start + 0, 27) +- MCDCBranch { true: Counter(6), false: Expression(13, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 20) + true = c6 + false = ((c5 - c8) + c9) +- MCDCBranch { true: Counter(2), false: Expression(15, Add), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 23) to (start + 0, 27) + true = c2 + false = (((((c0 - ((((c1 + c2) + c3) + c4) + c5)) + c1) + c3) + c4) + c5) +- Code(Expression(31, Add)) at (prev + 0, 31) to (start + 0, 49) + = (c6 + c2) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 20) +- MCDCBranch { true: Counter(3), false: Expression(20, Add), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 9) to (start + 0, 13) + true = c3 + false = (((((c0 - ((((c1 + c2) + c3) + c4) + c5)) + c1) + c2) + c4) + c5) +- MCDCBranch { true: Counter(4), false: Expression(24, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 16) to (start + 0, 20) + true = c4 + false = ((((c0 - ((((c1 + c2) + c3) + c4) + c5)) + c1) + c2) + c5) +- Code(Expression(32, Add)) at (prev + 0, 24) to (start + 0, 37) + = (c4 + c3) +- Code(Counter(7)) at (prev + 1, 14) to (start + 0, 16) +- Code(Expression(28, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((((c6 + c2) + c7) + (c4 + c3)) + (c1 + (c0 - ((((c1 + c2) + c3) + c4) + c5)))) + +Function name: match_misc::partial_matched_with_several_blocks +Raw bytes (95): 0x[01, 01, 0f, 05, 09, 2e, 09, 01, 05, 26, 1d, 2b, 0d, 2e, 09, 01, 05, 23, 19, 26, 1d, 2b, 0d, 2e, 09, 01, 05, 37, 19, 3b, 15, 02, 11, 0b, 01, 48, 01, 01, 0e, 05, 02, 09, 00, 14, 20, 02, 2b, 00, 09, 00, 14, 02, 00, 18, 00, 22, 0d, 01, 09, 00, 14, 20, 11, 23, 00, 09, 00, 14, 11, 00, 18, 00, 28, 20, 19, 1e, 01, 09, 00, 0d, 19, 00, 11, 00, 22, 15, 01, 0e, 00, 1a, 33, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 15 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(11, Sub), rhs = Counter(2) +- expression 2 operands: lhs = Counter(0), rhs = Counter(1) +- expression 3 operands: lhs = Expression(9, Sub), rhs = Counter(7) +- expression 4 operands: lhs = Expression(10, Add), rhs = Counter(3) +- expression 5 operands: lhs = Expression(11, Sub), rhs = Counter(2) +- expression 6 operands: lhs = Counter(0), rhs = Counter(1) +- expression 7 operands: lhs = Expression(8, Add), rhs = Counter(6) +- expression 8 operands: lhs = Expression(9, Sub), rhs = Counter(7) +- expression 9 operands: lhs = Expression(10, Add), rhs = Counter(3) +- expression 10 operands: lhs = Expression(11, Sub), rhs = Counter(2) +- expression 11 operands: lhs = Counter(0), rhs = Counter(1) +- expression 12 operands: lhs = Expression(13, Add), rhs = Counter(6) +- expression 13 operands: lhs = Expression(14, Add), rhs = Counter(5) +- expression 14 operands: lhs = Expression(0, Sub), rhs = Counter(4) +Number of file 0 mappings: 11 +- Code(Counter(0)) at (prev + 72, 1) to (start + 1, 14) +- Code(Counter(1)) at (prev + 2, 9) to (start + 0, 20) +- Branch { true: Expression(0, Sub), false: Expression(10, Add) } at (prev + 0, 9) to (start + 0, 20) + true = (c1 - c2) + false = ((c0 - c1) + c2) +- Code(Expression(0, Sub)) at (prev + 0, 24) to (start + 0, 34) + = (c1 - c2) +- Code(Counter(3)) at (prev + 1, 9) to (start + 0, 20) +- Branch { true: Counter(4), false: Expression(8, Add) } at (prev + 0, 9) to (start + 0, 20) + true = c4 + false = ((((c0 - c1) + c2) - c3) + c7) +- Code(Counter(4)) at (prev + 0, 24) to (start + 0, 40) +- Branch { true: Counter(6), false: Expression(7, Sub) } at (prev + 1, 9) to (start + 0, 13) + true = c6 + false = (((((c0 - c1) + c2) - c3) + c7) - c6) +- Code(Counter(6)) at (prev + 0, 17) to (start + 0, 34) +- Code(Counter(5)) at (prev + 1, 14) to (start + 0, 26) +- Code(Expression(12, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((((c1 - c2) + c4) + c5) + c6) + +Function name: match_misc::skipped_matching_decision +Raw bytes (59): 0x[01, 01, 03, 01, 05, 0b, 0d, 05, 09, 09, 01, 2b, 01, 01, 0e, 05, 02, 09, 00, 0a, 01, 00, 0e, 00, 0f, 20, 05, 02, 00, 0e, 00, 14, 05, 00, 13, 00, 2b, 20, 0d, 11, 01, 09, 00, 0b, 0d, 00, 0f, 00, 18, 09, 01, 0e, 00, 1a, 07, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(3) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 43, 1) to (start + 1, 14) +- Code(Counter(1)) at (prev + 2, 9) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 15) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 0, 14) to (start + 0, 20) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 43) +- Branch { true: Counter(3), false: Counter(4) } at (prev + 1, 9) to (start + 0, 11) + true = c3 + false = c4 +- Code(Counter(3)) at (prev + 0, 15) to (start + 0, 24) +- Code(Counter(2)) at (prev + 1, 14) to (start + 0, 26) +- Code(Expression(1, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((c1 + c2) + c3) + +Function name: match_misc::uncoverable_condition +Raw bytes (80): 0x[01, 01, 07, 00, 0d, 01, 0f, 05, 00, 05, 00, 11, 05, 1b, 15, 11, 05, 0a, 01, 5f, 01, 00, 2b, 03, 01, 0b, 00, 0e, 28, 03, 02, 01, 09, 00, 1f, 30, 06, 0f, 01, 02, 00, 00, 0a, 00, 13, 15, 00, 11, 00, 3c, 30, 15, 0d, 02, 00, 00, 00, 15, 00, 1e, 05, 01, 11, 00, 12, 11, 00, 25, 00, 26, 1b, 00, 2c, 00, 3f, 17, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 7 +- expression 0 operands: lhs = Zero, rhs = Counter(3) +- expression 1 operands: lhs = Counter(0), rhs = Expression(3, Add) +- expression 2 operands: lhs = Counter(1), rhs = Zero +- expression 3 operands: lhs = Counter(1), rhs = Zero +- expression 4 operands: lhs = Counter(4), rhs = Counter(1) +- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(5) +- expression 6 operands: lhs = Counter(4), rhs = Counter(1) +Number of file 0 mappings: 10 +- Code(Counter(0)) at (prev + 95, 1) to (start + 0, 43) +- Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 14) + = (Zero + c3) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 31) +- MCDCBranch { true: Expression(1, Sub), false: Expression(3, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 19) + true = (c0 - (c1 + Zero)) + false = (c1 + Zero) +- Code(Counter(5)) at (prev + 0, 17) to (start + 0, 60) +- MCDCBranch { true: Counter(5), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 30) + true = c5 + false = c3 +- Code(Counter(1)) at (prev + 1, 17) to (start + 0, 18) +- Code(Counter(4)) at (prev + 0, 37) to (start + 0, 38) +- Code(Expression(6, Add)) at (prev + 0, 44) to (start + 0, 63) + = (c4 + c1) +- Code(Expression(5, Add)) at (prev + 2, 1) to (start + 0, 2) + = ((c4 + c1) + c5) + diff --git a/tests/coverage/mcdc/match_misc.coverage b/tests/coverage/mcdc/match_misc.coverage new file mode 100644 index 0000000000000..2b4f233fe6078 --- /dev/null +++ b/tests/coverage/mcdc/match_misc.coverage @@ -0,0 +1,511 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ min-llvm-version: 19 + LL| |//@ compile-flags: -Zcoverage-options=mcdc + LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc + LL| | + LL| |// Loop over iterator contains pattern matching implicitly, + LL| |// do not generate mappings for it. + LL| 1|fn loop_over_iterator() { + LL| 4| for val in [1, 2, 3] { + ^3 + LL| 3| say(&val.to_string()); + LL| 3| } + LL| 1|} + LL| | + LL| |// Macro makes all conditions share same span. + LL| |// But now we don't generate mappings for it + LL| 3|fn match_with_macros(val: i32) { + LL| | macro_rules! variant_identifier { + LL| | ( + LL| | $val:expr, ($($index:expr),*) + LL| | )=> { + LL| | match $val { + LL| | $( + LL| | $index => say(&format!("{}",$index)), + LL| | )* + LL| | _ => say("not matched"), + LL| | } + LL| | } + LL| |} + LL| 3| variant_identifier!(val, (0, 1, 2)); + ^1 + LL| 3|} + LL| | + LL| |// No match pairs when lowering matching tree. + LL| 2|fn empty_matching_decision(val: i32) { + LL| 2| match val { + LL| 2| x if x > 8 && x < 10 => say("in (8, 10)"), + ^0 ^1 ^0 + ------------------ + | Branch (LL:14): [True: 1, False: 1] + | Branch (LL:23): [True: 0, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:29) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:14) + | Condition C2 --> (LL:23) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 2| x if x > 4 && x < 7 => say("in (4, 7)"), + ^1 ^1 + ------------------ + | Branch (LL:14): [True: 2, False: 0] + | Branch (LL:23): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:28) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:14) + | Condition C2 --> (LL:23) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, F = F } + | 2 { T, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| _ => say("other"), + LL| | } + LL| 2|} + LL| | + LL| |// Matching decision skips the first candidate + LL| 2|fn skipped_matching_decision(val: i32) { + LL| 2| match val { + LL| 2| x if x >= 0 => say("non-negative"), + ^0 ^0 + ------------------ + | Branch (LL:14): [True: 0, False: 2] + ------------------ + LL| 1| -1 => say("-1"), + ------------------ + | Branch (LL:9): [True: 1, False: 1] + ------------------ + LL| 1| _ => say("other"), + LL| | } + LL| 2|} + LL| | + LL| |// The first two candidates share same condition. + LL| 3|fn overlapping_decisions(val: (Option, Option)) { + LL| 1| match val { + LL| 1| (Some(_), Some(_)) => say("both some"), + ------------------ + | Branch (LL:10): [True: 2, False: 1] + | Branch (LL:19): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:27) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:19) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 1| (Some(_), None) | (None, Some(_)) => say("one and only one some"), + ------------------ + | Branch (LL:10): [True: 2, False: 1] + | Branch (LL:19): [True: 1, False: 1] + | Branch (LL:28): [True: 1, False: 0] + | Branch (LL:34): [True: 0, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:42) + | + | Number of Conditions: 4 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:19) + | Condition C3 --> (LL:28) + | Condition C4 --> (LL:34) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4 Result + | 1 { F, -, T, F = F } + | 2 { T, T, -, - = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | C3-Pair: not covered + | C4-Pair: not covered + | MC/DC Coverage for Decision: 25.00% + | + ------------------ + LL| 1| (None, None) => say("none"), + LL| | } + LL| 3|} + LL| | + LL| 4|fn partial_matched_decision(val: u8) { + LL| 4| // `b'-'` is the second test while `b'0'..=b'9'` is the last, though they + LL| 4| // are in same candidate. + LL| 4| match val { + LL| 2| b'"' | b'r' => say("quote or r"), + ------------------ + | Branch (LL:9): [True: 1, False: 3] + | Branch (LL:16): [True: 1, False: 2] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:20) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:16) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, F = F } + | 2 { F, T = T } + | 3 { T, - = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 2| b'0'..=b'9' | b'-' => say("number or -"), + ^1 + ------------------ + | Branch (LL:9): [True: 1, False: 0] + | Branch (LL:23): [True: 1, False: 3] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:27) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:23) + | Condition C2 --> (LL:9) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, T = T } + | 2 { T, - = T } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 0| b't' | b'f' => say("t or f"), + ------------------ + | Branch (LL:9): [True: 0, False: 4] + | Branch (LL:16): [True: 0, False: 4] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:20) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:16) + | + | Executed MC/DC Test Vectors: + | + | None. + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 0| _ => {} + LL| | } + LL| 4|} + LL| | + LL| |// Patterns are tested with several basic blocks. + LL| 3|fn partial_matched_with_several_blocks(val: u8) { + LL| 3| match val { + LL| 1| b'a'..=b'f' => say("hex"), + ------------------ + | Branch (LL:9): [True: 1, False: 2] + ------------------ + LL| 2| b'A'..=b'F' => say("hex upper"), + ^1 + ------------------ + | Branch (LL:9): [True: 1, False: 1] + ------------------ + LL| 1| b'_' => say("underscore"), + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ + LL| 0| _ => say("break"), + LL| | } + LL| 3|} + LL| | + LL| 2|fn match_failure_test_kind(val: bool, opt: Option) { + LL| 2| match (val, opt) { + LL| 1| (false, None) => say("none"), + ------------------ + | Branch (LL:10): [True: 2, False: 0] + | Branch (LL:17): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:22) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:17) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, F = F } + | 2 { T, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| (false, Some(_)) => say("some"), + ------------------ + | Branch (LL:10): [True: 2, False: 0] + | Branch (LL:17): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:25) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:17) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, T = T } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 0| _ => say("other"), + LL| | } + LL| 2|} + LL| | + LL| |enum Pat { + LL| | A(i32), + LL| | B(i32), + LL| |} + LL| | + LL| |// The last arm is shown like a condition but it never fails if tested. + LL| 2|fn uncoverable_condition(val: (Pat, Pat)) { + LL| 0| match val { + LL| 1| (Pat::A(a), Pat::A(b)) => say(&(a + b).to_string()), + ------------------ + | Branch (LL:10): [True: 1, False: 1] + | Branch (LL:21): [True: 1, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:31) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:21) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| (Pat::B(a), _) | (_, Pat::B(a)) => say(&a.to_string()), + ^0 + LL| | } + LL| 2|} + LL| | + LL| 3|fn nested_matching(a: bool, val: Pat) { + LL| 3| if a && match val { + ^2 + ------------------ + | Branch (LL:8): [True: 2, False: 1] + | Branch (LL:13): [True: 1, False: 1] + ------------------ + LL| 2| Pat::A(x) => x == 2, + ------------------ + | Branch (LL:9): [True: 2, False: 0] + ------------------ + LL| 0| _ => false, + LL| 1| } { + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:6) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 1| say("yes"); + LL| 2| } + LL| 3|} + LL| | + LL| |// It's possible to match two arms once. + LL| 2|fn multi_matched_candidates(val: Pat, a: i32) { + LL| 1| match val { + LL| 2| Pat::A(f) if f == a => say("first"), + ^1 ^1 + ------------------ + | Branch (LL:9): [True: 2, False: 0] + | Branch (LL:22): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:28) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:22) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, F = F } + | 2 { T, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| Pat::A(1) if a > 0 => say("second"), + ------------------ + | Branch (LL:9): [True: 2, False: 0] + | Branch (LL:16): [True: 1, False: 0] + | Branch (LL:22): [True: 1, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:27) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:16) + | Condition C3 --> (LL:22) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { T, T, T = T } + | + | C1-Pair: not covered + | C2-Pair: not covered + | C3-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 0| _ => say("other"), + LL| | } + LL| 2|} + LL| | + LL| 2|fn empty_subcandidate(val: Pat) { + LL| 2| match val { + LL| 1| Pat::A(1) | Pat::A(2) => say("first"), + ------------------ + | Branch (LL:9): [True: 1, False: 1] + | Branch (LL:16): [True: 1, False: 0] + | Branch (LL:28): [True: 0, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:30) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:16) + | Condition C3 --> (LL:28) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { F, -, - = F } + | 2 { T, T, - = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | C3-Pair: not covered + | MC/DC Coverage for Decision: 33.33% + | + ------------------ + LL| | // The first two condition in this pattern is redundant indeed. + LL| | // But this piece of code is legitimate and it could cause a subcandidate + LL| | // with no match pair. + LL| 1| Pat::A(_) | Pat::B(_) | _ => say("other"), + LL| | } + LL| 2|} + LL| |#[coverage(off)] + LL| |fn main() { + LL| | loop_over_iterator(); + LL| | + LL| | match_with_macros(0); + LL| | match_with_macros(2); + LL| | match_with_macros(5); + LL| | + LL| | empty_matching_decision(12); + LL| | empty_matching_decision(5); + LL| | + LL| | skipped_matching_decision(-1); + LL| | skipped_matching_decision(-5); + LL| | + LL| | overlapping_decisions((Some(1), Some(2))); + LL| | overlapping_decisions((Some(1), None)); + LL| | overlapping_decisions((None, None)); + LL| | + LL| | partial_matched_decision(b'"'); + LL| | partial_matched_decision(b'r'); + LL| | partial_matched_decision(b'7'); + LL| | partial_matched_decision(b'-'); + LL| | + LL| | partial_matched_with_several_blocks(b'd'); + LL| | partial_matched_with_several_blocks(b'D'); + LL| | partial_matched_with_several_blocks(b'_'); + LL| | + LL| | match_failure_test_kind(false, None); + LL| | match_failure_test_kind(false, Some(1)); + LL| | + LL| | uncoverable_condition((Pat::A(1), Pat::A(2))); + LL| | uncoverable_condition((Pat::B(1), Pat::B(2))); + LL| | + LL| | nested_matching(true, Pat::A(1)); + LL| | nested_matching(true, Pat::A(2)); + LL| | nested_matching(false, Pat::A(2)); + LL| | + LL| | multi_matched_candidates(Pat::A(1), 1); + LL| | multi_matched_candidates(Pat::A(1), 8); + LL| | + LL| | empty_subcandidate(Pat::A(1)); + LL| | empty_subcandidate(Pat::B(1)) + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + diff --git a/tests/coverage/mcdc/match_misc.rs b/tests/coverage/mcdc/match_misc.rs new file mode 100644 index 0000000000000..3f54560c355de --- /dev/null +++ b/tests/coverage/mcdc/match_misc.rs @@ -0,0 +1,176 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ min-llvm-version: 19 +//@ compile-flags: -Zcoverage-options=mcdc +//@ llvm-cov-flags: --show-branches=count --show-mcdc + +// Loop over iterator contains pattern matching implicitly, +// do not generate mappings for it. +fn loop_over_iterator() { + for val in [1, 2, 3] { + say(&val.to_string()); + } +} + +// Macro makes all conditions share same span. +// But now we don't generate mappings for it +fn match_with_macros(val: i32) { + macro_rules! variant_identifier { + ( + $val:expr, ($($index:expr),*) + )=> { + match $val { + $( + $index => say(&format!("{}",$index)), + )* + _ => say("not matched"), + } + } +} + variant_identifier!(val, (0, 1, 2)); +} + +// No match pairs when lowering matching tree. +fn empty_matching_decision(val: i32) { + match val { + x if x > 8 && x < 10 => say("in (8, 10)"), + x if x > 4 && x < 7 => say("in (4, 7)"), + _ => say("other"), + } +} + +// Matching decision skips the first candidate +fn skipped_matching_decision(val: i32) { + match val { + x if x >= 0 => say("non-negative"), + -1 => say("-1"), + _ => say("other"), + } +} + +// The first two candidates share same condition. +fn overlapping_decisions(val: (Option, Option)) { + match val { + (Some(_), Some(_)) => say("both some"), + (Some(_), None) | (None, Some(_)) => say("one and only one some"), + (None, None) => say("none"), + } +} + +fn partial_matched_decision(val: u8) { + // `b'-'` is the second test while `b'0'..=b'9'` is the last, though they + // are in same candidate. + match val { + b'"' | b'r' => say("quote or r"), + b'0'..=b'9' | b'-' => say("number or -"), + b't' | b'f' => say("t or f"), + _ => {} + } +} + +// Patterns are tested with several basic blocks. +fn partial_matched_with_several_blocks(val: u8) { + match val { + b'a'..=b'f' => say("hex"), + b'A'..=b'F' => say("hex upper"), + b'_' => say("underscore"), + _ => say("break"), + } +} + +fn match_failure_test_kind(val: bool, opt: Option) { + match (val, opt) { + (false, None) => say("none"), + (false, Some(_)) => say("some"), + _ => say("other"), + } +} + +enum Pat { + A(i32), + B(i32), +} + +// The last arm is shown like a condition but it never fails if tested. +fn uncoverable_condition(val: (Pat, Pat)) { + match val { + (Pat::A(a), Pat::A(b)) => say(&(a + b).to_string()), + (Pat::B(a), _) | (_, Pat::B(a)) => say(&a.to_string()), + } +} + +fn nested_matching(a: bool, val: Pat) { + if a && match val { + Pat::A(x) => x == 2, + _ => false, + } { + say("yes"); + } +} + +// It's possible to match two arms once. +fn multi_matched_candidates(val: Pat, a: i32) { + match val { + Pat::A(f) if f == a => say("first"), + Pat::A(1) if a > 0 => say("second"), + _ => say("other"), + } +} + +fn empty_subcandidate(val: Pat) { + match val { + Pat::A(1) | Pat::A(2) => say("first"), + // The first two condition in this pattern is redundant indeed. + // But this piece of code is legitimate and it could cause a subcandidate + // with no match pair. + Pat::A(_) | Pat::B(_) | _ => say("other"), + } +} +#[coverage(off)] +fn main() { + loop_over_iterator(); + + match_with_macros(0); + match_with_macros(2); + match_with_macros(5); + + empty_matching_decision(12); + empty_matching_decision(5); + + skipped_matching_decision(-1); + skipped_matching_decision(-5); + + overlapping_decisions((Some(1), Some(2))); + overlapping_decisions((Some(1), None)); + overlapping_decisions((None, None)); + + partial_matched_decision(b'"'); + partial_matched_decision(b'r'); + partial_matched_decision(b'7'); + partial_matched_decision(b'-'); + + partial_matched_with_several_blocks(b'd'); + partial_matched_with_several_blocks(b'D'); + partial_matched_with_several_blocks(b'_'); + + match_failure_test_kind(false, None); + match_failure_test_kind(false, Some(1)); + + uncoverable_condition((Pat::A(1), Pat::A(2))); + uncoverable_condition((Pat::B(1), Pat::B(2))); + + nested_matching(true, Pat::A(1)); + nested_matching(true, Pat::A(2)); + nested_matching(false, Pat::A(2)); + + multi_matched_candidates(Pat::A(1), 1); + multi_matched_candidates(Pat::A(1), 8); + + empty_subcandidate(Pat::A(1)); + empty_subcandidate(Pat::B(1)) +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +} diff --git a/tests/coverage/mcdc/match_pattern.cov-map b/tests/coverage/mcdc/match_pattern.cov-map new file mode 100644 index 0000000000000..e4ef60dcc16ec --- /dev/null +++ b/tests/coverage/mcdc/match_pattern.cov-map @@ -0,0 +1,334 @@ +Function name: match_pattern::joint_or_patterns +Raw bytes (342): 0x[01, 01, 29, 01, 0b, 05, 09, 05, 09, 02, 0d, 1d, 19, 1d, 21, 25, 15, 25, 15, 11, 15, 35, 29, 2d, 31, 35, a3, 01, 39, 3d, 45, 9e, 01, 35, a3, 01, 39, 3d, 47, 3d, 4b, 39, 41, 62, 66, 6b, 2d, 31, 45, 41, 41, 39, 62, 3d, 66, 6b, 2d, 31, 45, 41, 97, 01, 45, 9b, 01, 41, 39, 9e, 01, 35, a3, 01, 39, 3d, 49, 4d, 8b, 01, 93, 01, 8f, 01, 51, 49, 4d, 97, 01, 45, 9b, 01, 41, 39, 9e, 01, 35, a3, 01, 39, 3d, 20, 01, 2e, 01, 00, 27, 25, 01, 0b, 00, 0e, 28, 0b, 05, 01, 09, 00, 3d, 30, 02, 0b, 01, 02, 03, 00, 0a, 00, 19, 30, 0e, 0d, 02, 04, 00, 00, 11, 00, 18, 30, 05, 09, 03, 04, 00, 00, 1c, 00, 25, 30, 21, 13, 04, 00, 05, 00, 27, 00, 30, 30, 1d, 19, 05, 00, 00, 00, 33, 00, 3c, 17, 00, 41, 00, 63, 28, 03, 02, 01, 09, 00, 1f, 30, 25, 11, 01, 02, 00, 00, 0a, 00, 13, 30, 1e, 15, 02, 00, 00, 00, 15, 00, 1e, 1e, 00, 23, 00, 39, 23, 01, 0e, 00, 23, 55, 04, 0b, 00, 0e, 28, 16, 05, 01, 09, 00, 3d, 30, 2d, 27, 01, 02, 03, 00, 0a, 00, 19, 30, 66, 31, 02, 04, 00, 00, 11, 00, 18, 45, 00, 16, 00, 17, 30, 35, 29, 03, 04, 00, 00, 1c, 00, 25, 9e, 01, 00, 23, 00, 24, 30, 37, 43, 04, 00, 05, 00, 27, 00, 30, 45, 00, 2e, 00, 2f, 30, 5b, 5f, 05, 00, 00, 00, 33, 00, 3c, 41, 00, 3a, 00, 3b, 93, 01, 01, 0d, 00, 45, 28, 0e, 02, 02, 09, 00, 1f, 30, 55, 49, 01, 02, 00, 00, 0a, 00, 13, 30, 51, 4d, 02, 00, 00, 00, 15, 00, 1e, 51, 00, 23, 00, 39, 8f, 01, 01, 0e, 00, 23, 87, 01, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 41 +- expression 0 operands: lhs = Counter(0), rhs = Expression(2, Add) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Expression(0, Sub), rhs = Counter(3) +- expression 4 operands: lhs = Counter(7), rhs = Counter(6) +- expression 5 operands: lhs = Counter(7), rhs = Counter(8) +- expression 6 operands: lhs = Counter(9), rhs = Counter(5) +- expression 7 operands: lhs = Counter(9), rhs = Counter(5) +- expression 8 operands: lhs = Counter(4), rhs = Counter(5) +- expression 9 operands: lhs = Counter(13), rhs = Counter(10) +- expression 10 operands: lhs = Counter(11), rhs = Counter(12) +- expression 11 operands: lhs = Counter(13), rhs = Expression(40, Add) +- expression 12 operands: lhs = Counter(14), rhs = Counter(15) +- expression 13 operands: lhs = Counter(17), rhs = Expression(39, Sub) +- expression 14 operands: lhs = Counter(13), rhs = Expression(40, Add) +- expression 15 operands: lhs = Counter(14), rhs = Counter(15) +- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(15) +- expression 17 operands: lhs = Expression(18, Add), rhs = Counter(14) +- expression 18 operands: lhs = Counter(16), rhs = Expression(24, Sub) +- expression 19 operands: lhs = Expression(25, Sub), rhs = Expression(26, Add) +- expression 20 operands: lhs = Counter(11), rhs = Counter(12) +- expression 21 operands: lhs = Counter(17), rhs = Counter(16) +- expression 22 operands: lhs = Counter(16), rhs = Counter(14) +- expression 23 operands: lhs = Expression(24, Sub), rhs = Counter(15) +- expression 24 operands: lhs = Expression(25, Sub), rhs = Expression(26, Add) +- expression 25 operands: lhs = Counter(11), rhs = Counter(12) +- expression 26 operands: lhs = Counter(17), rhs = Counter(16) +- expression 27 operands: lhs = Expression(37, Add), rhs = Counter(17) +- expression 28 operands: lhs = Expression(38, Add), rhs = Counter(16) +- expression 29 operands: lhs = Counter(14), rhs = Expression(39, Sub) +- expression 30 operands: lhs = Counter(13), rhs = Expression(40, Add) +- expression 31 operands: lhs = Counter(14), rhs = Counter(15) +- expression 32 operands: lhs = Counter(18), rhs = Counter(19) +- expression 33 operands: lhs = Expression(34, Add), rhs = Expression(36, Add) +- expression 34 operands: lhs = Expression(35, Add), rhs = Counter(20) +- expression 35 operands: lhs = Counter(18), rhs = Counter(19) +- expression 36 operands: lhs = Expression(37, Add), rhs = Counter(17) +- expression 37 operands: lhs = Expression(38, Add), rhs = Counter(16) +- expression 38 operands: lhs = Counter(14), rhs = Expression(39, Sub) +- expression 39 operands: lhs = Counter(13), rhs = Expression(40, Add) +- expression 40 operands: lhs = Counter(14), rhs = Counter(15) +Number of file 0 mappings: 32 +- Code(Counter(0)) at (prev + 46, 1) to (start + 0, 39) +- Code(Counter(9)) at (prev + 1, 11) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 11, conditions_num: 5 } at (prev + 1, 9) to (start + 0, 61) +- MCDCBranch { true: Expression(0, Sub), false: Expression(2, Add), condition_id: 1, true_next_id: 2, false_next_id: 3 } at (prev + 0, 10) to (start + 0, 25) + true = (c0 - (c1 + c2)) + false = (c1 + c2) +- MCDCBranch { true: Expression(3, Sub), false: Counter(3), condition_id: 2, true_next_id: 4, false_next_id: 0 } at (prev + 0, 17) to (start + 0, 24) + true = ((c0 - (c1 + c2)) - c3) + false = c3 +- MCDCBranch { true: Counter(1), false: Counter(2), condition_id: 3, true_next_id: 4, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 37) + true = c1 + false = c2 +- MCDCBranch { true: Counter(8), false: Expression(4, Add), condition_id: 4, true_next_id: 0, false_next_id: 5 } at (prev + 0, 39) to (start + 0, 48) + true = c8 + false = (c7 + c6) +- MCDCBranch { true: Counter(7), false: Counter(6), condition_id: 5, true_next_id: 0, false_next_id: 0 } at (prev + 0, 51) to (start + 0, 60) + true = c7 + false = c6 +- Code(Expression(5, Add)) at (prev + 0, 65) to (start + 0, 99) + = (c7 + c8) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 31) +- MCDCBranch { true: Counter(9), false: Counter(4), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 19) + true = c9 + false = c4 +- MCDCBranch { true: Expression(7, Sub), false: Counter(5), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 30) + true = (c9 - c5) + false = c5 +- Code(Expression(7, Sub)) at (prev + 0, 35) to (start + 0, 57) + = (c9 - c5) +- Code(Expression(8, Add)) at (prev + 1, 14) to (start + 0, 35) + = (c4 + c5) +- Code(Counter(21)) at (prev + 4, 11) to (start + 0, 14) +- MCDCDecision { bitmap_idx: 22, conditions_num: 5 } at (prev + 1, 9) to (start + 0, 61) +- MCDCBranch { true: Counter(11), false: Expression(9, Add), condition_id: 1, true_next_id: 2, false_next_id: 3 } at (prev + 0, 10) to (start + 0, 25) + true = c11 + false = (c13 + c10) +- MCDCBranch { true: Expression(25, Sub), false: Counter(12), condition_id: 2, true_next_id: 4, false_next_id: 0 } at (prev + 0, 17) to (start + 0, 24) + true = (c11 - c12) + false = c12 +- Code(Counter(17)) at (prev + 0, 22) to (start + 0, 23) +- MCDCBranch { true: Counter(13), false: Counter(10), condition_id: 3, true_next_id: 4, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 37) + true = c13 + false = c10 +- Code(Expression(39, Sub)) at (prev + 0, 35) to (start + 0, 36) + = (c13 - (c14 + c15)) +- MCDCBranch { true: Expression(13, Add), false: Expression(16, Add), condition_id: 4, true_next_id: 0, false_next_id: 5 } at (prev + 0, 39) to (start + 0, 48) + true = (c17 + (c13 - (c14 + c15))) + false = (((c16 + ((c11 - c12) - (c17 + c16))) + c14) + c15) +- Code(Counter(17)) at (prev + 0, 46) to (start + 0, 47) +- MCDCBranch { true: Expression(22, Add), false: Expression(23, Add), condition_id: 5, true_next_id: 0, false_next_id: 0 } at (prev + 0, 51) to (start + 0, 60) + true = (c16 + c14) + false = (((c11 - c12) - (c17 + c16)) + c15) +- Code(Counter(16)) at (prev + 0, 58) to (start + 0, 59) +- Code(Expression(36, Add)) at (prev + 1, 13) to (start + 0, 69) + = (((c14 + (c13 - (c14 + c15))) + c16) + c17) +- MCDCDecision { bitmap_idx: 14, conditions_num: 2 } at (prev + 2, 9) to (start + 0, 31) +- MCDCBranch { true: Counter(21), false: Counter(18), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 19) + true = c21 + false = c18 +- MCDCBranch { true: Counter(20), false: Counter(19), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 30) + true = c20 + false = c19 +- Code(Counter(20)) at (prev + 0, 35) to (start + 0, 57) +- Code(Expression(35, Add)) at (prev + 1, 14) to (start + 0, 35) + = (c18 + c19) +- Code(Expression(33, Add)) at (prev + 2, 1) to (start + 0, 2) + = (((c18 + c19) + c20) + (((c14 + (c13 - (c14 + c15))) + c16) + c17)) + +Function name: match_pattern::joint_pattern_with_or +Raw bytes (150): 0x[01, 01, 15, 2a, 13, 01, 2f, 05, 09, 1d, 11, 1d, 11, 01, 2f, 05, 09, 05, 09, 1d, 21, 2a, 09, 01, 2f, 05, 09, 4b, 15, 4f, 11, 09, 0d, 43, 53, 47, 19, 4b, 15, 4f, 11, 09, 0d, 1d, 21, 0e, 01, 26, 01, 00, 2b, 02, 01, 0b, 00, 0e, 28, 08, 04, 01, 09, 00, 31, 30, 02, 13, 02, 03, 04, 00, 0a, 00, 19, 30, 21, 15, 03, 00, 00, 00, 11, 00, 18, 30, 1d, 11, 04, 00, 00, 00, 1c, 00, 25, 30, 2a, 2f, 01, 02, 00, 00, 27, 00, 30, 53, 00, 35, 00, 53, 28, 03, 02, 01, 09, 00, 1f, 30, 19, 0d, 02, 00, 00, 00, 0a, 00, 13, 30, 05, 27, 01, 02, 00, 00, 15, 00, 1e, 19, 00, 23, 00, 39, 47, 01, 0e, 00, 23, 3f, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 21 +- expression 0 operands: lhs = Expression(10, Sub), rhs = Expression(4, Add) +- expression 1 operands: lhs = Counter(0), rhs = Expression(11, Add) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Counter(7), rhs = Counter(4) +- expression 4 operands: lhs = Counter(7), rhs = Counter(4) +- expression 5 operands: lhs = Counter(0), rhs = Expression(11, Add) +- expression 6 operands: lhs = Counter(1), rhs = Counter(2) +- expression 7 operands: lhs = Counter(1), rhs = Counter(2) +- expression 8 operands: lhs = Counter(7), rhs = Counter(8) +- expression 9 operands: lhs = Expression(10, Sub), rhs = Counter(2) +- expression 10 operands: lhs = Counter(0), rhs = Expression(11, Add) +- expression 11 operands: lhs = Counter(1), rhs = Counter(2) +- expression 12 operands: lhs = Expression(18, Add), rhs = Counter(5) +- expression 13 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 14 operands: lhs = Counter(2), rhs = Counter(3) +- expression 15 operands: lhs = Expression(16, Add), rhs = Expression(20, Add) +- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(6) +- expression 17 operands: lhs = Expression(18, Add), rhs = Counter(5) +- expression 18 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 19 operands: lhs = Counter(2), rhs = Counter(3) +- expression 20 operands: lhs = Counter(7), rhs = Counter(8) +Number of file 0 mappings: 14 +- Code(Counter(0)) at (prev + 38, 1) to (start + 0, 43) +- Code(Expression(0, Sub)) at (prev + 1, 11) to (start + 0, 14) + = ((c0 - (c1 + c2)) - (c7 + c4)) +- MCDCDecision { bitmap_idx: 8, conditions_num: 4 } at (prev + 1, 9) to (start + 0, 49) +- MCDCBranch { true: Expression(0, Sub), false: Expression(4, Add), condition_id: 2, true_next_id: 3, false_next_id: 4 } at (prev + 0, 10) to (start + 0, 25) + true = ((c0 - (c1 + c2)) - (c7 + c4)) + false = (c7 + c4) +- MCDCBranch { true: Counter(8), false: Counter(5), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 17) to (start + 0, 24) + true = c8 + false = c5 +- MCDCBranch { true: Counter(7), false: Counter(4), condition_id: 4, true_next_id: 0, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 37) + true = c7 + false = c4 +- MCDCBranch { true: Expression(10, Sub), false: Expression(11, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 39) to (start + 0, 48) + true = (c0 - (c1 + c2)) + false = (c1 + c2) +- Code(Expression(20, Add)) at (prev + 0, 53) to (start + 0, 83) + = (c7 + c8) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 31) +- MCDCBranch { true: Counter(6), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 19) + true = c6 + false = c3 +- MCDCBranch { true: Counter(1), false: Expression(9, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 30) + true = c1 + false = ((c0 - (c1 + c2)) + c2) +- Code(Counter(6)) at (prev + 0, 35) to (start + 0, 57) +- Code(Expression(17, Add)) at (prev + 1, 14) to (start + 0, 35) + = (((c2 + c3) + c4) + c5) +- Code(Expression(15, Add)) at (prev + 2, 1) to (start + 0, 2) + = (((((c2 + c3) + c4) + c5) + c6) + (c7 + c8)) + +Function name: match_pattern::partial_matched +Raw bytes (89): 0x[01, 01, 08, 01, 05, 02, 09, 05, 09, 12, 0d, 02, 09, 1b, 11, 1f, 0d, 05, 09, 0b, 01, 3f, 01, 01, 0e, 28, 03, 02, 02, 09, 00, 23, 30, 05, 02, 01, 00, 02, 00, 09, 00, 13, 05, 00, 0a, 00, 12, 30, 09, 12, 02, 00, 00, 00, 16, 00, 23, 09, 00, 1a, 00, 22, 1f, 00, 27, 00, 4f, 20, 0d, 0e, 01, 09, 00, 1d, 0d, 00, 10, 00, 49, 11, 01, 0e, 00, 23, 17, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 8 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(0, Sub), rhs = Counter(2) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Expression(4, Sub), rhs = Counter(3) +- expression 4 operands: lhs = Expression(0, Sub), rhs = Counter(2) +- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(4) +- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(3) +- expression 7 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 11 +- Code(Counter(0)) at (prev + 63, 1) to (start + 1, 14) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 2, 9) to (start + 0, 35) +- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 9) to (start + 0, 19) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 10) to (start + 0, 18) +- MCDCBranch { true: Counter(2), false: Expression(4, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 22) to (start + 0, 35) + true = c2 + false = ((c0 - c1) - c2) +- Code(Counter(2)) at (prev + 0, 26) to (start + 0, 34) +- Code(Expression(7, Add)) at (prev + 0, 39) to (start + 0, 79) + = (c1 + c2) +- Branch { true: Counter(3), false: Expression(3, Sub) } at (prev + 1, 9) to (start + 0, 29) + true = c3 + false = (((c0 - c1) - c2) - c3) +- Code(Counter(3)) at (prev + 0, 16) to (start + 0, 73) +- Code(Counter(4)) at (prev + 1, 14) to (start + 0, 35) +- Code(Expression(5, Add)) at (prev + 2, 1) to (start + 0, 2) + = (((c1 + c2) + c3) + c4) + +Function name: match_pattern::simple_joint_pattern +Raw bytes (132): 0x[01, 01, 11, 1e, 11, 01, 23, 05, 09, 01, 23, 05, 09, 05, 09, 1e, 09, 01, 23, 05, 09, 3f, 15, 43, 11, 09, 0d, 37, 1d, 3b, 19, 3f, 15, 43, 11, 09, 0d, 0d, 01, 1e, 01, 00, 2a, 02, 01, 0b, 00, 0e, 28, 07, 03, 01, 09, 00, 25, 30, 1e, 23, 01, 02, 00, 00, 0a, 00, 19, 30, 1d, 15, 03, 00, 00, 00, 11, 00, 18, 30, 02, 11, 02, 03, 00, 00, 1b, 00, 24, 1d, 00, 29, 00, 43, 28, 03, 02, 01, 09, 00, 1f, 30, 05, 1b, 01, 02, 00, 00, 0a, 00, 13, 30, 19, 0d, 02, 00, 00, 00, 15, 00, 1e, 19, 00, 23, 00, 39, 3b, 01, 0e, 00, 23, 33, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 17 +- expression 0 operands: lhs = Expression(7, Sub), rhs = Counter(4) +- expression 1 operands: lhs = Counter(0), rhs = Expression(8, Add) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Counter(0), rhs = Expression(8, Add) +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(2) +- expression 7 operands: lhs = Counter(0), rhs = Expression(8, Add) +- expression 8 operands: lhs = Counter(1), rhs = Counter(2) +- expression 9 operands: lhs = Expression(15, Add), rhs = Counter(5) +- expression 10 operands: lhs = Expression(16, Add), rhs = Counter(4) +- expression 11 operands: lhs = Counter(2), rhs = Counter(3) +- expression 12 operands: lhs = Expression(13, Add), rhs = Counter(7) +- expression 13 operands: lhs = Expression(14, Add), rhs = Counter(6) +- expression 14 operands: lhs = Expression(15, Add), rhs = Counter(5) +- expression 15 operands: lhs = Expression(16, Add), rhs = Counter(4) +- expression 16 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 13 +- Code(Counter(0)) at (prev + 30, 1) to (start + 0, 42) +- Code(Expression(0, Sub)) at (prev + 1, 11) to (start + 0, 14) + = ((c0 - (c1 + c2)) - c4) +- MCDCDecision { bitmap_idx: 7, conditions_num: 3 } at (prev + 1, 9) to (start + 0, 37) +- MCDCBranch { true: Expression(7, Sub), false: Expression(8, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 25) + true = (c0 - (c1 + c2)) + false = (c1 + c2) +- MCDCBranch { true: Counter(7), false: Counter(5), condition_id: 3, true_next_id: 0, false_next_id: 0 } at (prev + 0, 17) to (start + 0, 24) + true = c7 + false = c5 +- MCDCBranch { true: Expression(0, Sub), false: Counter(4), condition_id: 2, true_next_id: 3, false_next_id: 0 } at (prev + 0, 27) to (start + 0, 36) + true = ((c0 - (c1 + c2)) - c4) + false = c4 +- Code(Counter(7)) at (prev + 0, 41) to (start + 0, 67) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 31) +- MCDCBranch { true: Counter(1), false: Expression(6, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 10) to (start + 0, 19) + true = c1 + false = ((c0 - (c1 + c2)) + c2) +- MCDCBranch { true: Counter(6), false: Counter(3), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 30) + true = c6 + false = c3 +- Code(Counter(6)) at (prev + 0, 35) to (start + 0, 57) +- Code(Expression(14, Add)) at (prev + 1, 14) to (start + 0, 35) + = (((c2 + c3) + c4) + c5) +- Code(Expression(12, Add)) at (prev + 2, 1) to (start + 0, 2) + = (((((c2 + c3) + c4) + c5) + c6) + c7) + +Function name: match_pattern::simple_or_pattern +Raw bytes (63): 0x[01, 01, 06, 01, 0b, 05, 09, 05, 09, 05, 02, 0d, 17, 05, 02, 07, 01, 17, 01, 01, 0e, 28, 03, 02, 02, 09, 00, 1e, 30, 02, 0b, 01, 00, 02, 00, 09, 00, 12, 30, 05, 09, 02, 00, 00, 00, 15, 00, 1e, 17, 00, 22, 00, 37, 0d, 01, 0e, 00, 1e, 13, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 6 +- expression 0 operands: lhs = Counter(0), rhs = Expression(2, Add) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Counter(1), rhs = Expression(0, Sub) +- expression 4 operands: lhs = Counter(3), rhs = Expression(5, Add) +- expression 5 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 23, 1) to (start + 1, 14) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 2, 9) to (start + 0, 30) +- MCDCBranch { true: Expression(0, Sub), false: Expression(2, Add), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 9) to (start + 0, 18) + true = (c0 - (c1 + c2)) + false = (c1 + c2) +- MCDCBranch { true: Counter(1), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 30) + true = c1 + false = c2 +- Code(Expression(5, Add)) at (prev + 0, 34) to (start + 0, 55) + = (c1 + (c0 - (c1 + c2))) +- Code(Counter(3)) at (prev + 1, 14) to (start + 0, 30) +- Code(Expression(4, Add)) at (prev + 2, 1) to (start + 0, 2) + = (c3 + (c1 + (c0 - (c1 + c2)))) + +Function name: match_pattern::single_nested_pattern +Raw bytes (136): 0x[01, 01, 12, 01, 23, 27, 00, 05, 09, 27, 00, 05, 09, 15, 2e, 02, 33, 15, 11, 27, 00, 05, 09, 11, 2e, 02, 33, 15, 11, 3b, 00, 02, 09, 43, 15, 47, 11, 09, 05, 0e, 01, 0e, 01, 00, 24, 02, 01, 0b, 00, 0e, 28, 06, 02, 01, 09, 00, 18, 30, 02, 23, 01, 02, 00, 00, 09, 00, 18, 30, 11, 17, 02, 00, 00, 00, 10, 00, 17, 11, 00, 1c, 00, 32, 28, 03, 02, 01, 09, 00, 15, 30, 02, 23, 01, 02, 00, 00, 09, 00, 15, 30, 15, 2b, 02, 00, 00, 00, 10, 00, 14, 15, 00, 19, 00, 2f, 20, 05, 37, 01, 09, 00, 12, 05, 00, 16, 00, 26, 09, 01, 16, 00, 26, 3f, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 18 +- expression 0 operands: lhs = Counter(0), rhs = Expression(8, Add) +- expression 1 operands: lhs = Expression(9, Add), rhs = Zero +- expression 2 operands: lhs = Counter(1), rhs = Counter(2) +- expression 3 operands: lhs = Expression(9, Add), rhs = Zero +- expression 4 operands: lhs = Counter(1), rhs = Counter(2) +- expression 5 operands: lhs = Counter(5), rhs = Expression(11, Sub) +- expression 6 operands: lhs = Expression(0, Sub), rhs = Expression(12, Add) +- expression 7 operands: lhs = Counter(5), rhs = Counter(4) +- expression 8 operands: lhs = Expression(9, Add), rhs = Zero +- expression 9 operands: lhs = Counter(1), rhs = Counter(2) +- expression 10 operands: lhs = Counter(4), rhs = Expression(11, Sub) +- expression 11 operands: lhs = Expression(0, Sub), rhs = Expression(12, Add) +- expression 12 operands: lhs = Counter(5), rhs = Counter(4) +- expression 13 operands: lhs = Expression(14, Add), rhs = Zero +- expression 14 operands: lhs = Expression(0, Sub), rhs = Counter(2) +- expression 15 operands: lhs = Expression(16, Add), rhs = Counter(5) +- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(4) +- expression 17 operands: lhs = Counter(2), rhs = Counter(1) +Number of file 0 mappings: 14 +- Code(Counter(0)) at (prev + 14, 1) to (start + 0, 36) +- Code(Expression(0, Sub)) at (prev + 1, 11) to (start + 0, 14) + = (c0 - ((c1 + c2) + Zero)) +- MCDCDecision { bitmap_idx: 6, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 24) +- MCDCBranch { true: Expression(0, Sub), false: Expression(8, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 24) + true = (c0 - ((c1 + c2) + Zero)) + false = ((c1 + c2) + Zero) +- MCDCBranch { true: Counter(4), false: Expression(5, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 16) to (start + 0, 23) + true = c4 + false = (c5 + ((c0 - ((c1 + c2) + Zero)) - (c5 + c4))) +- Code(Counter(4)) at (prev + 0, 28) to (start + 0, 50) +- MCDCDecision { bitmap_idx: 3, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 21) +- MCDCBranch { true: Expression(0, Sub), false: Expression(8, Add), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 21) + true = (c0 - ((c1 + c2) + Zero)) + false = ((c1 + c2) + Zero) +- MCDCBranch { true: Counter(5), false: Expression(10, Add), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 16) to (start + 0, 20) + true = c5 + false = (c4 + ((c0 - ((c1 + c2) + Zero)) - (c5 + c4))) +- Code(Counter(5)) at (prev + 0, 25) to (start + 0, 47) +- Branch { true: Counter(1), false: Expression(13, Add) } at (prev + 1, 9) to (start + 0, 18) + true = c1 + false = (((c0 - ((c1 + c2) + Zero)) + c2) + Zero) +- Code(Counter(1)) at (prev + 0, 22) to (start + 0, 38) +- Code(Counter(2)) at (prev + 1, 22) to (start + 0, 38) +- Code(Expression(15, Add)) at (prev + 2, 1) to (start + 0, 2) + = (((c2 + c1) + c4) + c5) + diff --git a/tests/coverage/mcdc/match_pattern.coverage b/tests/coverage/mcdc/match_pattern.coverage new file mode 100644 index 0000000000000..8eab2e953fdda --- /dev/null +++ b/tests/coverage/mcdc/match_pattern.coverage @@ -0,0 +1,392 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ min-llvm-version: 19 + LL| |//@ compile-flags: -Zcoverage-options=mcdc + LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc + LL| | + LL| |#[derive(Clone, Copy)] + LL| |enum Pat { + LL| | A(Option), + LL| | B(i32), + LL| | C(i32), + LL| |} + LL| | + LL| 2|fn single_nested_pattern(pat: Pat) { + LL| 1| match pat { + LL| 1| Pat::A(Some(_)) => say("matched A::Some"), + ------------------ + | Branch (LL:9): [True: 1, False: 1] + | Branch (LL:16): [True: 1, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:24) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:16) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 0| Pat::A(None) => say("matched A::None"), + ------------------ + | Branch (LL:9): [True: 1, False: 1] + | Branch (LL:16): [True: 0, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:21) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:16) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 1| Pat::B(_) => say("matched B"), + ------------------ + | Branch (LL:9): [True: 1, False: 1] + ------------------ + LL| 0| Pat::C(_) => say("matched C"), + LL| | } + LL| 2|} + LL| | + LL| 2|fn simple_or_pattern(pat: Pat) { + LL| 2| match pat { + LL| 1| Pat::B(_) | Pat::C(_) => say("matched B or C"), + ------------------ + | Branch (LL:9): [True: 0, False: 2] + | Branch (LL:21): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:30) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:21) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, F = F } + | 2 { F, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| _ => say("matched A"), + LL| | } + LL| 2|} + LL| | + LL| 3|fn simple_joint_pattern(pat: (Pat, Pat)) { + LL| 1| match pat { + LL| 1| (Pat::A(Some(_)), Pat::B(_)) => say("matched A::Some + B"), + ------------------ + | Branch (LL:10): [True: 2, False: 1] + | Branch (LL:17): [True: 1, False: 0] + | Branch (LL:27): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:37) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:17) + | Condition C3 --> (LL:27) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { F, -, - = F } + | 2 { T, -, F = F } + | 3 { T, T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: not covered + | C3-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 66.67% + | + ------------------ + LL| 0| (Pat::B(_), Pat::C(_)) => say("matched B and C"), + ------------------ + | Branch (LL:10): [True: 1, False: 2] + | Branch (LL:21): [True: 0, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:31) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:21) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 2| _ => say("matched others"), + LL| | } + LL| 3|} + LL| | + LL| 4|fn joint_pattern_with_or(pat: (Pat, Pat)) { + LL| 1| match pat { + LL| 2| (Pat::A(Some(_)) | Pat::C(_), Pat::B(_)) => say("matched A::Some | C + B"), + ------------------ + | Branch (LL:10): [True: 1, False: 2] + | Branch (LL:17): [True: 1, False: 0] + | Branch (LL:28): [True: 1, False: 1] + | Branch (LL:39): [True: 3, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:49) + | + | Number of Conditions: 4 + | Condition C1 --> (LL:39) + | Condition C2 --> (LL:10) + | Condition C3 --> (LL:17) + | Condition C4 --> (LL:28) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4 Result + | 1 { F, -, -, - = F } + | 2 { T, F, -, F = F } + | 3 { T, F, -, T = T } + | 4 { T, T, T, - = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,4) + | C3-Pair: not covered + | C4-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 75.00% + | + ------------------ + LL| 1| (Pat::B(_), Pat::C(_)) => say("matched B and C"), + ------------------ + | Branch (LL:10): [True: 1, False: 0] + | Branch (LL:21): [True: 1, False: 3] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:31) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:21) + | Condition C2 --> (LL:10) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| _ => say("matched others"), + LL| | } + LL| 4|} + LL| | + LL| 4|fn joint_or_patterns(pat: (Pat, Pat)) { + LL| 2| match pat { + LL| 2| (Pat::A(Some(_)) | Pat::C(_), Pat::B(_) | Pat::C(_)) => say("matched A::Some | C + B | C"), + ------------------ + | Branch (LL:10): [True: 1, False: 3] + | Branch (LL:17): [True: 1, False: 0] + | Branch (LL:28): [True: 1, False: 2] + | Branch (LL:39): [True: 2, False: 0] + | Branch (LL:51): [True: 0, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:61) + | + | Number of Conditions: 5 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:17) + | Condition C3 --> (LL:28) + | Condition C4 --> (LL:39) + | Condition C5 --> (LL:51) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4, C5 Result + | 1 { F, -, F, -, - = F } + | 2 { F, -, T, T, - = T } + | 3 { T, T, -, T, - = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: not covered + | C3-Pair: covered: (1,2) + | C4-Pair: not covered + | C5-Pair: not covered + | MC/DC Coverage for Decision: 40.00% + | + ------------------ + LL| 1| (Pat::B(_), Pat::C(_)) => say("matched B and C"), + ------------------ + | Branch (LL:10): [True: 2, False: 0] + | Branch (LL:21): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:31) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:21) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, F = F } + | 2 { T, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| _ => say("matched others"), + LL| | } + LL| | + LL| | // Try to use the matched value + LL| 2| match pat { + LL| 1| (Pat::A(Some(a)) | Pat::C(a), Pat::B(b) | Pat::C(b)) => { + ^0 + ------------------ + | Branch (LL:10): [True: 1, False: 3] + | Branch (LL:17): [True: 1, False: 0] + | Branch (LL:28): [True: 1, False: 2] + | Branch (LL:39): [True: 2, False: 0] + | Branch (LL:51): [True: 0, False: 0] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:61) + | + | Number of Conditions: 5 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:17) + | Condition C3 --> (LL:28) + | Condition C4 --> (LL:39) + | Condition C5 --> (LL:51) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3, C4, C5 Result + | 1 { F, -, F, -, - = F } + | 2 { F, -, T, T, - = T } + | 3 { T, T, -, T, - = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: not covered + | C3-Pair: covered: (1,2) + | C4-Pair: not covered + | C5-Pair: not covered + | MC/DC Coverage for Decision: 40.00% + | + ------------------ + LL| 2| say(&format!("matched A::Some | C ({a}) + B | C ({b})")) + LL| | } + LL| 1| (Pat::B(_), Pat::C(_)) => say("matched B and C"), + ------------------ + | Branch (LL:10): [True: 2, False: 0] + | Branch (LL:21): [True: 1, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:31) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:10) + | Condition C2 --> (LL:21) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, F = F } + | 2 { T, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| _ => say("matched others"), + LL| | } + LL| 4|} + LL| | + LL| 2|fn partial_matched(arr: &[i32]) { + LL| 2| match arr { + LL| 1| [selected] | [_, selected] => say(&format!("match arm 1: {selected}")), + ^0 + ------------------ + | Branch (LL:9): [True: 1, False: 1] + | Branch (LL:22): [True: 0, False: 1] + ------------------ + |---> MC/DC Decision Region (LL:9) to (LL:35) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:22) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, F = F } + | 2 { T, - = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| [_, _, selected, ..] => say(&format!("match arm 2: {selected}")), + ------------------ + | Branch (LL:9): [True: 1, False: 0] + ------------------ + LL| 0| _ => say("matched others"), + LL| | } + LL| 2|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | single_nested_pattern(Pat::A(Some(5))); + LL| | single_nested_pattern(Pat::B(5)); + LL| | + LL| | simple_or_pattern(Pat::A(None)); + LL| | simple_or_pattern(Pat::C(3)); + LL| | + LL| | simple_joint_pattern((Pat::A(Some(1)), Pat::B(2))); + LL| | simple_joint_pattern((Pat::A(Some(1)), Pat::C(2))); + LL| | simple_joint_pattern((Pat::B(1), Pat::B(2))); + LL| | + LL| | joint_pattern_with_or((Pat::A(Some(1)), Pat::B(2))); + LL| | joint_pattern_with_or((Pat::B(1), Pat::C(2))); + LL| | joint_pattern_with_or((Pat::B(1), Pat::B(2))); + LL| | joint_pattern_with_or((Pat::C(1), Pat::B(2))); + LL| | + LL| | joint_or_patterns((Pat::A(Some(1)), Pat::B(2))); + LL| | joint_or_patterns((Pat::B(1), Pat::C(2))); + LL| | joint_or_patterns((Pat::B(1), Pat::B(2))); + LL| | joint_or_patterns((Pat::C(1), Pat::B(2))); + LL| | + LL| | partial_matched(&[1]); + LL| | partial_matched(&[1, 2, 3]); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + diff --git a/tests/coverage/mcdc/match_pattern.rs b/tests/coverage/mcdc/match_pattern.rs new file mode 100644 index 0000000000000..30fd4335a63cf --- /dev/null +++ b/tests/coverage/mcdc/match_pattern.rs @@ -0,0 +1,100 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ min-llvm-version: 19 +//@ compile-flags: -Zcoverage-options=mcdc +//@ llvm-cov-flags: --show-branches=count --show-mcdc + +#[derive(Clone, Copy)] +enum Pat { + A(Option), + B(i32), + C(i32), +} + +fn single_nested_pattern(pat: Pat) { + match pat { + Pat::A(Some(_)) => say("matched A::Some"), + Pat::A(None) => say("matched A::None"), + Pat::B(_) => say("matched B"), + Pat::C(_) => say("matched C"), + } +} + +fn simple_or_pattern(pat: Pat) { + match pat { + Pat::B(_) | Pat::C(_) => say("matched B or C"), + _ => say("matched A"), + } +} + +fn simple_joint_pattern(pat: (Pat, Pat)) { + match pat { + (Pat::A(Some(_)), Pat::B(_)) => say("matched A::Some + B"), + (Pat::B(_), Pat::C(_)) => say("matched B and C"), + _ => say("matched others"), + } +} + +fn joint_pattern_with_or(pat: (Pat, Pat)) { + match pat { + (Pat::A(Some(_)) | Pat::C(_), Pat::B(_)) => say("matched A::Some | C + B"), + (Pat::B(_), Pat::C(_)) => say("matched B and C"), + _ => say("matched others"), + } +} + +fn joint_or_patterns(pat: (Pat, Pat)) { + match pat { + (Pat::A(Some(_)) | Pat::C(_), Pat::B(_) | Pat::C(_)) => say("matched A::Some | C + B | C"), + (Pat::B(_), Pat::C(_)) => say("matched B and C"), + _ => say("matched others"), + } + + // Try to use the matched value + match pat { + (Pat::A(Some(a)) | Pat::C(a), Pat::B(b) | Pat::C(b)) => { + say(&format!("matched A::Some | C ({a}) + B | C ({b})")) + } + (Pat::B(_), Pat::C(_)) => say("matched B and C"), + _ => say("matched others"), + } +} + +fn partial_matched(arr: &[i32]) { + match arr { + [selected] | [_, selected] => say(&format!("match arm 1: {selected}")), + [_, _, selected, ..] => say(&format!("match arm 2: {selected}")), + _ => say("matched others"), + } +} + +#[coverage(off)] +fn main() { + single_nested_pattern(Pat::A(Some(5))); + single_nested_pattern(Pat::B(5)); + + simple_or_pattern(Pat::A(None)); + simple_or_pattern(Pat::C(3)); + + simple_joint_pattern((Pat::A(Some(1)), Pat::B(2))); + simple_joint_pattern((Pat::A(Some(1)), Pat::C(2))); + simple_joint_pattern((Pat::B(1), Pat::B(2))); + + joint_pattern_with_or((Pat::A(Some(1)), Pat::B(2))); + joint_pattern_with_or((Pat::B(1), Pat::C(2))); + joint_pattern_with_or((Pat::B(1), Pat::B(2))); + joint_pattern_with_or((Pat::C(1), Pat::B(2))); + + joint_or_patterns((Pat::A(Some(1)), Pat::B(2))); + joint_or_patterns((Pat::B(1), Pat::C(2))); + joint_or_patterns((Pat::B(1), Pat::B(2))); + joint_or_patterns((Pat::C(1), Pat::B(2))); + + partial_matched(&[1]); + partial_matched(&[1, 2, 3]); +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +}