Description
This issue aims to be a discussion about the implementation of branch coverage, to follow up the discussions in #79649 and #124118.
In the following code example, the current implementation of branch coverage does not instrument the outcome of b
in the indirect
function, because it has no impact on the control flow of the program and is just assigned to v
. a
, however, creates a conditional evaluation of b
because of the short circuits semantics and thus creates a branch in the control flow.
fn direct(a: bool, b: bool) {
if a || b {
println!("success");
}
}
fn indirect(a: bool, b: bool) {
let v = a || b;
if v {
println!("success");
}
}
#[coverage(off)]
fn main() {
direct(true, false);
direct(false, false);
indirect(true, false);
indirect(false, false);
}
Even though this instrumentation is "branch coverage" according to the definition, there are several reasons for which one would expect b
to be instrumented as well :
- The current behavior may be found counter-intuitive, as illustrated by the question in Add support for branch coverage in source-based coverage #79649.
- This is the behavior adopted in clang/LLVM.
- MC/DC coverage instrumentation depends on branch coverage, and in certification context, MC/DC coverage implies that all the conditions in all the boolean expressions with 2+ operands are instrumented.
@Zalathar gave pertinent reasons on why the "instrument b
" behavior might not suit everyone's needs, one being that it implies inserting branches in the MIR that are not here by default. Roughly speaking, this would imply that the MIR of the expression would look like the one of let x = if a || b { true } else { false };
rather than let x = if a { b } else { false };
.
In order to give the choice to the user, I think we could implement this behavior under another compilation flags: -Z coverage-options=condition
.
This idea is that condition coverage instruments the outcome of each condition inside boolean expressions:
- when the expression has 2+ conditions and is outside of a control flow structure
- anytime the expression is in a control flow structure.
Basically, this is branch coverage with the special case of b
being instrumented.
In terms of implementation, I think the easiest would be to make condition
imply branch
coverage, and treat the special case only when condition
is enabled.