-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
coverage: Treat each match arm as a "branch" for branch coverage #124154
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, the added tests really speak for themselves 🎉
left a small style nit
Some small style updates (diff). |
I noticed that I accidentally included my let-else test as well, but instead of removing it, I decided to just add my if-let test as well. Both of these test show that the respective features don't support branch coverage yet. |
Discard match expressions early if they have <2 arms (diff). |
59c2385
to
356427f
Compare
After investigating some more, I realised that my scheme for making the false-branch expressions isn't accounting for the actual control flow around match guards. For matches with no guards, the false-count for any arm is just the sum of the execution counts of all subsequent arms. But for an arm with a guard, we also need to subtract the number of times that the match succeeded but the guard failed. We're currently failing to do that, which is why we get inflated counts, which then mess up all earlier arms too. |
For now I've updated the implementation to skip any match expressions that have guards, so that at least we aren't producing wrong branch-coverage numbers for them (diff). |
More style updates (diff). |
It looks like the point where we know an arm's pre-binding block and its guard-succeeded block is just after the call to Is there a good way to forward this information to |
☔ The latest upstream changes (presumably #124194) made this pull request unmergeable. Please resolve the merge conflicts. |
d0dfcd8
to
bc042ac
Compare
Rollup merge of rust-lang#124217 - Zalathar:pre-branch, r=oli-obk coverage: Prepare for improved branch coverage When trying to rebase my new branch coverage work (including rust-lang#124154) on top of the introduction of MC/DC coverage (rust-lang#123409), I found it a lot harder than anticipated. With the benefit of hindsight, the branch coverage code and MC/DC code have become more interdependent than I'm happy with. This PR therefore disentangles them a bit, so that it will be easier for both areas of code to evolve independently without interference. --- This PR also includes a few extra branch coverage tests that I had sitting around from my current branch coverage work. They mostly just demonstrate that certain language constructs listed in rust-lang#124118 currently don't have branch coverage support. ``@rustbot`` label +A-code-coverage
LL| | | ||
LL| 1|fn never_taken() { | ||
LL| 1| match black_box(false) { | ||
LL| 1| _ if black_box(false) => {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering why the pattern _
and the guard black_box(false)
are in the same code region, while there are 2 branch expressions.
I would have expected the guard to have its own counter displayed underneath
LL| 1| _ if black_box(false) => {}
^1 ^0
☔ The latest upstream changes (presumably #124399) made this pull request unmergeable. Please resolve the merge conflicts. |
Brushed some of the cobwebs off this PR. I still don't want to proceed without a plan for how to handle or-patterns, but at least this implementation is now a more up-to-date reference. |
Within the `InstrumentCoverage` pass, we now represent branches as a list of arms, instead of a true/false pair, until we prepare the final table of mappings to be attached to the MIR body. (We then flatten the list into two-way branches by treating each arm as a branch between its success block, and the total of all later arms.) Currently all of the branches produced by MIR building are still two-way, but this is a step towards allowing many-way branches.
☔ The latest upstream changes (presumably #127174) made this pull request unmergeable. Please resolve the merge conflicts. |
#127159 should make your life easier. |
I also want to add a link here to https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Details.20of.20match.20guard.20semantics/near/448161106, from which I learned that the interaction between or-patterns and guards is more complex than I had thought, because the guard might need to be evaluated multiple times per match in the general case. |
coverage: Restrict `ExpressionUsed` simplification to `Code` mappings In the future, branch and MC/DC mappings might have expressions that don't correspond to any single point in the control-flow graph. That makes it trickier to keep track of which expressions should expect an `ExpressionUsed` node. We therefore sidestep that complexity by only performing `ExpressionUsed` simplification for expressions associated directly with ordinary `Code` mappings. (This simplification step is inherited from the original coverage implementation, which only supported `Code` mappings anyway, so there's no particular reason to extend it to other kinds of mappings unless we specifically choose to.) Relevant to: - rust-lang#124154 - rust-lang#126677 - rust-lang#124278 `@rustbot` label +A-code-coverage
coverage: Restrict `ExpressionUsed` simplification to `Code` mappings In the future, branch and MC/DC mappings might have expressions that don't correspond to any single point in the control-flow graph. That makes it trickier to keep track of which expressions should expect an `ExpressionUsed` node. We therefore sidestep that complexity by only performing `ExpressionUsed` simplification for expressions associated directly with ordinary `Code` mappings. (This simplification step is inherited from the original coverage implementation, which only supported `Code` mappings anyway, so there's no particular reason to extend it to other kinds of mappings unless we specifically choose to.) Relevant to: - rust-lang#124154 - rust-lang#126677 - rust-lang#124278 ``@rustbot`` label +A-code-coverage
coverage: Restrict `ExpressionUsed` simplification to `Code` mappings In the future, branch and MC/DC mappings might have expressions that don't correspond to any single point in the control-flow graph. That makes it trickier to keep track of which expressions should expect an `ExpressionUsed` node. We therefore sidestep that complexity by only performing `ExpressionUsed` simplification for expressions associated directly with ordinary `Code` mappings. (This simplification step is inherited from the original coverage implementation, which only supported `Code` mappings anyway, so there's no particular reason to extend it to other kinds of mappings unless we specifically choose to.) Relevant to: - rust-lang#124154 - rust-lang#126677 - rust-lang#124278 ```@rustbot``` label +A-code-coverage
Rollup merge of rust-lang#127758 - Zalathar:expression-used, r=oli-obk coverage: Restrict `ExpressionUsed` simplification to `Code` mappings In the future, branch and MC/DC mappings might have expressions that don't correspond to any single point in the control-flow graph. That makes it trickier to keep track of which expressions should expect an `ExpressionUsed` node. We therefore sidestep that complexity by only performing `ExpressionUsed` simplification for expressions associated directly with ordinary `Code` mappings. (This simplification step is inherited from the original coverage implementation, which only supported `Code` mappings anyway, so there's no particular reason to extend it to other kinds of mappings unless we specifically choose to.) Relevant to: - rust-lang#124154 - rust-lang#126677 - rust-lang#124278 ```@rustbot``` label +A-code-coverage
@Zalathar any updates on this? thanks |
This PR adds branch coverage instrumentation for individual arms in
match
expressions.It seems to work well in most cases. However, the current implementation cannot handle match expressions containing or-patterns
|
, and therefore ignores such match expressions entirely.(More specifically, it ignores a match expression if any of its
Candidate
s haspre_binding_block = None
.)Right now I'm not sure whether this should be merged as-is (to provide a useful subset of functionality, and avoid bit-rot), or should be left as a draft until the or-pattern limitation can be lifted (could potentially require major rework).