Skip to content

Commit e255871

Browse files
committed
coverage: Allow make_code_region to fail gracefully
Currently there's no way for this function to return `None`, but the next patch will make it potentially able do so in some unknown corner-cases. If it does fail, then skipping a span or function is better than causing an ICE that the user might have no way to avoid without disabling coverage entirely.
1 parent b8b0800 commit e255871

File tree

1 file changed

+27
-12
lines changed
  • compiler/rustc_mir_transform/src/coverage

1 file changed

+27
-12
lines changed

compiler/rustc_mir_transform/src/coverage/mod.rs

+27-12
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
107107
.make_bcb_counters(&self.basic_coverage_blocks, bcb_has_coverage_spans);
108108

109109
let mappings = self.create_mappings_and_inject_coverage_statements(&coverage_spans);
110+
if mappings.is_empty() {
111+
return;
112+
}
110113

111114
self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
112115
function_source_hash: self.hir_info.function_source_hash,
@@ -135,18 +138,24 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
135138

136139
// Process the counters and spans associated with BCB nodes.
137140
for (bcb, counter_kind) in self.coverage_counters.bcb_node_counters() {
138-
let spans = coverage_spans.spans_for_bcb(bcb);
139-
let has_mappings = !spans.is_empty();
140-
141141
// If this BCB has any coverage spans, add corresponding mappings to
142142
// the mappings table.
143-
if has_mappings {
143+
let has_mappings = {
144144
let term = counter_kind.as_term();
145-
mappings.extend(spans.iter().map(|&span| {
146-
let code_region = make_code_region(source_map, file_name, span, body_span);
147-
Mapping { code_region, term }
145+
let old_mappings_len = mappings.len();
146+
147+
mappings.extend(coverage_spans.spans_for_bcb(bcb).iter().filter_map(|&span| {
148+
let Some(code_region) =
149+
make_code_region(source_map, file_name, span, body_span)
150+
else {
151+
debug!(?span, "Couldn't convert span to code region");
152+
return None;
153+
};
154+
Some(Mapping { code_region, term })
148155
}));
149-
}
156+
157+
mappings.len() > old_mappings_len
158+
};
150159

151160
let do_inject = match counter_kind {
152161
// Counter-increment statements always need to be injected.
@@ -238,13 +247,19 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb
238247
data.statements.insert(0, statement);
239248
}
240249

241-
/// Convert the Span into its file name, start line and column, and end line and column
250+
/// Convert the Span into its file name, start line and column, and end line and column.
251+
///
252+
/// Returns `None` if the conversion failed for some reason. There is no known example
253+
/// of code that would cause this to happen, but it's hard to rule out entirely
254+
/// (especially in the presence of complex macros or other expansions), and if it does
255+
/// happen then skipping a span or function is better than an ICE that the user might
256+
/// have no way to avoid.
242257
fn make_code_region(
243258
source_map: &SourceMap,
244259
file_name: Symbol,
245260
span: Span,
246261
body_span: Span,
247-
) -> CodeRegion {
262+
) -> Option<CodeRegion> {
248263
debug!(
249264
"Called make_code_region(file_name={}, span={}, body_span={})",
250265
file_name,
@@ -266,13 +281,13 @@ fn make_code_region(
266281
start_line = source_map.doctest_offset_line(&file.name, start_line);
267282
end_line = source_map.doctest_offset_line(&file.name, end_line);
268283
}
269-
CodeRegion {
284+
Some(CodeRegion {
270285
file_name,
271286
start_line: start_line as u32,
272287
start_col: start_col as u32,
273288
end_line: end_line as u32,
274289
end_col: end_col as u32,
275-
}
290+
})
276291
}
277292

278293
fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {

0 commit comments

Comments
 (0)