rustc_span/
caching_source_map_view.rs1use std::ops::Range;
2use std::sync::Arc;
3
4use crate::source_map::SourceMap;
5use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData};
6
7pub struct CachingSourceMapView<'sm> {
13 source_map: &'sm SourceMap,
14 file: Arc<SourceFile>,
15 line_bounds: Range<BytePos>,
28 line_number: usize,
29}
30
31impl<'sm> CachingSourceMapView<'sm> {
32 pub fn new(source_map: &'sm SourceMap) -> CachingSourceMapView<'sm> {
33 let files = source_map.files();
34 let first_file = Arc::clone(&files[0]);
35 CachingSourceMapView {
36 source_map,
37 file: first_file,
38 line_bounds: BytePos(0)..BytePos(0),
39 line_number: 0,
40 }
41 }
42
43 #[inline]
44 fn pos_to_line(&self, pos: BytePos) -> (Range<BytePos>, usize) {
45 let pos = self.file.relative_position(pos);
46 let line_index = self.file.lookup_line(pos).unwrap();
47 let line_bounds = self.file.line_bounds(line_index);
48 let line_number = line_index + 1;
49 (line_bounds, line_number)
50 }
51
52 #[inline]
53 fn update(&mut self, new_file: Option<Arc<SourceFile>>, pos: BytePos) {
54 if let Some(file) = new_file {
55 self.file = file;
56 }
57 (self.line_bounds, self.line_number) = self.pos_to_line(pos);
58 }
59
60 pub fn byte_pos_to_line_and_col(
61 &mut self,
62 pos: BytePos,
63 ) -> Option<(Arc<SourceFile>, usize, RelativeBytePos)> {
64 if self.line_bounds.contains(&pos) {
65 } else {
67 let new_file = if !file_contains(&self.file, pos) {
70 Some(self.file_for_position(pos)?)
71 } else {
72 None
73 };
74 self.update(new_file, pos);
75 };
76
77 let col = RelativeBytePos(pos.to_u32() - self.line_bounds.start.to_u32());
78 Some((Arc::clone(&self.file), self.line_number, col))
79 }
80
81 pub fn span_data_to_lines_and_cols(
82 &mut self,
83 span_data: &SpanData,
84 ) -> Option<(&SourceFile, usize, BytePos, usize, BytePos)> {
85 let lo_hit = self.line_bounds.contains(&span_data.lo);
86 let hi_hit = self.line_bounds.contains(&span_data.hi);
87 if lo_hit && hi_hit {
88 return Some((
90 &self.file,
91 self.line_number,
92 span_data.lo - self.line_bounds.start,
93 self.line_number,
94 span_data.hi - self.line_bounds.start,
95 ));
96 }
97
98 let new_file = if !file_contains(&self.file, span_data.lo) {
101 let new_file = self.file_for_position(span_data.lo)?;
102 if !file_contains(&new_file, span_data.hi) {
103 return None;
104 }
105 Some(new_file)
106 } else {
107 if !file_contains(&self.file, span_data.hi) {
108 return None;
109 }
110 None
111 };
112
113 if !lo_hit {
116 self.update(new_file, span_data.lo);
118 }
119 let lo_line_bounds = &self.line_bounds;
120 let lo_line_number = self.line_number.clone();
121
122 let (hi_line_bounds, hi_line_number) = if !self.line_bounds.contains(&span_data.hi) {
123 self.pos_to_line(span_data.hi)
125 } else {
126 (self.line_bounds.clone(), self.line_number)
128 };
129
130 if !(span_data.lo >= lo_line_bounds.start) {
::core::panicking::panic("assertion failed: span_data.lo >= lo_line_bounds.start")
};assert!(span_data.lo >= lo_line_bounds.start);
133 if !(span_data.lo <= lo_line_bounds.end) {
::core::panicking::panic("assertion failed: span_data.lo <= lo_line_bounds.end")
};assert!(span_data.lo <= lo_line_bounds.end);
134 if !(span_data.hi >= hi_line_bounds.start) {
::core::panicking::panic("assertion failed: span_data.hi >= hi_line_bounds.start")
};assert!(span_data.hi >= hi_line_bounds.start);
135 if !(span_data.hi <= hi_line_bounds.end) {
::core::panicking::panic("assertion failed: span_data.hi <= hi_line_bounds.end")
};assert!(span_data.hi <= hi_line_bounds.end);
136 if !self.file.contains(span_data.lo) {
::core::panicking::panic("assertion failed: self.file.contains(span_data.lo)")
};assert!(self.file.contains(span_data.lo));
137 if !self.file.contains(span_data.hi) {
::core::panicking::panic("assertion failed: self.file.contains(span_data.hi)")
};assert!(self.file.contains(span_data.hi));
138
139 Some((
140 &self.file,
141 lo_line_number,
142 span_data.lo - lo_line_bounds.start,
143 hi_line_number,
144 span_data.hi - hi_line_bounds.start,
145 ))
146 }
147
148 fn file_for_position(&self, pos: BytePos) -> Option<Arc<SourceFile>> {
149 if !self.source_map.files().is_empty() {
150 let file_idx = self.source_map.lookup_source_file_idx(pos);
151 let file = &self.source_map.files()[file_idx];
152
153 if file_contains(file, pos) {
154 return Some(Arc::clone(file));
155 }
156 }
157
158 None
159 }
160}
161
162#[inline]
163fn file_contains(file: &SourceFile, pos: BytePos) -> bool {
164 file.contains(pos) && !file.is_empty()
170}