rustc_utils/source_map/
span.rs1use std::cmp;
2
3use log::trace;
4use rustc_middle::ty::TyCtxt;
5use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, source_map::SourceMap};
6
7pub trait SpanExt {
9 fn subtract(&self, child_spans: Vec<Span>) -> Vec<Span>;
18
19 fn as_local(&self, outer_span: Span) -> Option<Span>;
22
23 fn overlaps_inclusive(&self, other: Span) -> bool;
25
26 fn trim_end(&self, other: Span) -> Option<Span>;
29
30 fn merge_overlaps(spans: Vec<Span>) -> Vec<Span>;
32
33 fn trim_leading_whitespace(&self, source_map: &SourceMap) -> Option<Vec<Span>>;
37
38 fn to_string(&self, tcx: TyCtxt<'_>) -> String;
40
41 fn size(&self) -> u32;
43}
44
45impl SpanExt for Span {
46 fn trim_end(&self, other: Span) -> Option<Span> {
47 let span = self.data();
48 let other = other.data();
49 if span.lo < other.lo {
50 Some(span.with_hi(cmp::min(span.hi, other.lo)))
51 } else {
52 None
53 }
54 }
55
56 fn subtract(&self, mut child_spans: Vec<Span>) -> Vec<Span> {
57 child_spans.retain(|s| s.overlaps_inclusive(*self));
58
59 let mut outer_spans = vec![];
60 if !child_spans.is_empty() {
61 child_spans = Span::merge_overlaps(child_spans);
63
64 if let Some(start) = self.trim_end(*child_spans.first().unwrap()) {
65 outer_spans.push(start);
66 }
67
68 for children in child_spans.windows(2) {
69 outer_spans.push(children[0].between(children[1]));
70 }
71
72 if let Some(end) = self.trim_start(*child_spans.last().unwrap()) {
73 outer_spans.push(end);
74 }
75 } else {
76 outer_spans.push(*self);
77 }
78
79 trace!("outer span for {self:?} with inner spans {child_spans:?} is {outer_spans:?}");
80
81 outer_spans
82 }
83
84 fn as_local(&self, outer_span: Span) -> Option<Span> {
85 if outer_span.contains(*self) {
89 return Some(*self);
90 }
91
92 let sp = self.source_callsite();
93 if outer_span.contains(sp) {
94 return Some(sp);
95 }
96
97 None
98 }
99
100 fn overlaps_inclusive(&self, other: Span) -> bool {
101 let s1 = self.data();
102 let s2 = other.data();
103 s1.lo <= s2.hi && s2.lo <= s1.hi
104 }
105
106 fn merge_overlaps(mut spans: Vec<Span>) -> Vec<Span> {
107 spans.sort_by_key(|s| (s.lo(), s.hi()));
108
109 for span in &mut spans {
111 *span = span.with_ctxt(SyntaxContext::root());
112 }
113
114 let mut output = Vec::new();
115 for span in spans {
116 match output
117 .iter_mut()
118 .find(|other| span.overlaps_inclusive(**other))
119 {
120 Some(other) => {
121 *other = span.to(*other);
122 }
123 None => {
124 output.push(span);
125 }
126 }
127 }
128 output
129 }
130
131 fn to_string(&self, tcx: TyCtxt<'_>) -> String {
132 let source_map = tcx.sess.source_map();
133 let lo = source_map.lookup_char_pos(self.lo());
134 let hi = source_map.lookup_char_pos(self.hi());
135 let snippet = source_map.span_to_snippet(*self).unwrap();
136 format!(
137 "{snippet} ({}:{}-{}:{})",
138 lo.line,
139 lo.col.to_usize() + 1,
140 hi.line,
141 hi.col.to_usize() + 1
142 )
143 }
144
145 fn size(&self) -> u32 {
146 self.hi().0 - self.lo().0
147 }
148
149 fn trim_leading_whitespace(&self, source_map: &SourceMap) -> Option<Vec<Span>> {
150 let snippet = source_map.span_to_snippet(*self).ok()?;
151 let mut spans = Vec::new();
152 let mut start = self.lo();
153 for line in snippet.split('\n') {
154 let offset = line
155 .chars()
156 .take_while(|c| c.is_whitespace())
157 .map(char::len_utf8)
158 .sum::<usize>();
159 let end = (start + BytePos(u32::try_from(line.len()).unwrap())).min(self.hi());
160 spans.push(
161 self
162 .with_lo(start + BytePos(u32::try_from(offset).unwrap()))
163 .with_hi(end),
164 );
165 start = end + BytePos(1);
166 }
167 Some(spans)
168 }
169}
170
171pub trait SpanDataExt {
173 fn size(&self) -> u32;
175}
176
177impl SpanDataExt for SpanData {
178 fn size(&self) -> u32 {
179 self.hi.0 - self.lo.0
180 }
181}
182
183#[cfg(test)]
184mod test {
185 use rustc_span::BytePos;
186
187 use super::*;
188
189 #[test]
190 fn test_span_subtract() {
191 rustc_span::create_default_session_globals_then(|| {
192 let mk = |lo, hi| Span::with_root_ctxt(BytePos(lo), BytePos(hi));
193 let outer = mk(1, 10);
194 let inner: Vec<Span> = vec![mk(0, 2), mk(3, 4), mk(3, 5), mk(7, 8), mk(9, 13)];
195 let desired: Vec<Span> = vec![mk(2, 3), mk(5, 7), mk(8, 9)];
196 assert_eq!(outer.subtract(inner), desired);
197 });
198 }
199}