Skip to content

Commit 6ee4a41

Browse files
committed
Fixes multiple issue with counters, with simplification
Includes a change to the implicit else span in ast_lowering, so coverage of the implicit else no longer spans the `then` block. Adds coverage for unused closures and async function bodies. Fixes: #78542
1 parent 609ea4e commit 6ee4a41

File tree

276 files changed

+5447
-9767
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

276 files changed

+5447
-9767
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
347347
// `_ => else_block` where `else_block` is `{}` if there's `None`:
348348
let else_pat = self.pat_wild(span);
349349
let (else_expr, contains_else_clause) = match else_opt {
350-
None => (self.expr_block_empty(span), false),
350+
None => (self.expr_block_empty(span.shrink_to_hi()), false),
351351
Some(els) => (self.lower_expr(els), true),
352352
};
353353
let else_arm = self.arm(else_pat, else_expr);

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+15-32
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::llvm;
55
use llvm::coverageinfo::CounterMappingRegion;
66
use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression};
77
use rustc_codegen_ssa::traits::ConstMethods;
8-
use rustc_data_structures::fx::FxIndexSet;
8+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
99
use rustc_llvm::RustString;
1010
use rustc_middle::mir::coverage::CodeRegion;
1111

@@ -42,6 +42,11 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
4242
return;
4343
}
4444

45+
let covered_def_ids = function_coverage_map
46+
.keys()
47+
.map(|instance| instance.def.def_id())
48+
.collect::<FxHashSet<_>>();
49+
4550
let mut mapgen = CoverageMapGenerator::new();
4651

4752
// Encode coverage mappings and generate function records
@@ -52,7 +57,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
5257
let mangled_function_name = cx.tcx.symbol_name(instance).to_string();
5358
let function_source_hash = function_coverage.source_hash();
5459
let (expressions, counter_regions) =
55-
function_coverage.get_expressions_and_counter_regions();
60+
function_coverage.get_expressions_and_counter_regions(&covered_def_ids);
5661

5762
let coverage_mapping_buffer = llvm::build_byte_buffer(|coverage_mapping_buffer| {
5863
mapgen.write_coverage_mapping(expressions, counter_regions, coverage_mapping_buffer);
@@ -141,36 +146,14 @@ impl CoverageMapGenerator {
141146
virtual_file_mapping.push(filenames_index as u32);
142147
}
143148
debug!("Adding counter {:?} to map for {:?}", counter, region);
144-
match counter.kind {
145-
rustc_codegen_ssa::coverageinfo::map::CounterKind::Zero => {
146-
// Assume `Zero` counters are either from `Unreachable` code regions, or they
147-
// should at least be handled similarly, the `gap_region` signifies a code span
148-
// that should be marked as known, but uncovered (counted as zero executions)
149-
// _except_ where other code regions overlap. For example, a closure that is
150-
// defined but never used will probably not get codegenned, and therefore would
151-
// not have any coverage, but the MIR in which the closure is defined can at
152-
// least inject the span for the closure as a `gap_region`, ensuring the code
153-
// region is at least known about, and can be flagged as uncovered if necessary.
154-
mapping_regions.push(CounterMappingRegion::gap_region(
155-
counter,
156-
current_file_id,
157-
start_line,
158-
start_col,
159-
end_line,
160-
end_col,
161-
));
162-
}
163-
_ => {
164-
mapping_regions.push(CounterMappingRegion::code_region(
165-
counter,
166-
current_file_id,
167-
start_line,
168-
start_col,
169-
end_line,
170-
end_col,
171-
));
172-
}
173-
}
149+
mapping_regions.push(CounterMappingRegion::code_region(
150+
counter,
151+
current_file_id,
152+
start_line,
153+
start_col,
154+
end_line,
155+
end_col,
156+
));
174157
}
175158

176159
// Encode and append the current function's coverage mapping data

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_middle::mir::coverage::{
1515
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op,
1616
};
1717
use rustc_middle::ty::Instance;
18+
use rustc_span::def_id::DefId;
1819

1920
use std::cell::RefCell;
2021
use std::ffi::CString;
@@ -127,7 +128,12 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
127128
}
128129
}
129130

130-
fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool {
131+
fn add_coverage_unreachable(
132+
&mut self,
133+
instance: Instance<'tcx>,
134+
closure_def_id: Option<DefId>,
135+
region: CodeRegion,
136+
) -> bool {
131137
if let Some(coverage_context) = self.coverage_context() {
132138
debug!(
133139
"adding unreachable code to coverage_map: instance={:?}, at {:?}",
@@ -137,7 +143,7 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
137143
coverage_map
138144
.entry(instance)
139145
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
140-
.add_unreachable_region(region);
146+
.add_unreachable(closure_def_id, region);
141147
true
142148
} else {
143149
false

compiler/rustc_codegen_ssa/src/coverageinfo/map.rs

+30-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
pub use super::ffi::*;
22

3+
use rustc_data_structures::fx::FxHashSet;
34
use rustc_index::vec::IndexVec;
45
use rustc_middle::mir::coverage::{
56
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId,
67
InjectedExpressionIndex, MappedExpressionIndex, Op,
78
};
89
use rustc_middle::ty::Instance;
910
use rustc_middle::ty::TyCtxt;
11+
use rustc_span::def_id::DefId;
1012

1113
#[derive(Clone, Debug)]
1214
pub struct Expression {
@@ -16,6 +18,12 @@ pub struct Expression {
1618
region: Option<CodeRegion>,
1719
}
1820

21+
#[derive(Debug)]
22+
struct Unreachable {
23+
closure_def_id: Option<DefId>,
24+
region: CodeRegion,
25+
}
26+
1927
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
2028
/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
2129
/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they
@@ -33,7 +41,7 @@ pub struct FunctionCoverage<'tcx> {
3341
source_hash: u64,
3442
counters: IndexVec<CounterValueReference, Option<CodeRegion>>,
3543
expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>,
36-
unreachable_regions: Vec<CodeRegion>,
44+
unreachables: Vec<Unreachable>,
3745
}
3846

3947
impl<'tcx> FunctionCoverage<'tcx> {
@@ -48,7 +56,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
4856
source_hash: 0, // will be set with the first `add_counter()`
4957
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
5058
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
51-
unreachable_regions: Vec::new(),
59+
unreachables: Vec::new(),
5260
}
5361
}
5462

@@ -100,8 +108,8 @@ impl<'tcx> FunctionCoverage<'tcx> {
100108
}
101109

102110
/// Add a region that will be marked as "unreachable", with a constant "zero counter".
103-
pub fn add_unreachable_region(&mut self, region: CodeRegion) {
104-
self.unreachable_regions.push(region)
111+
pub fn add_unreachable(&mut self, closure_def_id: Option<DefId>, region: CodeRegion) {
112+
self.unreachables.push(Unreachable { closure_def_id, region })
105113
}
106114

107115
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
@@ -113,8 +121,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
113121
/// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
114122
/// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
115123
/// `CounterMappingRegion`s.
116-
pub fn get_expressions_and_counter_regions<'a>(
124+
pub fn get_expressions_and_counter_regions<'a, 'b: 'a>(
117125
&'a self,
126+
covered_def_ids: &'b FxHashSet<DefId>,
118127
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {
119128
assert!(
120129
self.source_hash != 0,
@@ -124,10 +133,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
124133

125134
let counter_regions = self.counter_regions();
126135
let (counter_expressions, expression_regions) = self.expressions_with_regions();
127-
let unreachable_regions = self.unreachable_regions();
136+
let unreachables = self.unreachables(covered_def_ids);
128137

129138
let counter_regions =
130-
counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions));
139+
counter_regions.chain(expression_regions.into_iter().chain(unreachables));
131140
(counter_expressions, counter_regions)
132141
}
133142

@@ -250,8 +259,20 @@ impl<'tcx> FunctionCoverage<'tcx> {
250259
(counter_expressions, expression_regions.into_iter())
251260
}
252261

253-
fn unreachable_regions<'a>(&'a self) -> impl Iterator<Item = (Counter, &'a CodeRegion)> {
254-
self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
262+
fn unreachables<'a, 'b: 'a>(
263+
&'a self,
264+
covered_def_ids: &'b FxHashSet<DefId>,
265+
) -> impl Iterator<Item = (Counter, &'a CodeRegion)> {
266+
self.unreachables.iter().filter_map(move |unreachable| {
267+
if let Some(closure_def_id) = unreachable.closure_def_id {
268+
if covered_def_ids.contains(&closure_def_id) {
269+
debug!("unreachable {:?} IS COVERED AND WILL BE FILTERED", unreachable);
270+
return None;
271+
}
272+
}
273+
debug!("unreachable {:?} IS NOT COVERED... ADDING Counter::zero()", unreachable);
274+
Some((Counter::zero(), &unreachable.region))
275+
})
255276
}
256277

257278
fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex {

compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
3636
CoverageKind::Expression { id, lhs, op, rhs } => {
3737
bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region);
3838
}
39-
CoverageKind::Unreachable => {
39+
CoverageKind::Unreachable { closure_def_id } => {
4040
bx.add_coverage_unreachable(
4141
self.instance,
42+
closure_def_id,
4243
code_region.expect("unreachable regions always have code regions"),
4344
);
4445
}

compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::BackendTypes;
22
use rustc_middle::mir::coverage::*;
33
use rustc_middle::ty::Instance;
4+
use rustc_span::def_id::DefId;
45

56
pub trait CoverageInfoMethods: BackendTypes {
67
fn coverageinfo_finalize(&self);
@@ -41,5 +42,10 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
4142

4243
/// Returns true if the region was added to the coverage map; false if `-Z instrument-coverage`
4344
/// is not enabled (a coverage map is not being generated).
44-
fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool;
45+
fn add_coverage_unreachable(
46+
&mut self,
47+
instance: Instance<'tcx>,
48+
closure_def_id: Option<DefId>,
49+
region: CodeRegion,
50+
) -> bool;
4551
}

compiler/rustc_middle/src/mir/coverage.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Metadata from source code coverage analysis and instrumentation.
22
33
use rustc_macros::HashStable;
4+
use rustc_span::def_id::DefId;
45
use rustc_span::Symbol;
56

67
use std::cmp::Ord;
@@ -104,7 +105,9 @@ pub enum CoverageKind {
104105
op: Op,
105106
rhs: ExpressionOperandId,
106107
},
107-
Unreachable,
108+
Unreachable {
109+
closure_def_id: Option<DefId>,
110+
},
108111
}
109112

110113
impl CoverageKind {
@@ -113,7 +116,7 @@ impl CoverageKind {
113116
match *self {
114117
Counter { id, .. } => ExpressionOperandId::from(id),
115118
Expression { id, .. } => ExpressionOperandId::from(id),
116-
Unreachable => bug!("Unreachable coverage cannot be part of an expression"),
119+
Unreachable { .. } => bug!("Unreachable coverage cannot be part of an expression"),
117120
}
118121
}
119122

@@ -132,7 +135,10 @@ impl CoverageKind {
132135
}
133136

134137
pub fn is_unreachable(&self) -> bool {
135-
*self == Self::Unreachable
138+
match self {
139+
Self::Unreachable { .. } => true,
140+
_ => false,
141+
}
136142
}
137143
}
138144

@@ -149,7 +155,9 @@ impl Debug for CoverageKind {
149155
if *op == Op::Add { "+" } else { "-" },
150156
rhs.index(),
151157
),
152-
Unreachable => write!(fmt, "Unreachable"),
158+
Unreachable { closure_def_id } => {
159+
write!(fmt, "Unreachable(closure_def_id = {:?})", closure_def_id)
160+
}
153161
}
154162
}
155163
}

compiler/rustc_mir/src/transform/coverage/counters.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,7 @@ impl<'a> BcbCounters<'a> {
148148

149149
let mut bcbs_with_coverage = BitSet::new_empty(num_bcbs);
150150
for covspan in coverage_spans {
151-
if let Some(bcb) = covspan.bcb {
152-
bcbs_with_coverage.insert(bcb);
153-
}
151+
bcbs_with_coverage.insert(covspan.bcb);
154152
}
155153

156154
// Walk the `CoverageGraph`. For each `BasicCoverageBlock` node with an associated

compiler/rustc_mir/src/transform/coverage/debug.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -657,19 +657,10 @@ fn span_viewables(
657657
for coverage_span in coverage_spans {
658658
let tooltip = coverage_span.format_coverage_statements(tcx, mir_body);
659659
let CoverageSpan { span, bcb, .. } = coverage_span;
660-
if let Some(bcb) = *bcb {
661-
let bcb_data = &basic_coverage_blocks[bcb];
662-
let id = bcb_data.id();
663-
let leader_bb = bcb_data.leader_bb();
664-
span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip });
665-
} else {
666-
span_viewables.push(SpanViewable {
667-
bb: mir::START_BLOCK,
668-
span: *span,
669-
id: String::from("Unreachable"),
670-
tooltip,
671-
});
672-
}
660+
let bcb_data = &basic_coverage_blocks[*bcb];
661+
let id = bcb_data.id();
662+
let leader_bb = bcb_data.leader_bb();
663+
span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip });
673664
}
674665
span_viewables
675666
}

compiler/rustc_mir/src/transform/coverage/graph.rs

+8-10
Original file line numberDiff line numberDiff line change
@@ -118,18 +118,8 @@ impl CoverageGraph {
118118

119119
match term.kind {
120120
TerminatorKind::Return { .. }
121-
// FIXME(richkadel): Add test(s) for `Abort` coverage.
122121
| TerminatorKind::Abort
123-
// FIXME(richkadel): Add test(s) for `Assert` coverage.
124-
// Should `Assert` be handled like `FalseUnwind` instead? Since we filter out unwind
125-
// branches when creating the BCB CFG, aren't `Assert`s (without unwinds) just like
126-
// `FalseUnwinds` (which are kind of like `Goto`s)?
127-
| TerminatorKind::Assert { .. }
128-
// FIXME(richkadel): Add test(s) for `Yield` coverage, and confirm coverage is
129-
// sensible for code using the `yield` keyword.
130122
| TerminatorKind::Yield { .. }
131-
// FIXME(richkadel): Also add coverage tests using async/await, and threading.
132-
133123
| TerminatorKind::SwitchInt { .. } => {
134124
// The `bb` has more than one _outgoing_ edge, or exits the function. Save the
135125
// current sequence of `basic_blocks` gathered to this point, as a new
@@ -147,13 +137,21 @@ impl CoverageGraph {
147137
// `Terminator`s `successors()` list) checking the number of successors won't
148138
// work.
149139
}
140+
// The following `TerminatorKind`s are either not expected outside an unwind branch,
141+
// or they should not (under normal circumstances) branch. Coverage graphs are
142+
// simplified by assuring coverage results are accurate for well-behaved programs.
143+
// Programs that panic and unwind may record slightly inaccurate coverage results
144+
// for a coverage region containing the `Terminator` that began the panic. This
145+
// is as intended. (See Issue #78544 for a possible future option to support
146+
// coverage in test programs that panic.)
150147
TerminatorKind::Goto { .. }
151148
| TerminatorKind::Resume
152149
| TerminatorKind::Unreachable
153150
| TerminatorKind::Drop { .. }
154151
| TerminatorKind::DropAndReplace { .. }
155152
| TerminatorKind::Call { .. }
156153
| TerminatorKind::GeneratorDrop
154+
| TerminatorKind::Assert { .. }
157155
| TerminatorKind::FalseEdge { .. }
158156
| TerminatorKind::FalseUnwind { .. }
159157
| TerminatorKind::InlineAsm { .. } => {}

0 commit comments

Comments
 (0)