diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 65a0924f1c9fb..5f361fa09465b 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -116,6 +116,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { .make_bcb_counters(&self.basic_coverage_blocks, bcb_has_coverage_spans); let mappings = self.create_mappings_and_inject_coverage_statements(&coverage_spans); + if mappings.is_empty() { + return; + } self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: self.function_source_hash, @@ -144,18 +147,24 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Process the counters and spans associated with BCB nodes. for (bcb, counter_kind) in self.coverage_counters.bcb_node_counters() { - let spans = coverage_spans.spans_for_bcb(bcb); - let has_mappings = !spans.is_empty(); - // If this BCB has any coverage spans, add corresponding mappings to // the mappings table. - if has_mappings { + let has_mappings = { let term = counter_kind.as_term(); - mappings.extend(spans.iter().map(|&span| { - let code_region = make_code_region(source_map, file_name, span, body_span); - Mapping { code_region, term } + let old_mappings_len = mappings.len(); + + mappings.extend(coverage_spans.spans_for_bcb(bcb).iter().filter_map(|&span| { + let Some(code_region) = + make_code_region(source_map, file_name, span, body_span) + else { + debug!(?span, "Couldn't convert span to code region"); + return None; + }; + Some(Mapping { code_region, term }) })); - } + + mappings.len() > old_mappings_len + }; let do_inject = match counter_kind { // Counter-increment statements always need to be injected. @@ -247,13 +256,19 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb data.statements.insert(0, statement); } -/// Convert the Span into its file name, start line and column, and end line and column +/// Convert the Span into its file name, start line and column, and end line and column. +/// +/// Returns `None` if the conversion failed for some reason. There is no known example +/// of code that would cause this to happen, but it's hard to rule out entirely +/// (especially in the presence of complex macros or other expansions), and if it does +/// happen then skipping a span or function is better than an ICE that the user might +/// have no way to avoid. fn make_code_region( source_map: &SourceMap, file_name: Symbol, span: Span, body_span: Span, -) -> CodeRegion { +) -> Option { debug!( "Called make_code_region(file_name={}, span={}, body_span={})", file_name, @@ -275,13 +290,13 @@ fn make_code_region( start_line = source_map.doctest_offset_line(&file.name, start_line); end_line = source_map.doctest_offset_line(&file.name, end_line); } - CodeRegion { + Some(CodeRegion { file_name, start_line: start_line as u32, start_col: start_col as u32, end_line: end_line as u32, end_col: end_col as u32, - } + }) } fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {