Description
Introduction
Modified condition/decision coverage (MC/DC) is a code coverage criterion used widely in safety critical software components and is required by standards such as DO-178B and ISO26262.
Terminology
condition: boolean expressions that have no binary logical operators. For example, a || b
is not "condition" because it has an or operator while a==b
is.
decision: longest boolean expressions composed of conditions and binary boolean expressions only.
MC/DC requires each condition in a decision is shown to independently affect the outcome of the decision.
e.g Suppose we have code like
if (a || b) && c {
todo!();
}
Here (a || b) && c
is a decision and a
,b
,c
are conditions.
- If test cases are
(a=true, b=false, c=true)
and(a=false, b=false, c=true)
, we saya
can independently affect the decision because the value of decision is changed asa
changes while keepb
andc
unchanged. So that we get 1/3 MC/DC here (1 fora
and 3 fora
,b
,c
). - Test cases
(a=false, b=true, c=true)
and(a=false, b=false, c=false)
also showb
can independently affect the decision. Though in the later casec
is also changed but it is short-circuited and has no impacts (thus we can view it as same asc=true
). Whilec
is not acknowledged due to change ofb
. Plus the two cases before we get 2/3 MC/DC. - Test cases
(a=true,b=false,c=true)
and(a=true,b=false,c=false)
showc
can do the same. By now we get 3/3.
Notice that there are duplicate cases, so test cases collection {(a=true, b=false, c=true)
,(a=false, b=false, c=true)
,(a=false, b=true, c=true)
, (a=false, b=false, c=false)
,(a=true,b=false,c=false)
} are sufficient to prove 3/3 MC/DC.
In fact we can use at least n+1 cases to prove 100% MC/DC of decision with n conditions. (In this example, {(a=true,b=false,c=true)
,(a=false,b=false,c=true)
,(a=false,b=true,c=true)
,(a=true,b=false,c=false)
} are enough)
Progress
A basic implementation for MC/DC is filed on #123409 , which has some limits. There are still several cases need to handle:
- Support plain boolean expressions like
let val = a || b
. Done at
coverage: Optionally instrument the RHS of lazy logical operators #125756
MCDC Coverage: instrument last boolean RHS operands from condition coverage #125766 - Support pattern matching. Ferrous suggests that refutable patterns should be considered as decisions, which sounds certainly reasonable as these are considered as branches too.
Draft: Support mcdc analysis for pattern matching #124278 - Support nested decisions like
if (a || b) || inner_decision(c || d)
. Done at MCDC coverage: support nested decision coverage #124255 - So far only decisions with at most 6 conditions are counted due to resource cost. This limit should be relaxed now that the llvm backend had this optimization.
[Coverage][MCDC] Adapt mcdc to llvm 19 #126733
Known Issues
- Decisions containing constant conditions may result incorrect report, where some non-constant conditions are regarded as constants or vice versa. This issue occurs because the way rustc produces counters for constants is not what llvm expects. It's fixed by llvm #112694 and can be solved once rustc upgraded in-tree llvm to 20.
- Some nested decisions may cause panic as llvm #91600 described.
- On some occasions rustc may painic with "instrprof failed to lower mcdc parameters", see [Coverage][MCDC] Do not initialize mcdc parameters for instances containing no mcdc statements #129989