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}