rustc_mir_transform/coverage/unexpand.rs
1use rustc_span::{ExpnKind, Span};
2
3/// Walks through the expansion ancestors of `original_span` to find a span that
4/// is contained in `body_span` and has the same [syntax context] as `body_span`.
5pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> Option<Span> {
6 // Because we don't need to return any extra ancestor information,
7 // we can just delegate directly to `find_ancestor_inside_same_ctxt`.
8 original_span.find_ancestor_inside_same_ctxt(body_span)
9}
10
11/// Walks through the expansion ancestors of `original_span` to find a span that
12/// is contained in `body_span` and has the same [syntax context] as `body_span`.
13///
14/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
15/// the returned symbol will be the name of that macro (e.g. `foo`).
16pub(crate) fn unexpand_into_body_span_with_expn_kind(
17 original_span: Span,
18 body_span: Span,
19) -> Option<(Span, Option<ExpnKind>)> {
20 let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
21
22 let expn_kind = prev.map(|prev| prev.ctxt().outer_expn_data().kind);
23
24 Some((span, expn_kind))
25}
26
27/// Walks through the expansion ancestors of `original_span` to find a span that
28/// is contained in `body_span` and has the same [syntax context] as `body_span`.
29/// The ancestor that was traversed just before the matching span (if any) is
30/// also returned.
31///
32/// For example, a return value of `Some((ancestor, Some(prev)))` means that:
33/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
34/// - `prev.parent_callsite() == ancestor`
35///
36/// [syntax context]: rustc_span::SyntaxContext
37fn unexpand_into_body_span_with_prev(
38 original_span: Span,
39 body_span: Span,
40) -> Option<(Span, Option<Span>)> {
41 let mut prev = None;
42 let mut curr = original_span;
43
44 while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
45 prev = Some(curr);
46 curr = curr.parent_callsite()?;
47 }
48
49 debug_assert_eq!(Some(curr), original_span.find_ancestor_inside_same_ctxt(body_span));
50 if let Some(prev) = prev {
51 debug_assert_eq!(Some(curr), prev.parent_callsite());
52 }
53
54 Some((curr, prev))
55}