]>
Commit | Line | Data |
---|---|---|
416331ca XL |
1 | use super::*; |
2 | ||
3 | use rustc_data_structures::sync::Lrc; | |
4 | ||
5 | fn init_source_map() -> SourceMap { | |
6 | let sm = SourceMap::new(FilePathMapping::empty()); | |
dfeec247 XL |
7 | sm.new_source_file(PathBuf::from("blork.rs").into(), "first line.\nsecond line".to_string()); |
8 | sm.new_source_file(PathBuf::from("empty.rs").into(), String::new()); | |
9 | sm.new_source_file(PathBuf::from("blork2.rs").into(), "first line.\nsecond line".to_string()); | |
416331ca XL |
10 | sm |
11 | } | |
12 | ||
e1599b0c | 13 | /// Tests `lookup_byte_offset`. |
416331ca XL |
14 | #[test] |
15 | fn t3() { | |
416331ca XL |
16 | let sm = init_source_map(); |
17 | ||
18 | let srcfbp1 = sm.lookup_byte_offset(BytePos(23)); | |
19 | assert_eq!(srcfbp1.sf.name, PathBuf::from("blork.rs").into()); | |
20 | assert_eq!(srcfbp1.pos, BytePos(23)); | |
21 | ||
22 | let srcfbp1 = sm.lookup_byte_offset(BytePos(24)); | |
23 | assert_eq!(srcfbp1.sf.name, PathBuf::from("empty.rs").into()); | |
24 | assert_eq!(srcfbp1.pos, BytePos(0)); | |
25 | ||
26 | let srcfbp2 = sm.lookup_byte_offset(BytePos(25)); | |
27 | assert_eq!(srcfbp2.sf.name, PathBuf::from("blork2.rs").into()); | |
28 | assert_eq!(srcfbp2.pos, BytePos(0)); | |
29 | } | |
30 | ||
e1599b0c | 31 | /// Tests `bytepos_to_file_charpos`. |
416331ca XL |
32 | #[test] |
33 | fn t4() { | |
416331ca XL |
34 | let sm = init_source_map(); |
35 | ||
36 | let cp1 = sm.bytepos_to_file_charpos(BytePos(22)); | |
37 | assert_eq!(cp1, CharPos(22)); | |
38 | ||
39 | let cp2 = sm.bytepos_to_file_charpos(BytePos(25)); | |
40 | assert_eq!(cp2, CharPos(0)); | |
41 | } | |
42 | ||
e1599b0c | 43 | /// Tests zero-length `SourceFile`s. |
416331ca XL |
44 | #[test] |
45 | fn t5() { | |
416331ca XL |
46 | let sm = init_source_map(); |
47 | ||
48 | let loc1 = sm.lookup_char_pos(BytePos(22)); | |
49 | assert_eq!(loc1.file.name, PathBuf::from("blork.rs").into()); | |
50 | assert_eq!(loc1.line, 2); | |
51 | assert_eq!(loc1.col, CharPos(10)); | |
52 | ||
53 | let loc2 = sm.lookup_char_pos(BytePos(25)); | |
54 | assert_eq!(loc2.file.name, PathBuf::from("blork2.rs").into()); | |
55 | assert_eq!(loc2.line, 1); | |
56 | assert_eq!(loc2.col, CharPos(0)); | |
57 | } | |
58 | ||
59 | fn init_source_map_mbc() -> SourceMap { | |
60 | let sm = SourceMap::new(FilePathMapping::empty()); | |
e1599b0c | 61 | // "€" is a three-byte UTF8 char. |
dfeec247 XL |
62 | sm.new_source_file( |
63 | PathBuf::from("blork.rs").into(), | |
64 | "fir€st €€€€ line.\nsecond line".to_string(), | |
65 | ); | |
66 | sm.new_source_file( | |
67 | PathBuf::from("blork2.rs").into(), | |
68 | "first line€€.\n€ second line".to_string(), | |
69 | ); | |
416331ca XL |
70 | sm |
71 | } | |
72 | ||
e1599b0c | 73 | /// Tests `bytepos_to_file_charpos` in the presence of multi-byte chars. |
416331ca XL |
74 | #[test] |
75 | fn t6() { | |
416331ca XL |
76 | let sm = init_source_map_mbc(); |
77 | ||
78 | let cp1 = sm.bytepos_to_file_charpos(BytePos(3)); | |
79 | assert_eq!(cp1, CharPos(3)); | |
80 | ||
81 | let cp2 = sm.bytepos_to_file_charpos(BytePos(6)); | |
82 | assert_eq!(cp2, CharPos(4)); | |
83 | ||
84 | let cp3 = sm.bytepos_to_file_charpos(BytePos(56)); | |
85 | assert_eq!(cp3, CharPos(12)); | |
86 | ||
87 | let cp4 = sm.bytepos_to_file_charpos(BytePos(61)); | |
88 | assert_eq!(cp4, CharPos(15)); | |
89 | } | |
90 | ||
e1599b0c | 91 | /// Test `span_to_lines` for a span ending at the end of a `SourceFile`. |
416331ca XL |
92 | #[test] |
93 | fn t7() { | |
416331ca | 94 | let sm = init_source_map(); |
e1599b0c | 95 | let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); |
416331ca XL |
96 | let file_lines = sm.span_to_lines(span).unwrap(); |
97 | ||
98 | assert_eq!(file_lines.file.name, PathBuf::from("blork.rs").into()); | |
99 | assert_eq!(file_lines.lines.len(), 1); | |
100 | assert_eq!(file_lines.lines[0].line_index, 1); | |
101 | } | |
102 | ||
103 | /// Given a string like " ~~~~~~~~~~~~ ", produces a span | |
104 | /// converting that range. The idea is that the string has the same | |
105 | /// length as the input, and we uncover the byte positions. Note | |
106 | /// that this can span lines and so on. | |
107 | fn span_from_selection(input: &str, selection: &str) -> Span { | |
108 | assert_eq!(input.len(), selection.len()); | |
109 | let left_index = selection.find('~').unwrap() as u32; | |
5869c6ff | 110 | let right_index = selection.rfind('~').map_or(left_index, |x| x as u32); |
e1599b0c | 111 | Span::with_root_ctxt(BytePos(left_index), BytePos(right_index + 1)) |
416331ca XL |
112 | } |
113 | ||
e1599b0c | 114 | /// Tests `span_to_snippet` and `span_to_lines` for a span converting 3 |
416331ca XL |
115 | /// lines in the middle of a file. |
116 | #[test] | |
117 | fn span_to_snippet_and_lines_spanning_multiple_lines() { | |
118 | let sm = SourceMap::new(FilePathMapping::empty()); | |
119 | let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; | |
120 | let selection = " \n ~~\n~~~\n~~~~~ \n \n"; | |
121 | sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_string()); | |
122 | let span = span_from_selection(inputtext, selection); | |
123 | ||
e1599b0c | 124 | // Check that we are extracting the text we thought we were extracting. |
416331ca XL |
125 | assert_eq!(&sm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD"); |
126 | ||
e1599b0c | 127 | // Check that span_to_lines gives us the complete result with the lines/cols we expected. |
416331ca XL |
128 | let lines = sm.span_to_lines(span).unwrap(); |
129 | let expected = vec![ | |
130 | LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) }, | |
131 | LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) }, | |
dfeec247 XL |
132 | LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }, |
133 | ]; | |
416331ca XL |
134 | assert_eq!(lines.lines, expected); |
135 | } | |
136 | ||
e1599b0c | 137 | /// Test span_to_snippet for a span ending at the end of a `SourceFile`. |
416331ca XL |
138 | #[test] |
139 | fn t8() { | |
416331ca | 140 | let sm = init_source_map(); |
e1599b0c | 141 | let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); |
416331ca XL |
142 | let snippet = sm.span_to_snippet(span); |
143 | ||
144 | assert_eq!(snippet, Ok("second line".to_string())); | |
145 | } | |
146 | ||
e1599b0c | 147 | /// Test `span_to_str` for a span ending at the end of a `SourceFile`. |
416331ca XL |
148 | #[test] |
149 | fn t9() { | |
416331ca | 150 | let sm = init_source_map(); |
e1599b0c | 151 | let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); |
dfeec247 | 152 | let sstr = sm.span_to_string(span); |
416331ca XL |
153 | |
154 | assert_eq!(sstr, "blork.rs:2:1: 2:12"); | |
155 | } | |
156 | ||
e1599b0c | 157 | /// Tests failing to merge two spans on different lines. |
416331ca XL |
158 | #[test] |
159 | fn span_merging_fail() { | |
160 | let sm = SourceMap::new(FilePathMapping::empty()); | |
dfeec247 | 161 | let inputtext = "bbbb BB\ncc CCC\n"; |
416331ca XL |
162 | let selection1 = " ~~\n \n"; |
163 | let selection2 = " \n ~~~\n"; | |
164 | sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_owned()); | |
165 | let span1 = span_from_selection(inputtext, selection1); | |
166 | let span2 = span_from_selection(inputtext, selection2); | |
167 | ||
168 | assert!(sm.merge_spans(span1, span2).is_none()); | |
169 | } | |
170 | ||
ba9703b0 XL |
171 | /// Tests loading an external source file that requires normalization. |
172 | #[test] | |
173 | fn t10() { | |
174 | let sm = SourceMap::new(FilePathMapping::empty()); | |
175 | let unnormalized = "first line.\r\nsecond line"; | |
176 | let normalized = "first line.\nsecond line"; | |
177 | ||
178 | let src_file = sm.new_source_file(PathBuf::from("blork.rs").into(), unnormalized.to_string()); | |
179 | ||
180 | assert_eq!(src_file.src.as_ref().unwrap().as_ref(), normalized); | |
181 | assert!( | |
182 | src_file.src_hash.matches(unnormalized), | |
183 | "src_hash should use the source before normalization" | |
184 | ); | |
185 | ||
186 | let SourceFile { | |
187 | name, | |
188 | name_was_remapped, | |
189 | src_hash, | |
190 | start_pos, | |
191 | end_pos, | |
192 | lines, | |
193 | multibyte_chars, | |
194 | non_narrow_chars, | |
195 | normalized_pos, | |
196 | name_hash, | |
197 | .. | |
198 | } = (*src_file).clone(); | |
199 | ||
200 | let imported_src_file = sm.new_imported_source_file( | |
201 | name, | |
202 | name_was_remapped, | |
203 | src_hash, | |
204 | name_hash, | |
205 | (end_pos - start_pos).to_usize(), | |
206 | CrateNum::new(0), | |
207 | lines, | |
208 | multibyte_chars, | |
209 | non_narrow_chars, | |
210 | normalized_pos, | |
211 | start_pos, | |
212 | end_pos, | |
213 | ); | |
214 | ||
215 | assert!( | |
216 | imported_src_file.external_src.borrow().get_source().is_none(), | |
217 | "imported source file should not have source yet" | |
218 | ); | |
219 | imported_src_file.add_external_src(|| Some(unnormalized.to_string())); | |
220 | assert_eq!( | |
221 | imported_src_file.external_src.borrow().get_source().unwrap().as_ref(), | |
222 | normalized, | |
223 | "imported source file should be normalized" | |
224 | ); | |
225 | } | |
226 | ||
e1599b0c | 227 | /// Returns the span corresponding to the `n`th occurrence of `substring` in `source_text`. |
416331ca | 228 | trait SourceMapExtension { |
e1599b0c XL |
229 | fn span_substr( |
230 | &self, | |
231 | file: &Lrc<SourceFile>, | |
232 | source_text: &str, | |
233 | substring: &str, | |
234 | n: usize, | |
235 | ) -> Span; | |
416331ca XL |
236 | } |
237 | ||
238 | impl SourceMapExtension for SourceMap { | |
e1599b0c XL |
239 | fn span_substr( |
240 | &self, | |
241 | file: &Lrc<SourceFile>, | |
242 | source_text: &str, | |
243 | substring: &str, | |
244 | n: usize, | |
245 | ) -> Span { | |
246 | println!( | |
247 | "span_substr(file={:?}/{:?}, substring={:?}, n={})", | |
248 | file.name, file.start_pos, substring, n | |
249 | ); | |
416331ca XL |
250 | let mut i = 0; |
251 | let mut hi = 0; | |
252 | loop { | |
253 | let offset = source_text[hi..].find(substring).unwrap_or_else(|| { | |
e1599b0c XL |
254 | panic!( |
255 | "source_text `{}` does not have {} occurrences of `{}`, only {}", | |
256 | source_text, n, substring, i | |
257 | ); | |
416331ca XL |
258 | }); |
259 | let lo = hi + offset; | |
260 | hi = lo + substring.len(); | |
261 | if i == n { | |
e1599b0c | 262 | let span = Span::with_root_ctxt( |
416331ca XL |
263 | BytePos(lo as u32 + file.start_pos.0), |
264 | BytePos(hi as u32 + file.start_pos.0), | |
416331ca | 265 | ); |
e1599b0c | 266 | assert_eq!(&self.span_to_snippet(span).unwrap()[..], substring); |
416331ca XL |
267 | return span; |
268 | } | |
269 | i += 1; | |
270 | } | |
271 | } | |
272 | } |