diff options
| author | bors <bors@rust-lang.org> | 2024-01-06 06:00:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-01-06 06:00:27 +0000 |
| commit | aa7e9f21e9a058a4822b6cdc19ee88c80cdb3750 (patch) | |
| tree | 66a18845a73790a059b193a87292e6b4c211133d /compiler/rustc_mir_transform/src/coverage | |
| parent | d62f05b842d94d3bcad4d41d4b81df3949bad7c6 (diff) | |
| parent | 71610e2eb66dbcda5342700c8bfad12f89756054 (diff) | |
| download | rust-aa7e9f21e9a058a4822b6cdc19ee88c80cdb3750.tar.gz rust-aa7e9f21e9a058a4822b6cdc19ee88c80cdb3750.zip | |
Auto merge of #119648 - compiler-errors:rollup-42inxd8, r=compiler-errors
Rollup of 9 pull requests Successful merges: - #119208 (coverage: Hoist some complex code out of the main span refinement loop) - #119216 (Use diagnostic namespace in stdlib) - #119414 (bootstrap: Move -Clto= setting from Rustc::run to rustc_cargo) - #119420 (Handle ForeignItem as TAIT scope.) - #119468 (rustdoc-search: tighter encoding for f index) - #119628 (remove duplicate test) - #119638 (fix cyle error when suggesting to use associated function instead of constructor) - #119640 (library: Fix warnings in rtstartup) - #119642 (library: Fix a symlink test failing on Windows) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_mir_transform/src/coverage')
| -rw-r--r-- | compiler/rustc_mir_transform/src/coverage/spans.rs | 140 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs | 189 |
2 files changed, 175 insertions, 154 deletions
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index ed091752187..5983189984d 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,11 +1,9 @@ -use std::cell::OnceCell; - use rustc_data_structures::graph::WithNumNodes; use rustc_index::IndexVec; use rustc_middle::mir; -use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP}; +use rustc_span::{BytePos, Span, DUMMY_SP}; -use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; +use super::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::ExtractedHirInfo; mod from_mir; @@ -70,35 +68,17 @@ impl CoverageSpans { /// `dominates()` the `BasicBlock`s in this `CoverageSpan`. #[derive(Debug, Clone)] struct CoverageSpan { - pub span: Span, - pub expn_span: Span, - pub current_macro_or_none: OnceCell<Option<Symbol>>, - pub bcb: BasicCoverageBlock, + span: Span, + bcb: BasicCoverageBlock, /// List of all the original spans from MIR that have been merged into this /// span. Mainly used to precisely skip over gaps when truncating a span. - pub merged_spans: Vec<Span>, - pub is_closure: bool, + merged_spans: Vec<Span>, + is_closure: bool, } impl CoverageSpan { - pub fn for_fn_sig(fn_sig_span: Span) -> Self { - Self::new(fn_sig_span, fn_sig_span, START_BCB, false) - } - - pub(super) fn new( - span: Span, - expn_span: Span, - bcb: BasicCoverageBlock, - is_closure: bool, - ) -> Self { - Self { - span, - expn_span, - current_macro_or_none: Default::default(), - bcb, - merged_spans: vec![span], - is_closure, - } + fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self { + Self { span, bcb, merged_spans: vec![span], is_closure } } pub fn merge_from(&mut self, other: &Self) { @@ -123,37 +103,6 @@ impl CoverageSpan { pub fn is_in_same_bcb(&self, other: &Self) -> bool { self.bcb == other.bcb } - - /// If the span is part of a macro, returns the macro name symbol. - pub fn current_macro(&self) -> Option<Symbol> { - self.current_macro_or_none - .get_or_init(|| { - if let ExpnKind::Macro(MacroKind::Bang, current_macro) = - self.expn_span.ctxt().outer_expn_data().kind - { - return Some(current_macro); - } - None - }) - .map(|symbol| symbol) - } - - /// If the span is part of a macro, and the macro is visible (expands directly to the given - /// body_span), returns the macro name symbol. - pub fn visible_macro(&self, body_span: Span) -> Option<Symbol> { - let current_macro = self.current_macro()?; - let parent_callsite = self.expn_span.parent_callsite()?; - - // In addition to matching the context of the body span, the parent callsite - // must also be the source callsite, i.e. the parent must have no parent. - let is_visible_macro = - parent_callsite.parent_callsite().is_none() && parent_callsite.eq_ctxt(body_span); - is_visible_macro.then_some(current_macro) - } - - pub fn is_macro_expansion(&self) -> bool { - self.current_macro().is_some() - } } /// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a @@ -164,10 +113,6 @@ impl CoverageSpan { /// execution /// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) struct CoverageSpansGenerator<'a> { - /// A `Span` covering the function body of the MIR (typically from left curly brace to right - /// curly brace). - body_span: Span, - /// The BasicCoverageBlock Control Flow Graph (BCB CFG). basic_coverage_blocks: &'a CoverageGraph, @@ -244,7 +189,6 @@ impl<'a> CoverageSpansGenerator<'a> { ); let coverage_spans = Self { - body_span: hir_info.body_span, basic_coverage_blocks, sorted_spans_iter: sorted_spans.into_iter(), some_curr: None, @@ -266,7 +210,6 @@ impl<'a> CoverageSpansGenerator<'a> { // span-processing steps don't make sense yet. if self.some_prev.is_none() { debug!(" initial span"); - self.maybe_push_macro_name_span(); continue; } @@ -278,7 +221,6 @@ impl<'a> CoverageSpansGenerator<'a> { debug!(" same bcb (and neither is a closure), merge with prev={prev:?}"); let prev = self.take_prev(); self.curr_mut().merge_from(&prev); - self.maybe_push_macro_name_span(); // Note that curr.span may now differ from curr_original_span } else if prev.span.hi() <= curr.span.lo() { debug!( @@ -286,7 +228,6 @@ impl<'a> CoverageSpansGenerator<'a> { ); let prev = self.take_prev(); self.refined_spans.push(prev); - self.maybe_push_macro_name_span(); } else if prev.is_closure { // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the // next iter @@ -297,35 +238,11 @@ impl<'a> CoverageSpansGenerator<'a> { } else if curr.is_closure { self.carve_out_span_for_closure(); } else if self.prev_original_span == curr.span { - // Note that this compares the new (`curr`) span to `prev_original_span`. - // In this branch, the actual span byte range of `prev_original_span` is not - // important. What is important is knowing whether the new `curr` span was - // **originally** the same as the original span of `prev()`. The original spans - // reflect their original sort order, and for equal spans, conveys a partial - // ordering based on CFG dominator priority. - if prev.is_macro_expansion() && curr.is_macro_expansion() { - // Macros that expand to include branching (such as - // `assert_eq!()`, `assert_ne!()`, `info!()`, `debug!()`, or - // `trace!()`) typically generate callee spans with identical - // ranges (typically the full span of the macro) for all - // `BasicBlocks`. This makes it impossible to distinguish - // the condition (`if val1 != val2`) from the optional - // branched statements (such as the call to `panic!()` on - // assert failure). In this case it is better (or less - // worse) to drop the optional branch bcbs and keep the - // non-conditional statements, to count when reached. - debug!( - " curr and prev are part of a macro expansion, and curr has the same span \ - as prev, but is in a different bcb. Drop curr and keep prev for next iter. \ - prev={prev:?}", - ); - self.take_curr(); // Discards curr. - } else { - self.update_pending_dups(); - } + // `prev` and `curr` have the same span, or would have had the + // same span before `prev` was modified by other spans. + self.update_pending_dups(); } else { self.cutoff_prev_at_overlapping_curr(); - self.maybe_push_macro_name_span(); } } @@ -360,41 +277,6 @@ impl<'a> CoverageSpansGenerator<'a> { self.refined_spans } - /// If `curr` is part of a new macro expansion, carve out and push a separate - /// span that ends just after the macro name and its subsequent `!`. - fn maybe_push_macro_name_span(&mut self) { - let curr = self.curr(); - - let Some(visible_macro) = curr.visible_macro(self.body_span) else { return }; - if let Some(prev) = &self.some_prev - && prev.expn_span.eq_ctxt(curr.expn_span) - { - return; - } - - // The split point is relative to `curr_original_span`, - // because `curr.span` may have been merged with preceding spans. - let split_point_after_macro_bang = self.curr_original_span.lo() - + BytePos(visible_macro.as_str().len() as u32) - + BytePos(1); // add 1 for the `!` - debug_assert!(split_point_after_macro_bang <= curr.span.hi()); - if split_point_after_macro_bang > curr.span.hi() { - // Something is wrong with the macro name span; - // return now to avoid emitting malformed mappings (e.g. #117788). - return; - } - - let mut macro_name_cov = curr.clone(); - macro_name_cov.span = macro_name_cov.span.with_hi(split_point_after_macro_bang); - self.curr_mut().span = curr.span.with_lo(split_point_after_macro_bang); - - debug!( - " and curr starts a new macro expansion, so add a new span just for \ - the macro `{visible_macro}!`, new span={macro_name_cov:?}", - ); - self.refined_spans.push(macro_name_cov); - } - #[track_caller] fn curr(&self) -> &CoverageSpan { self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)")) diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 8f6592afe85..1b6dfccd574 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,11 +1,14 @@ use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{ self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_span::Span; +use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; -use crate::coverage::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use crate::coverage::graph::{ + BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, +}; use crate::coverage::spans::CoverageSpan; use crate::coverage::ExtractedHirInfo; @@ -15,26 +18,29 @@ pub(super) fn mir_to_initial_sorted_coverage_spans( basic_coverage_blocks: &CoverageGraph, ) -> Vec<CoverageSpan> { let &ExtractedHirInfo { is_async_fn, fn_sig_span, body_span, .. } = hir_info; + + let mut initial_spans = vec![SpanFromMir::for_fn_sig(fn_sig_span)]; + if is_async_fn { // An async function desugars into a function that returns a future, // with the user code wrapped in a closure. Any spans in the desugared - // outer function will be unhelpful, so just produce a single span - // associating the function signature with its entry BCB. - return vec![CoverageSpan::for_fn_sig(fn_sig_span)]; - } - - let mut initial_spans = Vec::with_capacity(mir_body.basic_blocks.len() * 2); - for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() { - initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data)); - } + // outer function will be unhelpful, so just keep the signature span + // and ignore all of the spans in the MIR body. + } else { + for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() { + initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data)); + } - if initial_spans.is_empty() { - // This can happen if, for example, the function is unreachable (contains only a - // `BasicBlock`(s) with an `Unreachable` terminator). - return initial_spans; + // If no spans were extracted from the body, discard the signature span. + // FIXME: This preserves existing behavior; consider getting rid of it. + if initial_spans.len() == 1 { + initial_spans.clear(); + } } - initial_spans.push(CoverageSpan::for_fn_sig(fn_sig_span)); + initial_spans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); + remove_unwanted_macro_spans(&mut initial_spans); + split_visible_macro_spans(&mut initial_spans); initial_spans.sort_by(|a, b| { // First sort by span start. @@ -53,7 +59,62 @@ pub(super) fn mir_to_initial_sorted_coverage_spans( .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse()) }); - initial_spans + initial_spans.into_iter().map(SpanFromMir::into_coverage_span).collect::<Vec<_>>() +} + +/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate +/// multiple condition/consequent blocks that have the span of the whole macro +/// invocation, which is unhelpful. Keeping only the first such span seems to +/// give better mappings, so remove the others. +/// +/// (The input spans should be sorted in BCB dominator order, so that the +/// retained "first" span is likely to dominate the others.) +fn remove_unwanted_macro_spans(initial_spans: &mut Vec<SpanFromMir>) { + let mut seen_macro_spans = FxHashSet::default(); + initial_spans.retain(|covspan| { + // Ignore (retain) closure spans and non-macro-expansion spans. + if covspan.is_closure || covspan.visible_macro.is_none() { + return true; + } + + // Retain only the first macro-expanded covspan with this span. + seen_macro_spans.insert(covspan.span) + }); +} + +/// When a span corresponds to a macro invocation that is visible from the +/// function body, split it into two parts. The first part covers just the +/// macro name plus `!`, and the second part covers the rest of the macro +/// invocation. This seems to give better results for code that uses macros. +fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) { + let mut extra_spans = vec![]; + + initial_spans.retain(|covspan| { + if covspan.is_closure { + return true; + } + + let Some(visible_macro) = covspan.visible_macro else { return true }; + + let split_len = visible_macro.as_str().len() as u32 + 1; + let (before, after) = covspan.span.split_at(split_len); + if !covspan.span.contains(before) || !covspan.span.contains(after) { + // Something is unexpectedly wrong with the split point. + // The debug assertion in `split_at` will have already caught this, + // but in release builds it's safer to do nothing and maybe get a + // bug report for unexpected coverage, rather than risk an ICE. + return true; + } + + assert!(!covspan.is_closure); + extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb, false)); + extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb, false)); + false // Discard the original covspan that we just split. + }); + + // The newly-split spans are added at the end, so any previous sorting + // is not preserved. + initial_spans.extend(extra_spans); } // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of @@ -66,22 +127,24 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( body_span: Span, bcb: BasicCoverageBlock, bcb_data: &'a BasicCoverageBlockData, -) -> impl Iterator<Item = CoverageSpan> + Captures<'a> + Captures<'tcx> { +) -> impl Iterator<Item = SpanFromMir> + Captures<'a> + Captures<'tcx> { bcb_data.basic_blocks.iter().flat_map(move |&bb| { let data = &mir_body[bb]; let statement_spans = data.statements.iter().filter_map(move |statement| { let expn_span = filtered_statement_span(statement)?; - let span = unexpand_into_body_span(expn_span, body_span)?; + let (span, visible_macro) = + unexpand_into_body_span_with_visible_macro(expn_span, body_span)?; - Some(CoverageSpan::new(span, expn_span, bcb, is_closure_or_coroutine(statement))) + Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_or_coroutine(statement))) }); let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| { let expn_span = filtered_terminator_span(terminator)?; - let span = unexpand_into_body_span(expn_span, body_span)?; + let (span, visible_macro) = + unexpand_into_body_span_with_visible_macro(expn_span, body_span)?; - Some(CoverageSpan::new(span, expn_span, bcb, false)) + Some(SpanFromMir::new(span, visible_macro, bcb, false)) }); statement_spans.chain(terminator_span) @@ -202,7 +265,83 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> { /// /// [^1]Expansions result from Rust syntax including macros, syntactic sugar, /// etc.). -#[inline] -fn unexpand_into_body_span(span: Span, body_span: Span) -> Option<Span> { - span.find_ancestor_inside_same_ctxt(body_span) +fn unexpand_into_body_span_with_visible_macro( + original_span: Span, + body_span: Span, +) -> Option<(Span, Option<Symbol>)> { + let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?; + + let visible_macro = prev + .map(|prev| match prev.ctxt().outer_expn_data().kind { + ExpnKind::Macro(MacroKind::Bang, name) => Some(name), + _ => None, + }) + .flatten(); + + Some((span, visible_macro)) +} + +/// Walks through the expansion ancestors of `original_span` to find a span that +/// is contained in `body_span` and has the same [`SyntaxContext`] as `body_span`. +/// The ancestor that was traversed just before the matching span (if any) is +/// also returned. +/// +/// For example, a return value of `Some((ancestor, Some(prev))` means that: +/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)` +/// - `ancestor == prev.parent_callsite()` +/// +/// [`SyntaxContext`]: rustc_span::SyntaxContext +fn unexpand_into_body_span_with_prev( + original_span: Span, + body_span: Span, +) -> Option<(Span, Option<Span>)> { + let mut prev = None; + let mut curr = original_span; + + while !body_span.contains(curr) || !curr.eq_ctxt(body_span) { + prev = Some(curr); + curr = curr.parent_callsite()?; + } + + debug_assert_eq!(Some(curr), original_span.find_ancestor_in_same_ctxt(body_span)); + if let Some(prev) = prev { + debug_assert_eq!(Some(curr), prev.parent_callsite()); + } + + Some((curr, prev)) +} + +#[derive(Debug)] +struct SpanFromMir { + /// A span that has been extracted from MIR and then "un-expanded" back to + /// within the current function's `body_span`. After various intermediate + /// processing steps, this span is emitted as part of the final coverage + /// mappings. + /// + /// With the exception of `fn_sig_span`, this should always be contained + /// within `body_span`. + span: Span, + visible_macro: Option<Symbol>, + bcb: BasicCoverageBlock, + is_closure: bool, +} + +impl SpanFromMir { + fn for_fn_sig(fn_sig_span: Span) -> Self { + Self::new(fn_sig_span, None, START_BCB, false) + } + + fn new( + span: Span, + visible_macro: Option<Symbol>, + bcb: BasicCoverageBlock, + is_closure: bool, + ) -> Self { + Self { span, visible_macro, bcb, is_closure } + } + + fn into_coverage_span(self) -> CoverageSpan { + let Self { span, visible_macro: _, bcb, is_closure } = self; + CoverageSpan::new(span, bcb, is_closure) + } } |
