rustc_utils/source_map/spanner/
hir_span.rs

1use hir::{HirId, LoopSource};
2use rustc_hir::{
3  self as hir, ExprKind, MatchSource, Node,
4  intravisit::{self, Visitor as HirVisitor},
5};
6use rustc_span::{BytePos, Span};
7
8use super::Spanner;
9use crate::SpanExt;
10
11// Collect all the spans for children beneath the visited node.
12// For example, when visiting "if true { 1 } else { 2 }" then we
13// should collect: "true" "1" "2"
14struct ChildExprSpans {
15  spans: Vec<Span>,
16  item_span: Span,
17}
18impl HirVisitor<'_> for ChildExprSpans {
19  fn visit_expr(&mut self, ex: &hir::Expr) {
20    match ex.kind {
21      // Don't take the span for the whole block, since we want to leave
22      // curly braces to be associated with the outer statement
23      ExprKind::Block(..) => {
24        intravisit::walk_expr(self, ex);
25      }
26      // ForLoopDesgar case:
27      //   The HIR span for a for-loop desugared to a match is *smaller*
28      //   than the span of its children. So we have to explicitly recurse
29      //   into the match arm instead of just taking the span for the match.
30      //   See `forloop_some_relevant` for where this matters.
31      //
32      // Normal case:
33      //   The SwitchInts for a normal match exclusively source-map to the patterns
34      //   in the arms, not the matched expression. So to make sure that `match e { .. }`
35      //   includes `e` when `match` is relevant, we exclude `e` from the child spans.
36      ExprKind::Match(_, arms, MatchSource::ForLoopDesugar | MatchSource::Normal) => {
37        for arm in arms {
38          intravisit::walk_arm(self, arm);
39        }
40      }
41      _ => {
42        if let Some(span) = ex.span.as_local(self.item_span) {
43          self.spans.push(span);
44        }
45      }
46    }
47  }
48
49  fn visit_arm(&mut self, arm: &hir::Arm) {
50    // We want the arm condition to be included in the outer span for the match,
51    // so we only visit the arm body here.
52    self.visit_expr(arm.body);
53  }
54
55  fn visit_stmt(&mut self, stmt: &hir::Stmt) {
56    if let Some(span) = stmt.span.as_local(self.item_span) {
57      self.spans.push(span);
58    }
59  }
60}
61
62/// Which parts of a HIR node's span should be included for a matching MIR node
63#[derive(Clone, Copy)]
64pub enum EnclosingHirSpans {
65  /// The entire span
66  Full,
67
68  /// The spans of the node minus its children
69  OuterOnly,
70
71  /// No span
72  None,
73}
74
75macro_rules! try_span {
76  ($self:expr, $span:expr) => {
77    match $span.as_local($self.item_span) {
78      Some(span) if !$self.invalid_span(span) => span,
79      _ => {
80        return None;
81      }
82    }
83  };
84}
85
86impl Spanner<'_> {
87  pub fn hir_spans(&self, id: HirId, mode: EnclosingHirSpans) -> Option<Vec<Span>> {
88    let span = try_span!(self, self.tcx.hir_span(id));
89    let inner_spans = match self.tcx.hir_node(id) {
90      Node::Expr(expr) => match expr.kind {
91        ExprKind::Loop(_, _, loop_source, header) => match loop_source {
92          LoopSource::ForLoop | LoopSource::While => {
93            vec![expr.span.trim_start(header).unwrap_or(expr.span)]
94          }
95
96          LoopSource::Loop => vec![expr.span.with_lo(expr.span.lo() + BytePos(4))],
97        },
98        ExprKind::Break(..) => return None,
99        _ => {
100          let mut visitor = ChildExprSpans {
101            spans: Vec::new(),
102            item_span: self.item_span,
103          };
104          intravisit::walk_expr(&mut visitor, expr);
105
106          visitor.spans
107        }
108      },
109      Node::Stmt(stmt) => {
110        let mut visitor = ChildExprSpans {
111          spans: Vec::new(),
112          item_span: self.item_span,
113        };
114        intravisit::walk_stmt(&mut visitor, stmt);
115        visitor.spans
116      }
117      Node::Param(_param) => vec![],
118      _ => {
119        return None;
120      }
121    };
122
123    Some(match mode {
124      EnclosingHirSpans::Full => vec![span],
125      EnclosingHirSpans::OuterOnly => span.subtract(inner_spans),
126      EnclosingHirSpans::None => vec![],
127    })
128  }
129}