mirror of
https://github.com/rust-lang/rust
synced 2024-10-02 23:04:50 +00:00
Auto merge of #124255 - RenjiSann:renji/mcdc-nested-expressions, r=Zalathar
MCDC coverage: support nested decision coverage #123409 provided the initial MCDC coverage implementation. As referenced in #124144, it does not currently support "nested" decisions, like the following example : ```rust fn nested_if_in_condition(a: bool, b: bool, c: bool) { if a && if b || c { true } else { false } { say("yes"); } else { say("no"); } } ``` Note that there is an if-expression (`if b || c ...`) embedded inside a boolean expression in the decision of an outer if-expression. This PR proposes a workaround for this cases, by introducing a Decision context stack, and by handing several `temporary condition bitmaps` instead of just one. When instrumenting boolean expressions, if the current node is a leaf condition (i.e. not a `||`/`&&` logical operator nor a `!` not operator), we insert a new decision context, such that if there are more boolean expressions inside the condition, they are handled as separate expressions. On the codegen LLVM side, we allocate as many `temp_cond_bitmap`s as necessary to handle the maximum encountered decision depth.
This commit is contained in:
commit
7a58674259
|
@ -27,6 +27,7 @@
|
||||||
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::ffi::CString;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
@ -1709,7 +1710,8 @@ pub(crate) fn mcdc_parameters(
|
||||||
fn_name: &'ll Value,
|
fn_name: &'ll Value,
|
||||||
hash: &'ll Value,
|
hash: &'ll Value,
|
||||||
bitmap_bytes: &'ll Value,
|
bitmap_bytes: &'ll Value,
|
||||||
) -> &'ll Value {
|
max_decision_depth: u32,
|
||||||
|
) -> Vec<&'ll Value> {
|
||||||
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
|
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
|
||||||
|
|
||||||
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
||||||
|
@ -1722,6 +1724,8 @@ pub(crate) fn mcdc_parameters(
|
||||||
let args = &[fn_name, hash, bitmap_bytes];
|
let args = &[fn_name, hash, bitmap_bytes];
|
||||||
let args = self.check_call("call", llty, llfn, args);
|
let args = self.check_call("call", llty, llfn, args);
|
||||||
|
|
||||||
|
let mut cond_bitmaps = vec![];
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = llvm::LLVMRustBuildCall(
|
let _ = llvm::LLVMRustBuildCall(
|
||||||
self.llbuilder,
|
self.llbuilder,
|
||||||
|
@ -1733,18 +1737,23 @@ pub(crate) fn mcdc_parameters(
|
||||||
0 as c_uint,
|
0 as c_uint,
|
||||||
);
|
);
|
||||||
// Create condition bitmap named `mcdc.addr`.
|
// Create condition bitmap named `mcdc.addr`.
|
||||||
|
for i in 0..=max_decision_depth {
|
||||||
let mut bx = Builder::with_cx(self.cx);
|
let mut bx = Builder::with_cx(self.cx);
|
||||||
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
|
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
|
||||||
|
|
||||||
|
let name = CString::new(format!("mcdc.addr.{i}")).unwrap();
|
||||||
let cond_bitmap = {
|
let cond_bitmap = {
|
||||||
let alloca =
|
let alloca =
|
||||||
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), c"mcdc.addr".as_ptr());
|
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), name.as_ptr());
|
||||||
llvm::LLVMSetAlignment(alloca, 4);
|
llvm::LLVMSetAlignment(alloca, 4);
|
||||||
alloca
|
alloca
|
||||||
};
|
};
|
||||||
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
|
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
|
||||||
cond_bitmap
|
cond_bitmaps.push(cond_bitmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cond_bitmaps
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn mcdc_tvbitmap_update(
|
pub(crate) fn mcdc_tvbitmap_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub struct CrateCoverageContext<'ll, 'tcx> {
|
||||||
pub(crate) function_coverage_map:
|
pub(crate) function_coverage_map:
|
||||||
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
|
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
|
||||||
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||||
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||||
|
@ -49,9 +49,20 @@ pub fn take_function_coverage_map(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap.
|
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap.
|
||||||
/// This value is named `mcdc.addr` (same as clang) and is a 32-bit integer.
|
/// In order to handle nested decisions, several condition bitmaps can be
|
||||||
fn try_get_mcdc_condition_bitmap(&self, instance: &Instance<'tcx>) -> Option<&'ll llvm::Value> {
|
/// allocated for a function body.
|
||||||
self.mcdc_condition_bitmap_map.borrow().get(instance).copied()
|
/// These values are named `mcdc.addr.{i}` and are a 32-bit integers.
|
||||||
|
/// They respectively hold the condition bitmaps for decisions with a depth of `i`.
|
||||||
|
fn try_get_mcdc_condition_bitmap(
|
||||||
|
&self,
|
||||||
|
instance: &Instance<'tcx>,
|
||||||
|
decision_depth: u16,
|
||||||
|
) -> Option<&'ll llvm::Value> {
|
||||||
|
self.mcdc_condition_bitmap_map
|
||||||
|
.borrow()
|
||||||
|
.get(instance)
|
||||||
|
.and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
|
||||||
|
.copied() // Dereference Option<&&Value> to Option<&Value>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +154,7 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
||||||
CoverageKind::ExpressionUsed { id } => {
|
CoverageKind::ExpressionUsed { id } => {
|
||||||
func_coverage.mark_expression_id_seen(id);
|
func_coverage.mark_expression_id_seen(id);
|
||||||
}
|
}
|
||||||
CoverageKind::CondBitmapUpdate { id, value, .. } => {
|
CoverageKind::CondBitmapUpdate { id, value, decision_depth } => {
|
||||||
drop(coverage_map);
|
drop(coverage_map);
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
id.as_u32(),
|
id.as_u32(),
|
||||||
|
@ -151,7 +162,7 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
||||||
"ConditionId of evaluated conditions should never be zero"
|
"ConditionId of evaluated conditions should never be zero"
|
||||||
);
|
);
|
||||||
let cond_bitmap = coverage_context
|
let cond_bitmap = coverage_context
|
||||||
.try_get_mcdc_condition_bitmap(&instance)
|
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
|
||||||
.expect("mcdc cond bitmap should have been allocated for updating");
|
.expect("mcdc cond bitmap should have been allocated for updating");
|
||||||
let cond_loc = bx.const_i32(id.as_u32() as i32 - 1);
|
let cond_loc = bx.const_i32(id.as_u32() as i32 - 1);
|
||||||
let bool_value = bx.const_bool(value);
|
let bool_value = bx.const_bool(value);
|
||||||
|
@ -159,10 +170,10 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
||||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||||
bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value);
|
bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value);
|
||||||
}
|
}
|
||||||
CoverageKind::TestVectorBitmapUpdate { bitmap_idx } => {
|
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
|
||||||
drop(coverage_map);
|
drop(coverage_map);
|
||||||
let cond_bitmap = coverage_context
|
let cond_bitmap = coverage_context
|
||||||
.try_get_mcdc_condition_bitmap(&instance)
|
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
|
||||||
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
|
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
|
||||||
let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
|
let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
|
||||||
assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
|
assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
|
||||||
|
@ -195,7 +206,8 @@ fn ensure_mcdc_parameters<'ll, 'tcx>(
|
||||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||||
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
|
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
|
||||||
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes);
|
let max_decision_depth = function_coverage_info.mcdc_max_decision_depth;
|
||||||
|
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes, max_decision_depth as u32);
|
||||||
bx.coverage_context()
|
bx.coverage_context()
|
||||||
.expect("already checked above")
|
.expect("already checked above")
|
||||||
.mcdc_condition_bitmap_map
|
.mcdc_condition_bitmap_map
|
||||||
|
|
|
@ -132,7 +132,7 @@ pub enum CoverageKind {
|
||||||
///
|
///
|
||||||
/// If this statement does not survive MIR optimizations, the condition would never be
|
/// If this statement does not survive MIR optimizations, the condition would never be
|
||||||
/// taken as evaluated.
|
/// taken as evaluated.
|
||||||
CondBitmapUpdate { id: ConditionId, value: bool },
|
CondBitmapUpdate { id: ConditionId, value: bool, decision_depth: u16 },
|
||||||
|
|
||||||
/// Marks the point in MIR control flow represented by a evaluated decision.
|
/// Marks the point in MIR control flow represented by a evaluated decision.
|
||||||
///
|
///
|
||||||
|
@ -140,7 +140,7 @@ pub enum CoverageKind {
|
||||||
///
|
///
|
||||||
/// If this statement does not survive MIR optimizations, the decision would never be
|
/// If this statement does not survive MIR optimizations, the decision would never be
|
||||||
/// taken as evaluated.
|
/// taken as evaluated.
|
||||||
TestVectorBitmapUpdate { bitmap_idx: u32 },
|
TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for CoverageKind {
|
impl Debug for CoverageKind {
|
||||||
|
@ -151,11 +151,17 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||||
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
|
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
|
||||||
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
|
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
|
||||||
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
|
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
|
||||||
CondBitmapUpdate { id, value } => {
|
CondBitmapUpdate { id, value, decision_depth } => {
|
||||||
write!(fmt, "CondBitmapUpdate({:?}, {:?})", id.index(), value)
|
write!(
|
||||||
|
fmt,
|
||||||
|
"CondBitmapUpdate({:?}, {:?}, depth={:?})",
|
||||||
|
id.index(),
|
||||||
|
value,
|
||||||
|
decision_depth
|
||||||
|
)
|
||||||
}
|
}
|
||||||
TestVectorBitmapUpdate { bitmap_idx } => {
|
TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
|
||||||
write!(fmt, "TestVectorUpdate({:?})", bitmap_idx)
|
write!(fmt, "TestVectorUpdate({:?}, depth={:?})", bitmap_idx, decision_depth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,6 +275,9 @@ pub struct FunctionCoverageInfo {
|
||||||
pub mcdc_bitmap_bytes: u32,
|
pub mcdc_bitmap_bytes: u32,
|
||||||
pub expressions: IndexVec<ExpressionId, Expression>,
|
pub expressions: IndexVec<ExpressionId, Expression>,
|
||||||
pub mappings: Vec<Mapping>,
|
pub mappings: Vec<Mapping>,
|
||||||
|
/// The depth of the deepest decision is used to know how many
|
||||||
|
/// temp condbitmaps should be allocated for the function.
|
||||||
|
pub mcdc_max_decision_depth: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
|
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
|
||||||
|
@ -319,6 +328,7 @@ pub struct MCDCBranchSpan {
|
||||||
pub condition_info: Option<ConditionInfo>,
|
pub condition_info: Option<ConditionInfo>,
|
||||||
pub true_marker: BlockMarkerId,
|
pub true_marker: BlockMarkerId,
|
||||||
pub false_marker: BlockMarkerId,
|
pub false_marker: BlockMarkerId,
|
||||||
|
pub decision_depth: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -334,4 +344,5 @@ pub struct MCDCDecisionSpan {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub conditions_num: usize,
|
pub conditions_num: usize,
|
||||||
pub end_markers: Vec<BlockMarkerId>,
|
pub end_markers: Vec<BlockMarkerId>,
|
||||||
|
pub decision_depth: u16,
|
||||||
}
|
}
|
||||||
|
|
|
@ -496,20 +496,27 @@ fn write_coverage_branch_info(
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
|
for coverage::MCDCBranchSpan {
|
||||||
mcdc_branch_spans
|
span,
|
||||||
|
condition_info,
|
||||||
|
true_marker,
|
||||||
|
false_marker,
|
||||||
|
decision_depth,
|
||||||
|
} in mcdc_branch_spans
|
||||||
{
|
{
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
w,
|
||||||
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
|
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}",
|
||||||
condition_info.map(|info| info.condition_id)
|
condition_info.map(|info| info.condition_id)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for coverage::MCDCDecisionSpan { span, conditions_num, end_markers } in mcdc_decision_spans {
|
for coverage::MCDCDecisionSpan { span, conditions_num, end_markers, decision_depth } in
|
||||||
|
mcdc_decision_spans
|
||||||
|
{
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
w,
|
||||||
"{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?} }} => {span:?}"
|
"{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,10 +101,14 @@ fn fetch_mcdc_condition_info(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
true_marker: BlockMarkerId,
|
true_marker: BlockMarkerId,
|
||||||
false_marker: BlockMarkerId,
|
false_marker: BlockMarkerId,
|
||||||
) -> Option<ConditionInfo> {
|
) -> Option<(u16, ConditionInfo)> {
|
||||||
let mcdc_state = self.mcdc_state.as_mut()?;
|
let mcdc_state = self.mcdc_state.as_mut()?;
|
||||||
|
let decision_depth = mcdc_state.decision_depth();
|
||||||
let (mut condition_info, decision_result) =
|
let (mut condition_info, decision_result) =
|
||||||
mcdc_state.take_condition(true_marker, false_marker);
|
mcdc_state.take_condition(true_marker, false_marker);
|
||||||
|
// take_condition() returns Some for decision_result when the decision stack
|
||||||
|
// is empty, i.e. when all the conditions of the decision were instrumented,
|
||||||
|
// and the decision is "complete".
|
||||||
if let Some(decision) = decision_result {
|
if let Some(decision) = decision_result {
|
||||||
match decision.conditions_num {
|
match decision.conditions_num {
|
||||||
0 => {
|
0 => {
|
||||||
|
@ -131,7 +135,7 @@ fn fetch_mcdc_condition_info(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
condition_info
|
condition_info.map(|cond_info| (decision_depth, cond_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_two_way_branch<'tcx>(
|
fn add_two_way_branch<'tcx>(
|
||||||
|
@ -199,17 +203,32 @@ pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
|
||||||
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
|
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
|
||||||
const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
|
const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
|
||||||
|
|
||||||
struct MCDCState {
|
#[derive(Default)]
|
||||||
|
struct MCDCDecisionCtx {
|
||||||
/// To construct condition evaluation tree.
|
/// To construct condition evaluation tree.
|
||||||
decision_stack: VecDeque<ConditionInfo>,
|
decision_stack: VecDeque<ConditionInfo>,
|
||||||
processing_decision: Option<MCDCDecisionSpan>,
|
processing_decision: Option<MCDCDecisionSpan>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MCDCState {
|
||||||
|
decision_ctx_stack: Vec<MCDCDecisionCtx>,
|
||||||
|
}
|
||||||
|
|
||||||
impl MCDCState {
|
impl MCDCState {
|
||||||
fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
|
fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
|
||||||
tcx.sess
|
tcx.sess
|
||||||
.instrument_coverage_mcdc()
|
.instrument_coverage_mcdc()
|
||||||
.then(|| Self { decision_stack: VecDeque::new(), processing_decision: None })
|
.then(|| Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
|
||||||
|
/// as it is very unlikely that the depth ever reaches 2^16.
|
||||||
|
#[inline]
|
||||||
|
fn decision_depth(&self) -> u16 {
|
||||||
|
u16::try_from(
|
||||||
|
self.decision_ctx_stack.len().checked_sub(1).expect("Unexpected empty decision stack"),
|
||||||
|
)
|
||||||
|
.expect("decision depth did not fit in u16, this is likely to be an instrumentation error")
|
||||||
}
|
}
|
||||||
|
|
||||||
// At first we assign ConditionIds for each sub expression.
|
// At first we assign ConditionIds for each sub expression.
|
||||||
|
@ -253,19 +272,23 @@ fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
|
||||||
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
|
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
|
||||||
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
|
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
|
||||||
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
|
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
|
||||||
let decision = match self.processing_decision.as_mut() {
|
let decision_depth = self.decision_depth();
|
||||||
|
let decision_ctx =
|
||||||
|
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
|
||||||
|
let decision = match decision_ctx.processing_decision.as_mut() {
|
||||||
Some(decision) => {
|
Some(decision) => {
|
||||||
decision.span = decision.span.to(span);
|
decision.span = decision.span.to(span);
|
||||||
decision
|
decision
|
||||||
}
|
}
|
||||||
None => self.processing_decision.insert(MCDCDecisionSpan {
|
None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
|
||||||
span,
|
span,
|
||||||
conditions_num: 0,
|
conditions_num: 0,
|
||||||
end_markers: vec![],
|
end_markers: vec![],
|
||||||
|
decision_depth,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
|
let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
|
||||||
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
|
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
|
||||||
decision.conditions_num += 1;
|
decision.conditions_num += 1;
|
||||||
ConditionId::from(decision.conditions_num)
|
ConditionId::from(decision.conditions_num)
|
||||||
|
@ -305,8 +328,8 @@ fn record_conditions(&mut self, op: LogicalOp, span: Span) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// We visit expressions tree in pre-order, so place the left-hand side on the top.
|
// We visit expressions tree in pre-order, so place the left-hand side on the top.
|
||||||
self.decision_stack.push_back(rhs);
|
decision_ctx.decision_stack.push_back(rhs);
|
||||||
self.decision_stack.push_back(lhs);
|
decision_ctx.decision_stack.push_back(lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_condition(
|
fn take_condition(
|
||||||
|
@ -314,10 +337,12 @@ fn take_condition(
|
||||||
true_marker: BlockMarkerId,
|
true_marker: BlockMarkerId,
|
||||||
false_marker: BlockMarkerId,
|
false_marker: BlockMarkerId,
|
||||||
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
|
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
|
||||||
let Some(condition_info) = self.decision_stack.pop_back() else {
|
let decision_ctx =
|
||||||
|
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
|
||||||
|
let Some(condition_info) = decision_ctx.decision_stack.pop_back() else {
|
||||||
return (None, None);
|
return (None, None);
|
||||||
};
|
};
|
||||||
let Some(decision) = self.processing_decision.as_mut() else {
|
let Some(decision) = decision_ctx.processing_decision.as_mut() else {
|
||||||
bug!("Processing decision should have been created before any conditions are taken");
|
bug!("Processing decision should have been created before any conditions are taken");
|
||||||
};
|
};
|
||||||
if condition_info.true_next_id == ConditionId::NONE {
|
if condition_info.true_next_id == ConditionId::NONE {
|
||||||
|
@ -327,8 +352,8 @@ fn take_condition(
|
||||||
decision.end_markers.push(false_marker);
|
decision.end_markers.push(false_marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.decision_stack.is_empty() {
|
if decision_ctx.decision_stack.is_empty() {
|
||||||
(Some(condition_info), self.processing_decision.take())
|
(Some(condition_info), decision_ctx.processing_decision.take())
|
||||||
} else {
|
} else {
|
||||||
(Some(condition_info), None)
|
(Some(condition_info), None)
|
||||||
}
|
}
|
||||||
|
@ -364,13 +389,17 @@ pub(crate) fn visit_coverage_branch_condition(
|
||||||
|block| branch_info.inject_block_marker(&mut self.cfg, source_info, block);
|
|block| branch_info.inject_block_marker(&mut self.cfg, source_info, block);
|
||||||
let true_marker = inject_block_marker(then_block);
|
let true_marker = inject_block_marker(then_block);
|
||||||
let false_marker = inject_block_marker(else_block);
|
let false_marker = inject_block_marker(else_block);
|
||||||
let condition_info =
|
let (decision_depth, condition_info) = branch_info
|
||||||
branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker);
|
.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker)
|
||||||
|
.map_or((0, None), |(decision_depth, condition_info)| {
|
||||||
|
(decision_depth, Some(condition_info))
|
||||||
|
});
|
||||||
branch_info.mcdc_branch_spans.push(MCDCBranchSpan {
|
branch_info.mcdc_branch_spans.push(MCDCBranchSpan {
|
||||||
span: source_info.span,
|
span: source_info.span,
|
||||||
condition_info,
|
condition_info,
|
||||||
true_marker,
|
true_marker,
|
||||||
false_marker,
|
false_marker,
|
||||||
|
decision_depth,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -385,4 +414,20 @@ pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp,
|
||||||
mcdc_state.record_conditions(logical_op, span);
|
mcdc_state.record_conditions(logical_op, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) {
|
||||||
|
if let Some(branch_info) = self.coverage_branch_info.as_mut()
|
||||||
|
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
|
||||||
|
{
|
||||||
|
mcdc_state.decision_ctx_stack.push(MCDCDecisionCtx::default());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
|
||||||
|
if let Some(branch_info) = self.coverage_branch_info.as_mut()
|
||||||
|
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
|
||||||
|
{
|
||||||
|
mcdc_state.decision_ctx_stack.pop().expect("Unexpected empty decision stack");
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,8 +148,14 @@ fn then_else_break_inner(
|
||||||
let mut block = block;
|
let mut block = block;
|
||||||
let temp_scope = args.temp_scope_override.unwrap_or_else(|| this.local_scope());
|
let temp_scope = args.temp_scope_override.unwrap_or_else(|| this.local_scope());
|
||||||
let mutability = Mutability::Mut;
|
let mutability = Mutability::Mut;
|
||||||
|
|
||||||
|
// Increment the decision depth, in case we encounter boolean expressions
|
||||||
|
// further down.
|
||||||
|
this.mcdc_increment_depth_if_enabled();
|
||||||
let place =
|
let place =
|
||||||
unpack!(block = this.as_temp(block, Some(temp_scope), expr_id, mutability));
|
unpack!(block = this.as_temp(block, Some(temp_scope), expr_id, mutability));
|
||||||
|
this.mcdc_decrement_depth_if_enabled();
|
||||||
|
|
||||||
let operand = Operand::Move(Place::from(place));
|
let operand = Operand::Move(Place::from(place));
|
||||||
|
|
||||||
let then_block = this.cfg.start_new_block();
|
let then_block = this.cfg.start_new_block();
|
||||||
|
|
|
@ -102,12 +102,23 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
|
||||||
|
|
||||||
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
|
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
|
||||||
|
|
||||||
|
let mcdc_max_decision_depth = coverage_spans
|
||||||
|
.mappings
|
||||||
|
.iter()
|
||||||
|
.filter_map(|bcb_mapping| match bcb_mapping.kind {
|
||||||
|
BcbMappingKind::MCDCDecision { decision_depth, .. } => Some(decision_depth),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
||||||
function_source_hash: hir_info.function_source_hash,
|
function_source_hash: hir_info.function_source_hash,
|
||||||
num_counters: coverage_counters.num_counters(),
|
num_counters: coverage_counters.num_counters(),
|
||||||
mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
|
mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
|
||||||
expressions: coverage_counters.into_expressions(),
|
expressions: coverage_counters.into_expressions(),
|
||||||
mappings,
|
mappings,
|
||||||
|
mcdc_max_decision_depth,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,16 +156,17 @@ fn create_mappings<'tcx>(
|
||||||
|BcbMapping { kind: bcb_mapping_kind, span }| {
|
|BcbMapping { kind: bcb_mapping_kind, span }| {
|
||||||
let kind = match *bcb_mapping_kind {
|
let kind = match *bcb_mapping_kind {
|
||||||
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
|
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
|
||||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => {
|
BcbMappingKind::MCDCBranch {
|
||||||
MappingKind::Branch {
|
true_bcb, false_bcb, condition_info: None, ..
|
||||||
|
} => MappingKind::Branch {
|
||||||
true_term: term_for_bcb(true_bcb),
|
true_term: term_for_bcb(true_bcb),
|
||||||
false_term: term_for_bcb(false_bcb),
|
false_term: term_for_bcb(false_bcb),
|
||||||
}
|
},
|
||||||
}
|
|
||||||
BcbMappingKind::MCDCBranch {
|
BcbMappingKind::MCDCBranch {
|
||||||
true_bcb,
|
true_bcb,
|
||||||
false_bcb,
|
false_bcb,
|
||||||
condition_info: Some(mcdc_params),
|
condition_info: Some(mcdc_params),
|
||||||
|
..
|
||||||
} => MappingKind::MCDCBranch {
|
} => MappingKind::MCDCBranch {
|
||||||
true_term: term_for_bcb(true_bcb),
|
true_term: term_for_bcb(true_bcb),
|
||||||
false_term: term_for_bcb(false_bcb),
|
false_term: term_for_bcb(false_bcb),
|
||||||
|
@ -246,24 +258,28 @@ fn inject_mcdc_statements<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject test vector update first because `inject_statement` always insert new statement at head.
|
// Inject test vector update first because `inject_statement` always insert new statement at head.
|
||||||
for (end_bcbs, bitmap_idx) in
|
for (end_bcbs, bitmap_idx, decision_depth) in
|
||||||
coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind {
|
coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind {
|
||||||
BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => {
|
BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, decision_depth, .. } => {
|
||||||
Some((end_bcbs, *bitmap_idx))
|
Some((end_bcbs, *bitmap_idx, *decision_depth))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
for end in end_bcbs {
|
for end in end_bcbs {
|
||||||
let end_bb = basic_coverage_blocks[*end].leader_bb();
|
let end_bb = basic_coverage_blocks[*end].leader_bb();
|
||||||
inject_statement(mir_body, CoverageKind::TestVectorBitmapUpdate { bitmap_idx }, end_bb);
|
inject_statement(
|
||||||
|
mir_body,
|
||||||
|
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth },
|
||||||
|
end_bb,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (true_bcb, false_bcb, condition_id) in
|
for (true_bcb, false_bcb, condition_id, decision_depth) in
|
||||||
coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind {
|
coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind {
|
||||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => {
|
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info, decision_depth } => {
|
||||||
Some((true_bcb, false_bcb, condition_info?.condition_id))
|
Some((true_bcb, false_bcb, condition_info?.condition_id, decision_depth))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
@ -271,13 +287,13 @@ fn inject_mcdc_statements<'tcx>(
|
||||||
let true_bb = basic_coverage_blocks[true_bcb].leader_bb();
|
let true_bb = basic_coverage_blocks[true_bcb].leader_bb();
|
||||||
inject_statement(
|
inject_statement(
|
||||||
mir_body,
|
mir_body,
|
||||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: true },
|
CoverageKind::CondBitmapUpdate { id: condition_id, value: true, decision_depth },
|
||||||
true_bb,
|
true_bb,
|
||||||
);
|
);
|
||||||
let false_bb = basic_coverage_blocks[false_bcb].leader_bb();
|
let false_bb = basic_coverage_blocks[false_bcb].leader_bb();
|
||||||
inject_statement(
|
inject_statement(
|
||||||
mir_body,
|
mir_body,
|
||||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: false },
|
CoverageKind::CondBitmapUpdate { id: condition_id, value: false, decision_depth },
|
||||||
false_bb,
|
false_bb,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,15 @@ pub(super) enum BcbMappingKind {
|
||||||
/// If `None`, this actually represents a normal branch mapping inserted
|
/// If `None`, this actually represents a normal branch mapping inserted
|
||||||
/// for code that was too complex for MC/DC.
|
/// for code that was too complex for MC/DC.
|
||||||
condition_info: Option<ConditionInfo>,
|
condition_info: Option<ConditionInfo>,
|
||||||
|
decision_depth: u16,
|
||||||
},
|
},
|
||||||
/// Associates a mcdc decision with its join BCB.
|
/// Associates a mcdc decision with its join BCB.
|
||||||
MCDCDecision { end_bcbs: BTreeSet<BasicCoverageBlock>, bitmap_idx: u32, conditions_num: u16 },
|
MCDCDecision {
|
||||||
|
end_bcbs: BTreeSet<BasicCoverageBlock>,
|
||||||
|
bitmap_idx: u32,
|
||||||
|
conditions_num: u16,
|
||||||
|
decision_depth: u16,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -453,14 +453,24 @@ pub(super) fn extract_mcdc_mappings(
|
||||||
Some((span, true_bcb, false_bcb))
|
Some((span, true_bcb, false_bcb))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mcdc_branch_filter_map =
|
let mcdc_branch_filter_map = |&MCDCBranchSpan {
|
||||||
|&MCDCBranchSpan { span: raw_span, true_marker, false_marker, condition_info }| {
|
span: raw_span,
|
||||||
check_branch_bcb(raw_span, true_marker, false_marker).map(
|
true_marker,
|
||||||
|(span, true_bcb, false_bcb)| BcbMapping {
|
false_marker,
|
||||||
kind: BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info },
|
condition_info,
|
||||||
span,
|
decision_depth,
|
||||||
|
}| {
|
||||||
|
check_branch_bcb(raw_span, true_marker, false_marker).map(|(span, true_bcb, false_bcb)| {
|
||||||
|
BcbMapping {
|
||||||
|
kind: BcbMappingKind::MCDCBranch {
|
||||||
|
true_bcb,
|
||||||
|
false_bcb,
|
||||||
|
condition_info,
|
||||||
|
decision_depth,
|
||||||
},
|
},
|
||||||
)
|
span,
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut next_bitmap_idx = 0;
|
let mut next_bitmap_idx = 0;
|
||||||
|
@ -482,6 +492,7 @@ pub(super) fn extract_mcdc_mappings(
|
||||||
end_bcbs,
|
end_bcbs,
|
||||||
bitmap_idx,
|
bitmap_idx,
|
||||||
conditions_num: decision.conditions_num as u16,
|
conditions_num: decision.conditions_num as u16,
|
||||||
|
decision_depth: decision.decision_depth,
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
|
|
201
tests/coverage/mcdc_nested_if.cov-map
Normal file
201
tests/coverage/mcdc_nested_if.cov-map
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
Function name: mcdc_nested_if::doubly_nested_if_in_condition
|
||||||
|
Raw bytes (168): 0x[01, 01, 0e, 01, 05, 05, 11, 05, 11, 26, 19, 05, 11, 19, 1d, 19, 1d, 1d, 22, 26, 19, 05, 11, 11, 15, 09, 02, 0d, 37, 09, 02, 14, 01, 0f, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4e, 05, 00, 10, 00, 11, 28, 01, 02, 00, 10, 00, 36, 30, 11, 26, 01, 00, 02, 00, 10, 00, 11, 30, 15, 21, 02, 00, 00, 00, 15, 00, 36, 26, 00, 18, 00, 19, 28, 00, 02, 00, 18, 00, 1e, 30, 19, 22, 01, 02, 00, 00, 18, 00, 19, 19, 00, 1d, 00, 1e, 30, 1a, 1d, 02, 00, 00, 00, 1d, 00, 1e, 1a, 00, 21, 00, 25, 1f, 00, 2f, 00, 34, 2b, 00, 39, 00, 3e, 21, 00, 48, 00, 4c, 0d, 00, 4f, 02, 06, 37, 02, 0c, 02, 06, 33, 03, 01, 00, 02]
|
||||||
|
Number of files: 1
|
||||||
|
- file 0 => global file 1
|
||||||
|
Number of expressions: 14
|
||||||
|
- 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 = Expression(9, Sub), rhs = Counter(6)
|
||||||
|
- expression 4 operands: lhs = Counter(1), rhs = Counter(4)
|
||||||
|
- expression 5 operands: lhs = Counter(6), rhs = Counter(7)
|
||||||
|
- expression 6 operands: lhs = Counter(6), rhs = Counter(7)
|
||||||
|
- expression 7 operands: lhs = Counter(7), rhs = Expression(8, Sub)
|
||||||
|
- expression 8 operands: lhs = Expression(9, Sub), rhs = Counter(6)
|
||||||
|
- expression 9 operands: lhs = Counter(1), rhs = Counter(4)
|
||||||
|
- expression 10 operands: lhs = Counter(4), rhs = Counter(5)
|
||||||
|
- expression 11 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||||
|
- expression 12 operands: lhs = Counter(3), rhs = Expression(13, Add)
|
||||||
|
- expression 13 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||||
|
Number of file 0 mappings: 20
|
||||||
|
- Code(Counter(0)) at (prev + 15, 1) to (start + 1, 9)
|
||||||
|
- MCDCDecision { bitmap_idx: 2, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 78)
|
||||||
|
- 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 + 0, 78)
|
||||||
|
true = c3
|
||||||
|
false = c2
|
||||||
|
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
- MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 0, 16) to (start + 0, 54)
|
||||||
|
- MCDCBranch { true: Counter(4), false: Expression(9, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
true = c4
|
||||||
|
false = (c1 - c4)
|
||||||
|
- MCDCBranch { true: Counter(5), false: Counter(8), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 54)
|
||||||
|
true = c5
|
||||||
|
false = c8
|
||||||
|
- Code(Expression(9, Sub)) at (prev + 0, 24) to (start + 0, 25)
|
||||||
|
= (c1 - c4)
|
||||||
|
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 24) to (start + 0, 30)
|
||||||
|
- MCDCBranch { true: Counter(6), false: Expression(8, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 24) to (start + 0, 25)
|
||||||
|
true = c6
|
||||||
|
false = ((c1 - c4) - c6)
|
||||||
|
- Code(Counter(6)) at (prev + 0, 29) to (start + 0, 30)
|
||||||
|
- MCDCBranch { true: Expression(6, Sub), false: Counter(7), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 29) to (start + 0, 30)
|
||||||
|
true = (c6 - c7)
|
||||||
|
false = c7
|
||||||
|
- Code(Expression(6, Sub)) at (prev + 0, 33) to (start + 0, 37)
|
||||||
|
= (c6 - c7)
|
||||||
|
- Code(Expression(7, Add)) at (prev + 0, 47) to (start + 0, 52)
|
||||||
|
= (c7 + ((c1 - c4) - c6))
|
||||||
|
- Code(Expression(10, Add)) at (prev + 0, 57) to (start + 0, 62)
|
||||||
|
= (c4 + c5)
|
||||||
|
- Code(Counter(8)) at (prev + 0, 72) to (start + 0, 76)
|
||||||
|
- Code(Counter(3)) at (prev + 0, 79) to (start + 2, 6)
|
||||||
|
- Code(Expression(13, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||||
|
= (c2 + (c0 - c1))
|
||||||
|
- Code(Expression(12, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||||
|
= (c3 + (c2 + (c0 - c1)))
|
||||||
|
|
||||||
|
Function name: mcdc_nested_if::nested_if_in_condition
|
||||||
|
Raw bytes (120): 0x[01, 01, 0b, 01, 05, 05, 11, 05, 11, 1e, 15, 05, 11, 11, 15, 1e, 15, 05, 11, 09, 02, 0d, 2b, 09, 02, 0e, 01, 07, 01, 01, 09, 28, 01, 02, 01, 08, 00, 2e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 2e, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 1e, 01, 00, 02, 00, 10, 00, 11, 1e, 00, 15, 00, 16, 30, 15, 1a, 02, 00, 00, 00, 15, 00, 16, 17, 00, 19, 00, 1d, 1a, 00, 27, 00, 2c, 0d, 00, 2f, 02, 06, 2b, 02, 0c, 02, 06, 27, 03, 01, 00, 02]
|
||||||
|
Number of files: 1
|
||||||
|
- file 0 => global file 1
|
||||||
|
Number of expressions: 11
|
||||||
|
- 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 = Expression(7, Sub), rhs = Counter(5)
|
||||||
|
- expression 4 operands: lhs = Counter(1), rhs = Counter(4)
|
||||||
|
- expression 5 operands: lhs = Counter(4), rhs = Counter(5)
|
||||||
|
- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(5)
|
||||||
|
- expression 7 operands: lhs = Counter(1), rhs = Counter(4)
|
||||||
|
- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||||
|
- expression 9 operands: lhs = Counter(3), rhs = Expression(10, Add)
|
||||||
|
- expression 10 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||||
|
Number of file 0 mappings: 14
|
||||||
|
- Code(Counter(0)) at (prev + 7, 1) to (start + 1, 9)
|
||||||
|
- MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 46)
|
||||||
|
- 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 + 0, 46)
|
||||||
|
true = c3
|
||||||
|
false = c2
|
||||||
|
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 16) to (start + 0, 22)
|
||||||
|
- MCDCBranch { true: Counter(4), false: Expression(7, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
true = c4
|
||||||
|
false = (c1 - c4)
|
||||||
|
- Code(Expression(7, Sub)) at (prev + 0, 21) to (start + 0, 22)
|
||||||
|
= (c1 - c4)
|
||||||
|
- MCDCBranch { true: Counter(5), false: Expression(6, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 22)
|
||||||
|
true = c5
|
||||||
|
false = ((c1 - c4) - c5)
|
||||||
|
- Code(Expression(5, Add)) at (prev + 0, 25) to (start + 0, 29)
|
||||||
|
= (c4 + c5)
|
||||||
|
- Code(Expression(6, Sub)) at (prev + 0, 39) to (start + 0, 44)
|
||||||
|
= ((c1 - c4) - c5)
|
||||||
|
- Code(Counter(3)) at (prev + 0, 47) to (start + 2, 6)
|
||||||
|
- Code(Expression(10, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||||
|
= (c2 + (c0 - c1))
|
||||||
|
- Code(Expression(9, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||||
|
= (c3 + (c2 + (c0 - c1)))
|
||||||
|
|
||||||
|
Function name: mcdc_nested_if::nested_in_then_block_in_condition
|
||||||
|
Raw bytes (176): 0x[01, 01, 12, 01, 05, 05, 11, 05, 11, 3a, 15, 05, 11, 11, 15, 33, 19, 11, 15, 19, 1d, 19, 1d, 1d, 2e, 33, 19, 11, 15, 3a, 15, 05, 11, 09, 02, 0d, 47, 09, 02, 14, 01, 22, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4b, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4b, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 3a, 01, 00, 02, 00, 10, 00, 11, 3a, 00, 15, 00, 16, 30, 15, 36, 02, 00, 00, 00, 15, 00, 16, 33, 00, 1c, 00, 1d, 28, 01, 02, 00, 1c, 00, 22, 30, 19, 2e, 01, 02, 00, 00, 1c, 00, 1d, 19, 00, 21, 00, 22, 30, 26, 1d, 02, 00, 00, 00, 21, 00, 22, 26, 00, 25, 00, 29, 2b, 00, 33, 00, 38, 36, 00, 44, 00, 49, 0d, 00, 4c, 02, 06, 47, 02, 0c, 02, 06, 43, 03, 01, 00, 02]
|
||||||
|
Number of files: 1
|
||||||
|
- file 0 => global file 1
|
||||||
|
Number of expressions: 18
|
||||||
|
- 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 = Expression(14, Sub), rhs = Counter(5)
|
||||||
|
- expression 4 operands: lhs = Counter(1), rhs = Counter(4)
|
||||||
|
- expression 5 operands: lhs = Counter(4), rhs = Counter(5)
|
||||||
|
- expression 6 operands: lhs = Expression(12, Add), rhs = Counter(6)
|
||||||
|
- expression 7 operands: lhs = Counter(4), rhs = Counter(5)
|
||||||
|
- expression 8 operands: lhs = Counter(6), rhs = Counter(7)
|
||||||
|
- expression 9 operands: lhs = Counter(6), rhs = Counter(7)
|
||||||
|
- expression 10 operands: lhs = Counter(7), rhs = Expression(11, Sub)
|
||||||
|
- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(6)
|
||||||
|
- expression 12 operands: lhs = Counter(4), rhs = Counter(5)
|
||||||
|
- expression 13 operands: lhs = Expression(14, Sub), rhs = Counter(5)
|
||||||
|
- expression 14 operands: lhs = Counter(1), rhs = Counter(4)
|
||||||
|
- expression 15 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||||
|
- expression 16 operands: lhs = Counter(3), rhs = Expression(17, Add)
|
||||||
|
- expression 17 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||||
|
Number of file 0 mappings: 20
|
||||||
|
- Code(Counter(0)) at (prev + 34, 1) to (start + 1, 9)
|
||||||
|
- MCDCDecision { bitmap_idx: 2, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 75)
|
||||||
|
- 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 + 0, 75)
|
||||||
|
true = c3
|
||||||
|
false = c2
|
||||||
|
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 16) to (start + 0, 22)
|
||||||
|
- MCDCBranch { true: Counter(4), false: Expression(14, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
true = c4
|
||||||
|
false = (c1 - c4)
|
||||||
|
- Code(Expression(14, Sub)) at (prev + 0, 21) to (start + 0, 22)
|
||||||
|
= (c1 - c4)
|
||||||
|
- MCDCBranch { true: Counter(5), false: Expression(13, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 22)
|
||||||
|
true = c5
|
||||||
|
false = ((c1 - c4) - c5)
|
||||||
|
- Code(Expression(12, Add)) at (prev + 0, 28) to (start + 0, 29)
|
||||||
|
= (c4 + c5)
|
||||||
|
- MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 0, 28) to (start + 0, 34)
|
||||||
|
- MCDCBranch { true: Counter(6), false: Expression(11, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 29)
|
||||||
|
true = c6
|
||||||
|
false = ((c4 + c5) - c6)
|
||||||
|
- Code(Counter(6)) at (prev + 0, 33) to (start + 0, 34)
|
||||||
|
- MCDCBranch { true: Expression(9, Sub), false: Counter(7), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 33) to (start + 0, 34)
|
||||||
|
true = (c6 - c7)
|
||||||
|
false = c7
|
||||||
|
- Code(Expression(9, Sub)) at (prev + 0, 37) to (start + 0, 41)
|
||||||
|
= (c6 - c7)
|
||||||
|
- Code(Expression(10, Add)) at (prev + 0, 51) to (start + 0, 56)
|
||||||
|
= (c7 + ((c4 + c5) - c6))
|
||||||
|
- Code(Expression(13, Sub)) at (prev + 0, 68) to (start + 0, 73)
|
||||||
|
= ((c1 - c4) - c5)
|
||||||
|
- Code(Counter(3)) at (prev + 0, 76) to (start + 2, 6)
|
||||||
|
- Code(Expression(17, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||||
|
= (c2 + (c0 - c1))
|
||||||
|
- Code(Expression(16, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||||
|
= (c3 + (c2 + (c0 - c1)))
|
||||||
|
|
||||||
|
Function name: mcdc_nested_if::nested_single_condition_decision
|
||||||
|
Raw bytes (85): 0x[01, 01, 06, 01, 05, 05, 11, 05, 11, 09, 02, 0d, 17, 09, 02, 0b, 01, 17, 01, 04, 09, 28, 00, 02, 04, 08, 00, 29, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 29, 05, 00, 10, 00, 11, 20, 11, 0a, 00, 10, 00, 11, 11, 00, 14, 00, 19, 0a, 00, 23, 00, 27, 0d, 00, 2a, 02, 06, 17, 02, 0c, 02, 06, 13, 03, 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 + 23, 1) to (start + 4, 9)
|
||||||
|
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 4, 8) to (start + 0, 41)
|
||||||
|
- 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 + 0, 41)
|
||||||
|
true = c3
|
||||||
|
false = c2
|
||||||
|
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
- Branch { true: Counter(4), false: Expression(2, Sub) } at (prev + 0, 16) to (start + 0, 17)
|
||||||
|
true = c4
|
||||||
|
false = (c1 - c4)
|
||||||
|
- Code(Counter(4)) at (prev + 0, 20) to (start + 0, 25)
|
||||||
|
- Code(Expression(2, Sub)) at (prev + 0, 35) to (start + 0, 39)
|
||||||
|
= (c1 - c4)
|
||||||
|
- Code(Counter(3)) at (prev + 0, 42) to (start + 2, 6)
|
||||||
|
- Code(Expression(5, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||||
|
= (c2 + (c0 - c1))
|
||||||
|
- Code(Expression(4, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||||
|
= (c3 + (c2 + (c0 - c1)))
|
||||||
|
|
235
tests/coverage/mcdc_nested_if.coverage
Normal file
235
tests/coverage/mcdc_nested_if.coverage
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
LL| |#![feature(coverage_attribute)]
|
||||||
|
LL| |//@ edition: 2021
|
||||||
|
LL| |//@ min-llvm-version: 18
|
||||||
|
LL| |//@ compile-flags: -Zcoverage-options=mcdc
|
||||||
|
LL| |//@ llvm-cov-flags: --show-mcdc
|
||||||
|
LL| |
|
||||||
|
LL| 4|fn nested_if_in_condition(a: bool, b: bool, c: bool) {
|
||||||
|
LL| 4| if a && if b || c { true } else { false } {
|
||||||
|
^3 ^2 ^2 ^1
|
||||||
|
------------------
|
||||||
|
|---> MC/DC Decision Region (LL:8) to (LL:46)
|
||||||
|
|
|
||||||
|
| 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%
|
||||||
|
|
|
||||||
|
|---> MC/DC Decision Region (LL:16) to (LL:22)
|
||||||
|
|
|
||||||
|
| Number of Conditions: 2
|
||||||
|
| Condition C1 --> (LL:16)
|
||||||
|
| Condition C2 --> (LL:21)
|
||||||
|
|
|
||||||
|
| Executed MC/DC Test Vectors:
|
||||||
|
|
|
||||||
|
| C1, C2 Result
|
||||||
|
| 1 { F, F = F }
|
||||||
|
| 2 { T, - = T }
|
||||||
|
| 3 { F, T = T }
|
||||||
|
|
|
||||||
|
| C1-Pair: covered: (1,2)
|
||||||
|
| C2-Pair: covered: (1,3)
|
||||||
|
| MC/DC Coverage for Decision: 100.00%
|
||||||
|
|
|
||||||
|
------------------
|
||||||
|
LL| 2| say("yes");
|
||||||
|
LL| 2| } else {
|
||||||
|
LL| 2| say("no");
|
||||||
|
LL| 2| }
|
||||||
|
LL| 4|}
|
||||||
|
LL| |
|
||||||
|
LL| 4|fn doubly_nested_if_in_condition(a: bool, b: bool, c: bool, d: bool) {
|
||||||
|
LL| 4| if a && if b || if c && d { true } else { false } { false } else { true } {
|
||||||
|
^3 ^2 ^1 ^1 ^1 ^2 ^1
|
||||||
|
------------------
|
||||||
|
|---> MC/DC Decision Region (LL:8) to (LL:78)
|
||||||
|
|
|
||||||
|
| 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%
|
||||||
|
|
|
||||||
|
|---> MC/DC Decision Region (LL:16) to (LL:54)
|
||||||
|
|
|
||||||
|
| Number of Conditions: 2
|
||||||
|
| Condition C1 --> (LL:16)
|
||||||
|
| Condition C2 --> (LL:21)
|
||||||
|
|
|
||||||
|
| Executed MC/DC Test Vectors:
|
||||||
|
|
|
||||||
|
| C1, C2 Result
|
||||||
|
| 1 { F, F = F }
|
||||||
|
| 2 { T, - = T }
|
||||||
|
| 3 { F, T = T }
|
||||||
|
|
|
||||||
|
| C1-Pair: covered: (1,2)
|
||||||
|
| C2-Pair: covered: (1,3)
|
||||||
|
| MC/DC Coverage for Decision: 100.00%
|
||||||
|
|
|
||||||
|
|---> MC/DC Decision Region (LL:24) to (LL:30)
|
||||||
|
|
|
||||||
|
| Number of Conditions: 2
|
||||||
|
| Condition C1 --> (LL:24)
|
||||||
|
| Condition C2 --> (LL:29)
|
||||||
|
|
|
||||||
|
| 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("yes");
|
||||||
|
LL| 3| } else {
|
||||||
|
LL| 3| say("no");
|
||||||
|
LL| 3| }
|
||||||
|
LL| 4|}
|
||||||
|
LL| |
|
||||||
|
LL| 3|fn nested_single_condition_decision(a: bool, b: bool) {
|
||||||
|
LL| 3| // Decision with only 1 decision should not be instrumented by MCDC because
|
||||||
|
LL| 3| // branch-coverage is equivalent to MCDC coverage in this case, and we don't
|
||||||
|
LL| 3| // want to waste bitmap space for this.
|
||||||
|
LL| 3| if a && if b { false } else { true } {
|
||||||
|
^2 ^1 ^1
|
||||||
|
------------------
|
||||||
|
|---> MC/DC Decision Region (LL:8) to (LL:41)
|
||||||
|
|
|
||||||
|
| 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| } else {
|
||||||
|
LL| 2| say("no");
|
||||||
|
LL| 2| }
|
||||||
|
LL| 3|}
|
||||||
|
LL| |
|
||||||
|
LL| 7|fn nested_in_then_block_in_condition(a: bool, b: bool, c: bool, d: bool, e: bool) {
|
||||||
|
LL| 7| if a && if b || c { if d && e { true } else { false } } else { false } {
|
||||||
|
^6 ^5 ^5 ^2 ^1 ^4 ^1
|
||||||
|
------------------
|
||||||
|
|---> MC/DC Decision Region (LL:8) to (LL:75)
|
||||||
|
|
|
||||||
|
| 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%
|
||||||
|
|
|
||||||
|
|---> MC/DC Decision Region (LL:16) to (LL:22)
|
||||||
|
|
|
||||||
|
| Number of Conditions: 2
|
||||||
|
| Condition C1 --> (LL:16)
|
||||||
|
| Condition C2 --> (LL:21)
|
||||||
|
|
|
||||||
|
| Executed MC/DC Test Vectors:
|
||||||
|
|
|
||||||
|
| C1, C2 Result
|
||||||
|
| 1 { F, F = F }
|
||||||
|
| 2 { T, - = T }
|
||||||
|
| 3 { F, T = T }
|
||||||
|
|
|
||||||
|
| C1-Pair: covered: (1,2)
|
||||||
|
| C2-Pair: covered: (1,3)
|
||||||
|
| MC/DC Coverage for Decision: 100.00%
|
||||||
|
|
|
||||||
|
|---> MC/DC Decision Region (LL:28) to (LL:34)
|
||||||
|
|
|
||||||
|
| Number of Conditions: 2
|
||||||
|
| Condition C1 --> (LL:28)
|
||||||
|
| Condition C2 --> (LL:33)
|
||||||
|
|
|
||||||
|
| 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| 6| } else {
|
||||||
|
LL| 6| say("no");
|
||||||
|
LL| 6| }
|
||||||
|
LL| 7|}
|
||||||
|
LL| |
|
||||||
|
LL| |#[coverage(off)]
|
||||||
|
LL| |fn main() {
|
||||||
|
LL| | nested_if_in_condition(true, false, false);
|
||||||
|
LL| | nested_if_in_condition(true, true, true);
|
||||||
|
LL| | nested_if_in_condition(true, false, true);
|
||||||
|
LL| | nested_if_in_condition(false, true, true);
|
||||||
|
LL| |
|
||||||
|
LL| | doubly_nested_if_in_condition(true, false, false, true);
|
||||||
|
LL| | doubly_nested_if_in_condition(true, true, true, true);
|
||||||
|
LL| | doubly_nested_if_in_condition(true, false, true, true);
|
||||||
|
LL| | doubly_nested_if_in_condition(false, true, true, true);
|
||||||
|
LL| |
|
||||||
|
LL| | nested_single_condition_decision(true, true);
|
||||||
|
LL| | nested_single_condition_decision(true, false);
|
||||||
|
LL| | nested_single_condition_decision(false, false);
|
||||||
|
LL| |
|
||||||
|
LL| | nested_in_then_block_in_condition(false, false, false, false, false);
|
||||||
|
LL| | nested_in_then_block_in_condition(true, false, false, false, false);
|
||||||
|
LL| | nested_in_then_block_in_condition(true, true, false, false, false);
|
||||||
|
LL| | nested_in_then_block_in_condition(true, false, true, false, false);
|
||||||
|
LL| | nested_in_then_block_in_condition(true, false, true, true, false);
|
||||||
|
LL| | nested_in_then_block_in_condition(true, false, true, false, true);
|
||||||
|
LL| | nested_in_then_block_in_condition(true, false, true, true, true);
|
||||||
|
LL| |}
|
||||||
|
LL| |
|
||||||
|
LL| |#[coverage(off)]
|
||||||
|
LL| |fn say(message: &str) {
|
||||||
|
LL| | core::hint::black_box(message);
|
||||||
|
LL| |}
|
||||||
|
|
70
tests/coverage/mcdc_nested_if.rs
Normal file
70
tests/coverage/mcdc_nested_if.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#![feature(coverage_attribute)]
|
||||||
|
//@ edition: 2021
|
||||||
|
//@ min-llvm-version: 18
|
||||||
|
//@ compile-flags: -Zcoverage-options=mcdc
|
||||||
|
//@ llvm-cov-flags: --show-mcdc
|
||||||
|
|
||||||
|
fn nested_if_in_condition(a: bool, b: bool, c: bool) {
|
||||||
|
if a && if b || c { true } else { false } {
|
||||||
|
say("yes");
|
||||||
|
} else {
|
||||||
|
say("no");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doubly_nested_if_in_condition(a: bool, b: bool, c: bool, d: bool) {
|
||||||
|
if a && if b || if c && d { true } else { false } { false } else { true } {
|
||||||
|
say("yes");
|
||||||
|
} else {
|
||||||
|
say("no");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nested_single_condition_decision(a: bool, b: bool) {
|
||||||
|
// Decision with only 1 decision should not be instrumented by MCDC because
|
||||||
|
// branch-coverage is equivalent to MCDC coverage in this case, and we don't
|
||||||
|
// want to waste bitmap space for this.
|
||||||
|
if a && if b { false } else { true } {
|
||||||
|
say("yes");
|
||||||
|
} else {
|
||||||
|
say("no");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nested_in_then_block_in_condition(a: bool, b: bool, c: bool, d: bool, e: bool) {
|
||||||
|
if a && if b || c { if d && e { true } else { false } } else { false } {
|
||||||
|
say("yes");
|
||||||
|
} else {
|
||||||
|
say("no");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[coverage(off)]
|
||||||
|
fn main() {
|
||||||
|
nested_if_in_condition(true, false, false);
|
||||||
|
nested_if_in_condition(true, true, true);
|
||||||
|
nested_if_in_condition(true, false, true);
|
||||||
|
nested_if_in_condition(false, true, true);
|
||||||
|
|
||||||
|
doubly_nested_if_in_condition(true, false, false, true);
|
||||||
|
doubly_nested_if_in_condition(true, true, true, true);
|
||||||
|
doubly_nested_if_in_condition(true, false, true, true);
|
||||||
|
doubly_nested_if_in_condition(false, true, true, true);
|
||||||
|
|
||||||
|
nested_single_condition_decision(true, true);
|
||||||
|
nested_single_condition_decision(true, false);
|
||||||
|
nested_single_condition_decision(false, false);
|
||||||
|
|
||||||
|
nested_in_then_block_in_condition(false, false, false, false, false);
|
||||||
|
nested_in_then_block_in_condition(true, false, false, false, false);
|
||||||
|
nested_in_then_block_in_condition(true, true, false, false, false);
|
||||||
|
nested_in_then_block_in_condition(true, false, true, false, false);
|
||||||
|
nested_in_then_block_in_condition(true, false, true, true, false);
|
||||||
|
nested_in_then_block_in_condition(true, false, true, false, true);
|
||||||
|
nested_in_then_block_in_condition(true, false, true, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[coverage(off)]
|
||||||
|
fn say(message: &str) {
|
||||||
|
core::hint::black_box(message);
|
||||||
|
}
|
Loading…
Reference in a new issue