rustc_utils/source_map/spanner/
hir_span.rs1use 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
11struct 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 ExprKind::Block(..) => {
24 intravisit::walk_expr(self, ex);
25 }
26 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 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#[derive(Clone, Copy)]
64pub enum EnclosingHirSpans {
65 Full,
67
68 OuterOnly,
70
71 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}