]>
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 | ||
6a06907d XL |
13 | impl SourceMap { |
14 | /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If | |
15 | /// there are gaps between LHS and RHS, the resulting union will cross these gaps. | |
16 | /// For this to work, | |
17 | /// | |
18 | /// * the syntax contexts of both spans much match, | |
19 | /// * the LHS span needs to end on the same line the RHS span begins, | |
20 | /// * the LHS span must start at or before the RHS span. | |
21 | fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> { | |
22 | // Ensure we're at the same expansion ID. | |
23 | if sp_lhs.ctxt() != sp_rhs.ctxt() { | |
24 | return None; | |
25 | } | |
26 | ||
27 | let lhs_end = match self.lookup_line(sp_lhs.hi()) { | |
28 | Ok(x) => x, | |
29 | Err(_) => return None, | |
30 | }; | |
31 | let rhs_begin = match self.lookup_line(sp_rhs.lo()) { | |
32 | Ok(x) => x, | |
33 | Err(_) => return None, | |
34 | }; | |
35 | ||
36 | // If we must cross lines to merge, don't merge. | |
37 | if lhs_end.line != rhs_begin.line { | |
38 | return None; | |
39 | } | |
40 | ||
41 | // Ensure these follow the expected order and that we don't overlap. | |
42 | if (sp_lhs.lo() <= sp_rhs.lo()) && (sp_lhs.hi() <= sp_rhs.lo()) { | |
43 | Some(sp_lhs.to(sp_rhs)) | |
44 | } else { | |
45 | None | |
46 | } | |
47 | } | |
48 | ||
49 | /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. | |
50 | fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { | |
51 | let idx = self.lookup_source_file_idx(bpos); | |
52 | let sf = &(*self.files.borrow().source_files)[idx]; | |
53 | sf.bytepos_to_file_charpos(bpos) | |
54 | } | |
55 | } | |
56 | ||
e1599b0c | 57 | /// Tests `lookup_byte_offset`. |
416331ca XL |
58 | #[test] |
59 | fn t3() { | |
416331ca XL |
60 | let sm = init_source_map(); |
61 | ||
62 | let srcfbp1 = sm.lookup_byte_offset(BytePos(23)); | |
63 | assert_eq!(srcfbp1.sf.name, PathBuf::from("blork.rs").into()); | |
64 | assert_eq!(srcfbp1.pos, BytePos(23)); | |
65 | ||
66 | let srcfbp1 = sm.lookup_byte_offset(BytePos(24)); | |
67 | assert_eq!(srcfbp1.sf.name, PathBuf::from("empty.rs").into()); | |
68 | assert_eq!(srcfbp1.pos, BytePos(0)); | |
69 | ||
70 | let srcfbp2 = sm.lookup_byte_offset(BytePos(25)); | |
71 | assert_eq!(srcfbp2.sf.name, PathBuf::from("blork2.rs").into()); | |
72 | assert_eq!(srcfbp2.pos, BytePos(0)); | |
73 | } | |
74 | ||
e1599b0c | 75 | /// Tests `bytepos_to_file_charpos`. |
416331ca XL |
76 | #[test] |
77 | fn t4() { | |
416331ca XL |
78 | let sm = init_source_map(); |
79 | ||
80 | let cp1 = sm.bytepos_to_file_charpos(BytePos(22)); | |
81 | assert_eq!(cp1, CharPos(22)); | |
82 | ||
83 | let cp2 = sm.bytepos_to_file_charpos(BytePos(25)); | |
84 | assert_eq!(cp2, CharPos(0)); | |
85 | } | |
86 | ||
e1599b0c | 87 | /// Tests zero-length `SourceFile`s. |
416331ca XL |
88 | #[test] |
89 | fn t5() { | |
416331ca XL |
90 | let sm = init_source_map(); |
91 | ||
92 | let loc1 = sm.lookup_char_pos(BytePos(22)); | |
93 | assert_eq!(loc1.file.name, PathBuf::from("blork.rs").into()); | |
94 | assert_eq!(loc1.line, 2); | |
95 | assert_eq!(loc1.col, CharPos(10)); | |
96 | ||
97 | let loc2 = sm.lookup_char_pos(BytePos(25)); | |
98 | assert_eq!(loc2.file.name, PathBuf::from("blork2.rs").into()); | |
99 | assert_eq!(loc2.line, 1); | |
100 | assert_eq!(loc2.col, CharPos(0)); | |
101 | } | |
102 | ||
103 | fn init_source_map_mbc() -> SourceMap { | |
104 | let sm = SourceMap::new(FilePathMapping::empty()); | |
e1599b0c | 105 | // "€" is a three-byte UTF8 char. |
dfeec247 XL |
106 | sm.new_source_file( |
107 | PathBuf::from("blork.rs").into(), | |
108 | "fir€st €€€€ line.\nsecond line".to_string(), | |
109 | ); | |
110 | sm.new_source_file( | |
111 | PathBuf::from("blork2.rs").into(), | |
112 | "first line€€.\n€ second line".to_string(), | |
113 | ); | |
416331ca XL |
114 | sm |
115 | } | |
116 | ||
e1599b0c | 117 | /// Tests `bytepos_to_file_charpos` in the presence of multi-byte chars. |
416331ca XL |
118 | #[test] |
119 | fn t6() { | |
416331ca XL |
120 | let sm = init_source_map_mbc(); |
121 | ||
122 | let cp1 = sm.bytepos_to_file_charpos(BytePos(3)); | |
123 | assert_eq!(cp1, CharPos(3)); | |
124 | ||
125 | let cp2 = sm.bytepos_to_file_charpos(BytePos(6)); | |
126 | assert_eq!(cp2, CharPos(4)); | |
127 | ||
128 | let cp3 = sm.bytepos_to_file_charpos(BytePos(56)); | |
129 | assert_eq!(cp3, CharPos(12)); | |
130 | ||
131 | let cp4 = sm.bytepos_to_file_charpos(BytePos(61)); | |
132 | assert_eq!(cp4, CharPos(15)); | |
133 | } | |
134 | ||
e1599b0c | 135 | /// Test `span_to_lines` for a span ending at the end of a `SourceFile`. |
416331ca XL |
136 | #[test] |
137 | fn t7() { | |
416331ca | 138 | let sm = init_source_map(); |
e1599b0c | 139 | let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); |
416331ca XL |
140 | let file_lines = sm.span_to_lines(span).unwrap(); |
141 | ||
142 | assert_eq!(file_lines.file.name, PathBuf::from("blork.rs").into()); | |
143 | assert_eq!(file_lines.lines.len(), 1); | |
144 | assert_eq!(file_lines.lines[0].line_index, 1); | |
145 | } | |
146 | ||
147 | /// Given a string like " ~~~~~~~~~~~~ ", produces a span | |
148 | /// converting that range. The idea is that the string has the same | |
149 | /// length as the input, and we uncover the byte positions. Note | |
150 | /// that this can span lines and so on. | |
151 | fn span_from_selection(input: &str, selection: &str) -> Span { | |
152 | assert_eq!(input.len(), selection.len()); | |
153 | let left_index = selection.find('~').unwrap() as u32; | |
5869c6ff | 154 | let right_index = selection.rfind('~').map_or(left_index, |x| x as u32); |
e1599b0c | 155 | Span::with_root_ctxt(BytePos(left_index), BytePos(right_index + 1)) |
416331ca XL |
156 | } |
157 | ||
e1599b0c | 158 | /// Tests `span_to_snippet` and `span_to_lines` for a span converting 3 |
416331ca XL |
159 | /// lines in the middle of a file. |
160 | #[test] | |
161 | fn span_to_snippet_and_lines_spanning_multiple_lines() { | |
162 | let sm = SourceMap::new(FilePathMapping::empty()); | |
163 | let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; | |
164 | let selection = " \n ~~\n~~~\n~~~~~ \n \n"; | |
165 | sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_string()); | |
166 | let span = span_from_selection(inputtext, selection); | |
167 | ||
e1599b0c | 168 | // Check that we are extracting the text we thought we were extracting. |
416331ca XL |
169 | assert_eq!(&sm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD"); |
170 | ||
e1599b0c | 171 | // Check that span_to_lines gives us the complete result with the lines/cols we expected. |
416331ca XL |
172 | let lines = sm.span_to_lines(span).unwrap(); |
173 | let expected = vec![ | |
174 | LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) }, | |
175 | LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) }, | |
dfeec247 XL |
176 | LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }, |
177 | ]; | |
416331ca XL |
178 | assert_eq!(lines.lines, expected); |
179 | } | |
180 | ||
e1599b0c | 181 | /// Test span_to_snippet for a span ending at the end of a `SourceFile`. |
416331ca XL |
182 | #[test] |
183 | fn t8() { | |
416331ca | 184 | let sm = init_source_map(); |
e1599b0c | 185 | let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); |
416331ca XL |
186 | let snippet = sm.span_to_snippet(span); |
187 | ||
188 | assert_eq!(snippet, Ok("second line".to_string())); | |
189 | } | |
190 | ||
e1599b0c | 191 | /// Test `span_to_str` for a span ending at the end of a `SourceFile`. |
416331ca XL |
192 | #[test] |
193 | fn t9() { | |
416331ca | 194 | let sm = init_source_map(); |
e1599b0c | 195 | let span = Span::with_root_ctxt(BytePos(12), BytePos(23)); |
17df50a5 | 196 | let sstr = sm.span_to_diagnostic_string(span); |
416331ca XL |
197 | |
198 | assert_eq!(sstr, "blork.rs:2:1: 2:12"); | |
199 | } | |
200 | ||
e1599b0c | 201 | /// Tests failing to merge two spans on different lines. |
416331ca XL |
202 | #[test] |
203 | fn span_merging_fail() { | |
204 | let sm = SourceMap::new(FilePathMapping::empty()); | |
dfeec247 | 205 | let inputtext = "bbbb BB\ncc CCC\n"; |
416331ca XL |
206 | let selection1 = " ~~\n \n"; |
207 | let selection2 = " \n ~~~\n"; | |
208 | sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_owned()); | |
209 | let span1 = span_from_selection(inputtext, selection1); | |
210 | let span2 = span_from_selection(inputtext, selection2); | |
211 | ||
212 | assert!(sm.merge_spans(span1, span2).is_none()); | |
213 | } | |
214 | ||
ba9703b0 XL |
215 | /// Tests loading an external source file that requires normalization. |
216 | #[test] | |
217 | fn t10() { | |
218 | let sm = SourceMap::new(FilePathMapping::empty()); | |
219 | let unnormalized = "first line.\r\nsecond line"; | |
220 | let normalized = "first line.\nsecond line"; | |
221 | ||
222 | let src_file = sm.new_source_file(PathBuf::from("blork.rs").into(), unnormalized.to_string()); | |
223 | ||
224 | assert_eq!(src_file.src.as_ref().unwrap().as_ref(), normalized); | |
225 | assert!( | |
226 | src_file.src_hash.matches(unnormalized), | |
227 | "src_hash should use the source before normalization" | |
228 | ); | |
229 | ||
230 | let SourceFile { | |
231 | name, | |
ba9703b0 XL |
232 | src_hash, |
233 | start_pos, | |
234 | end_pos, | |
235 | lines, | |
236 | multibyte_chars, | |
237 | non_narrow_chars, | |
238 | normalized_pos, | |
239 | name_hash, | |
240 | .. | |
241 | } = (*src_file).clone(); | |
242 | ||
243 | let imported_src_file = sm.new_imported_source_file( | |
244 | name, | |
ba9703b0 XL |
245 | src_hash, |
246 | name_hash, | |
247 | (end_pos - start_pos).to_usize(), | |
248 | CrateNum::new(0), | |
249 | lines, | |
250 | multibyte_chars, | |
251 | non_narrow_chars, | |
252 | normalized_pos, | |
253 | start_pos, | |
f2b60f7d | 254 | 0, |
ba9703b0 XL |
255 | ); |
256 | ||
257 | assert!( | |
258 | imported_src_file.external_src.borrow().get_source().is_none(), | |
259 | "imported source file should not have source yet" | |
260 | ); | |
261 | imported_src_file.add_external_src(|| Some(unnormalized.to_string())); | |
262 | assert_eq!( | |
263 | imported_src_file.external_src.borrow().get_source().unwrap().as_ref(), | |
264 | normalized, | |
265 | "imported source file should be normalized" | |
266 | ); | |
267 | } | |
268 | ||
e1599b0c | 269 | /// Returns the span corresponding to the `n`th occurrence of `substring` in `source_text`. |
416331ca | 270 | trait SourceMapExtension { |
e1599b0c XL |
271 | fn span_substr( |
272 | &self, | |
273 | file: &Lrc<SourceFile>, | |
274 | source_text: &str, | |
275 | substring: &str, | |
276 | n: usize, | |
277 | ) -> Span; | |
416331ca XL |
278 | } |
279 | ||
280 | impl SourceMapExtension for SourceMap { | |
e1599b0c XL |
281 | fn span_substr( |
282 | &self, | |
283 | file: &Lrc<SourceFile>, | |
284 | source_text: &str, | |
285 | substring: &str, | |
286 | n: usize, | |
287 | ) -> Span { | |
6a06907d | 288 | eprintln!( |
e1599b0c XL |
289 | "span_substr(file={:?}/{:?}, substring={:?}, n={})", |
290 | file.name, file.start_pos, substring, n | |
291 | ); | |
416331ca XL |
292 | let mut i = 0; |
293 | let mut hi = 0; | |
294 | loop { | |
295 | let offset = source_text[hi..].find(substring).unwrap_or_else(|| { | |
e1599b0c XL |
296 | panic!( |
297 | "source_text `{}` does not have {} occurrences of `{}`, only {}", | |
298 | source_text, n, substring, i | |
299 | ); | |
416331ca XL |
300 | }); |
301 | let lo = hi + offset; | |
302 | hi = lo + substring.len(); | |
303 | if i == n { | |
e1599b0c | 304 | let span = Span::with_root_ctxt( |
416331ca XL |
305 | BytePos(lo as u32 + file.start_pos.0), |
306 | BytePos(hi as u32 + file.start_pos.0), | |
416331ca | 307 | ); |
e1599b0c | 308 | assert_eq!(&self.span_to_snippet(span).unwrap()[..], substring); |
416331ca XL |
309 | return span; |
310 | } | |
311 | i += 1; | |
312 | } | |
313 | } | |
314 | } | |
04454e1e | 315 | |
923072b8 FG |
316 | // Takes a unix-style path and returns a platform specific path. |
317 | fn path(p: &str) -> PathBuf { | |
318 | path_str(p).into() | |
319 | } | |
320 | ||
321 | // Takes a unix-style path and returns a platform specific path. | |
322 | fn path_str(p: &str) -> String { | |
323 | #[cfg(not(windows))] | |
324 | { | |
325 | return p.into(); | |
326 | } | |
327 | ||
328 | #[cfg(windows)] | |
329 | { | |
330 | let mut path = p.replace('/', "\\"); | |
331 | if let Some(rest) = path.strip_prefix('\\') { | |
332 | path = ["X:\\", rest].concat(); | |
333 | } | |
334 | ||
335 | path | |
336 | } | |
337 | } | |
338 | ||
339 | fn map_path_prefix(mapping: &FilePathMapping, p: &str) -> String { | |
04454e1e FG |
340 | // It's important that we convert to a string here because that's what |
341 | // later stages do too (e.g. in the backend), and comparing `Path` values | |
342 | // won't catch some differences at the string level, e.g. "abc" and "abc/" | |
343 | // compare as equal. | |
923072b8 | 344 | mapping.map_prefix(path(p)).0.to_string_lossy().to_string() |
04454e1e FG |
345 | } |
346 | ||
9c376795 FG |
347 | fn reverse_map_prefix(mapping: &FilePathMapping, p: &str) -> Option<String> { |
348 | mapping.reverse_map_prefix_heuristically(&path(p)).map(|q| q.to_string_lossy().to_string()) | |
349 | } | |
350 | ||
04454e1e FG |
351 | #[test] |
352 | fn path_prefix_remapping() { | |
353 | // Relative to relative | |
354 | { | |
923072b8 | 355 | let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("foo"))]); |
04454e1e | 356 | |
923072b8 FG |
357 | assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("foo/src/main.rs")); |
358 | assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("foo")); | |
04454e1e FG |
359 | } |
360 | ||
361 | // Relative to absolute | |
362 | { | |
923072b8 | 363 | let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("/foo"))]); |
04454e1e | 364 | |
923072b8 FG |
365 | assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("/foo/src/main.rs")); |
366 | assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("/foo")); | |
04454e1e FG |
367 | } |
368 | ||
369 | // Absolute to relative | |
370 | { | |
923072b8 | 371 | let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("foo"))]); |
04454e1e | 372 | |
923072b8 FG |
373 | assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("foo/src/main.rs")); |
374 | assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("foo")); | |
04454e1e FG |
375 | } |
376 | ||
377 | // Absolute to absolute | |
378 | { | |
923072b8 | 379 | let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("/foo"))]); |
04454e1e | 380 | |
923072b8 FG |
381 | assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("/foo/src/main.rs")); |
382 | assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("/foo")); | |
04454e1e FG |
383 | } |
384 | } | |
385 | ||
04454e1e | 386 | #[test] |
923072b8 FG |
387 | fn path_prefix_remapping_expand_to_absolute() { |
388 | // "virtual" working directory is relative path | |
389 | let mapping = | |
390 | &FilePathMapping::new(vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))]); | |
391 | let working_directory = path("/foo"); | |
392 | let working_directory = RealFileName::Remapped { | |
393 | local_path: Some(working_directory.clone()), | |
9c376795 | 394 | virtual_name: mapping.map_prefix(working_directory).0.into_owned(), |
923072b8 FG |
395 | }; |
396 | ||
397 | assert_eq!(working_directory.remapped_path_if_available(), path("FOO")); | |
398 | ||
399 | // Unmapped absolute path | |
400 | assert_eq!( | |
401 | mapping.to_embeddable_absolute_path( | |
402 | RealFileName::LocalPath(path("/foo/src/main.rs")), | |
403 | &working_directory | |
404 | ), | |
405 | RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } | |
406 | ); | |
04454e1e | 407 | |
923072b8 FG |
408 | // Unmapped absolute path with unrelated working directory |
409 | assert_eq!( | |
410 | mapping.to_embeddable_absolute_path( | |
411 | RealFileName::LocalPath(path("/bar/src/main.rs")), | |
412 | &working_directory | |
413 | ), | |
414 | RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") } | |
415 | ); | |
04454e1e | 416 | |
923072b8 FG |
417 | // Unmapped absolute path that does not match any prefix |
418 | assert_eq!( | |
419 | mapping.to_embeddable_absolute_path( | |
420 | RealFileName::LocalPath(path("/quux/src/main.rs")), | |
421 | &working_directory | |
422 | ), | |
423 | RealFileName::LocalPath(path("/quux/src/main.rs")), | |
424 | ); | |
04454e1e | 425 | |
923072b8 FG |
426 | // Unmapped relative path |
427 | assert_eq!( | |
428 | mapping.to_embeddable_absolute_path( | |
429 | RealFileName::LocalPath(path("src/main.rs")), | |
430 | &working_directory | |
431 | ), | |
432 | RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } | |
433 | ); | |
04454e1e | 434 | |
923072b8 FG |
435 | // Unmapped relative path with `./` |
436 | assert_eq!( | |
437 | mapping.to_embeddable_absolute_path( | |
438 | RealFileName::LocalPath(path("./src/main.rs")), | |
439 | &working_directory | |
440 | ), | |
441 | RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } | |
442 | ); | |
04454e1e | 443 | |
923072b8 FG |
444 | // Unmapped relative path that does not match any prefix |
445 | assert_eq!( | |
446 | mapping.to_embeddable_absolute_path( | |
447 | RealFileName::LocalPath(path("quux/src/main.rs")), | |
448 | &RealFileName::LocalPath(path("/abc")), | |
449 | ), | |
450 | RealFileName::LocalPath(path("/abc/quux/src/main.rs")), | |
451 | ); | |
04454e1e | 452 | |
923072b8 FG |
453 | // Already remapped absolute path |
454 | assert_eq!( | |
455 | mapping.to_embeddable_absolute_path( | |
456 | RealFileName::Remapped { | |
457 | local_path: Some(path("/foo/src/main.rs")), | |
458 | virtual_name: path("FOO/src/main.rs"), | |
459 | }, | |
460 | &working_directory | |
461 | ), | |
462 | RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") } | |
463 | ); | |
04454e1e | 464 | |
923072b8 FG |
465 | // Already remapped absolute path, with unrelated working directory |
466 | assert_eq!( | |
467 | mapping.to_embeddable_absolute_path( | |
468 | RealFileName::Remapped { | |
469 | local_path: Some(path("/bar/src/main.rs")), | |
470 | virtual_name: path("BAR/src/main.rs"), | |
471 | }, | |
472 | &working_directory | |
473 | ), | |
474 | RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") } | |
475 | ); | |
476 | ||
477 | // Already remapped relative path | |
478 | assert_eq!( | |
479 | mapping.to_embeddable_absolute_path( | |
480 | RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }, | |
481 | &working_directory | |
482 | ), | |
483 | RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") } | |
484 | ); | |
04454e1e | 485 | } |
2b03887a | 486 | |
9c376795 FG |
487 | #[test] |
488 | fn path_prefix_remapping_reverse() { | |
489 | // Ignores options without alphanumeric chars. | |
490 | { | |
491 | let mapping = | |
492 | &FilePathMapping::new(vec![(path("abc"), path("/")), (path("def"), path("."))]); | |
493 | ||
494 | assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None); | |
495 | assert_eq!(reverse_map_prefix(mapping, "./hello.rs"), None); | |
496 | } | |
497 | ||
498 | // Returns `None` if multiple options match. | |
499 | { | |
500 | let mapping = &FilePathMapping::new(vec![ | |
501 | (path("abc"), path("/redacted")), | |
502 | (path("def"), path("/redacted")), | |
503 | ]); | |
504 | ||
505 | assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None); | |
506 | } | |
507 | ||
508 | // Distinct reverse mappings. | |
509 | { | |
510 | let mapping = &FilePathMapping::new(vec![ | |
511 | (path("abc"), path("/redacted")), | |
512 | (path("def/ghi"), path("/fake/dir")), | |
513 | ]); | |
514 | ||
515 | assert_eq!( | |
516 | reverse_map_prefix(mapping, "/redacted/path/hello.rs"), | |
517 | Some(path_str("abc/path/hello.rs")) | |
518 | ); | |
519 | assert_eq!( | |
520 | reverse_map_prefix(mapping, "/fake/dir/hello.rs"), | |
521 | Some(path_str("def/ghi/hello.rs")) | |
522 | ); | |
523 | } | |
524 | } | |
525 | ||
2b03887a FG |
526 | #[test] |
527 | fn test_next_point() { | |
528 | let sm = SourceMap::new(FilePathMapping::empty()); | |
529 | sm.new_source_file(PathBuf::from("example.rs").into(), "a…b".to_string()); | |
530 | ||
531 | // Dummy spans don't advance. | |
532 | let span = DUMMY_SP; | |
533 | let span = sm.next_point(span); | |
534 | assert_eq!(span.lo().0, 0); | |
535 | assert_eq!(span.hi().0, 0); | |
536 | ||
537 | // Span advance respect multi-byte character | |
538 | let span = Span::with_root_ctxt(BytePos(0), BytePos(1)); | |
539 | assert_eq!(sm.span_to_snippet(span), Ok("a".to_string())); | |
540 | let span = sm.next_point(span); | |
541 | assert_eq!(sm.span_to_snippet(span), Ok("…".to_string())); | |
542 | assert_eq!(span.lo().0, 1); | |
543 | assert_eq!(span.hi().0, 4); | |
544 | ||
545 | // An empty span pointing just before a multi-byte character should | |
546 | // advance to contain the multi-byte character. | |
547 | let span = Span::with_root_ctxt(BytePos(1), BytePos(1)); | |
548 | let span = sm.next_point(span); | |
549 | assert_eq!(span.lo().0, 1); | |
550 | assert_eq!(span.hi().0, 4); | |
551 | ||
552 | let span = Span::with_root_ctxt(BytePos(1), BytePos(4)); | |
553 | let span = sm.next_point(span); | |
554 | assert_eq!(span.lo().0, 4); | |
555 | assert_eq!(span.hi().0, 5); | |
556 | ||
487cf647 | 557 | // Reaching to the end of file, return a span that will get error with `span_to_snippet` |
2b03887a FG |
558 | let span = Span::with_root_ctxt(BytePos(4), BytePos(5)); |
559 | let span = sm.next_point(span); | |
560 | assert_eq!(span.lo().0, 5); | |
487cf647 FG |
561 | assert_eq!(span.hi().0, 6); |
562 | assert!(sm.span_to_snippet(span).is_err()); | |
2b03887a | 563 | |
487cf647 | 564 | // Reaching to the end of file, return a span that will get error with `span_to_snippet` |
2b03887a FG |
565 | let span = Span::with_root_ctxt(BytePos(5), BytePos(5)); |
566 | let span = sm.next_point(span); | |
567 | assert_eq!(span.lo().0, 5); | |
487cf647 FG |
568 | assert_eq!(span.hi().0, 6); |
569 | assert!(sm.span_to_snippet(span).is_err()); | |
2b03887a | 570 | } |