3 use rustc_data_structures
::sync
::Lrc
;
5 fn init_source_map() -> SourceMap
{
6 let sm
= SourceMap
::new(FilePathMapping
::empty());
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());
13 /// Tests `lookup_byte_offset`.
16 let sm
= init_source_map();
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));
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));
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));
31 /// Tests `bytepos_to_file_charpos`.
34 let sm
= init_source_map();
36 let cp1
= sm
.bytepos_to_file_charpos(BytePos(22));
37 assert_eq
!(cp1
, CharPos(22));
39 let cp2
= sm
.bytepos_to_file_charpos(BytePos(25));
40 assert_eq
!(cp2
, CharPos(0));
43 /// Tests zero-length `SourceFile`s.
46 let sm
= init_source_map();
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));
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));
59 fn init_source_map_mbc() -> SourceMap
{
60 let sm
= SourceMap
::new(FilePathMapping
::empty());
61 // "€" is a three-byte UTF8 char.
63 PathBuf
::from("blork.rs").into(),
64 "fir€st €€€€ line.\nsecond line".to_string(),
67 PathBuf
::from("blork2.rs").into(),
68 "first line€€.\n€ second line".to_string(),
73 /// Tests `bytepos_to_file_charpos` in the presence of multi-byte chars.
76 let sm
= init_source_map_mbc();
78 let cp1
= sm
.bytepos_to_file_charpos(BytePos(3));
79 assert_eq
!(cp1
, CharPos(3));
81 let cp2
= sm
.bytepos_to_file_charpos(BytePos(6));
82 assert_eq
!(cp2
, CharPos(4));
84 let cp3
= sm
.bytepos_to_file_charpos(BytePos(56));
85 assert_eq
!(cp3
, CharPos(12));
87 let cp4
= sm
.bytepos_to_file_charpos(BytePos(61));
88 assert_eq
!(cp4
, CharPos(15));
91 /// Test `span_to_lines` for a span ending at the end of a `SourceFile`.
94 let sm
= init_source_map();
95 let span
= Span
::with_root_ctxt(BytePos(12), BytePos(23));
96 let file_lines
= sm
.span_to_lines(span
).unwrap();
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);
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;
110 let right_index
= selection
.rfind('
~'
).map(|x
| x
as u32).unwrap_or(left_index
);
111 Span
::with_root_ctxt(BytePos(left_index
), BytePos(right_index
+ 1))
114 /// Tests `span_to_snippet` and `span_to_lines` for a span converting 3
115 /// lines in the middle of a file.
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
);
124 // Check that we are extracting the text we thought we were extracting.
125 assert_eq
!(&sm
.span_to_snippet(span
).unwrap(), "BB\nCCC\nDDDDD");
127 // Check that span_to_lines gives us the complete result with the lines/cols we expected.
128 let lines
= sm
.span_to_lines(span
).unwrap();
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) }
,
132 LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }
,
134 assert_eq
!(lines
.lines
, expected
);
137 /// Test span_to_snippet for a span ending at the end of a `SourceFile`.
140 let sm
= init_source_map();
141 let span
= Span
::with_root_ctxt(BytePos(12), BytePos(23));
142 let snippet
= sm
.span_to_snippet(span
);
144 assert_eq
!(snippet
, Ok("second line".to_string()));
147 /// Test `span_to_str` for a span ending at the end of a `SourceFile`.
150 let sm
= init_source_map();
151 let span
= Span
::with_root_ctxt(BytePos(12), BytePos(23));
152 let sstr
= sm
.span_to_string(span
);
154 assert_eq
!(sstr
, "blork.rs:2:1: 2:12");
157 /// Tests failing to merge two spans on different lines.
159 fn span_merging_fail() {
160 let sm
= SourceMap
::new(FilePathMapping
::empty());
161 let inputtext
= "bbbb BB\ncc CCC\n";
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
);
168 assert
!(sm
.merge_spans(span1
, span2
).is_none());
171 /// Tests loading an external source file that requires normalization.
174 let sm
= SourceMap
::new(FilePathMapping
::empty());
175 let unnormalized
= "first line.\r\nsecond line";
176 let normalized
= "first line.\nsecond line";
178 let src_file
= sm
.new_source_file(PathBuf
::from("blork.rs").into(), unnormalized
.to_string());
180 assert_eq
!(src_file
.src
.as_ref().unwrap().as_ref(), normalized
);
182 src_file
.src_hash
.matches(unnormalized
),
183 "src_hash should use the source before normalization"
198 } = (*src_file
).clone();
200 let imported_src_file
= sm
.new_imported_source_file(
205 (end_pos
- start_pos
).to_usize(),
216 imported_src_file
.external_src
.borrow().get_source().is_none(),
217 "imported source file should not have source yet"
219 imported_src_file
.add_external_src(|| Some(unnormalized
.to_string()));
221 imported_src_file
.external_src
.borrow().get_source().unwrap().as_ref(),
223 "imported source file should be normalized"
227 /// Returns the span corresponding to the `n`th occurrence of `substring` in `source_text`.
228 trait SourceMapExtension
{
231 file
: &Lrc
<SourceFile
>,
238 impl SourceMapExtension
for SourceMap
{
241 file
: &Lrc
<SourceFile
>,
247 "span_substr(file={:?}/{:?}, substring={:?}, n={})",
248 file
.name
, file
.start_pos
, substring
, n
253 let offset
= source_text
[hi
..].find(substring
).unwrap_or_else(|| {
255 "source_text `{}` does not have {} occurrences of `{}`, only {}",
256 source_text
, n
, substring
, i
259 let lo
= hi
+ offset
;
260 hi
= lo
+ substring
.len();
262 let span
= Span
::with_root_ctxt(
263 BytePos(lo
as u32 + file
.start_pos
.0),
264 BytePos(hi
as u32 + file
.start_pos
.0),
266 assert_eq
!(&self.span_to_snippet(span
).unwrap()[..], substring
);