3 use rustc_data_structures::sync::Lrc;
5 fn init_source_map() -> SourceMap {
6 let sm = SourceMap::new(FilePathMapping::empty());
8 PathBuf::from("blork.rs").into(),
9 "first line.\nsecond line".to_string(),
12 PathBuf::from("empty.rs").into(),
16 PathBuf::from("blork2.rs").into(),
17 "first line.\nsecond line".to_string(),
22 /// Tests `lookup_byte_offset`.
25 let sm = init_source_map();
27 let srcfbp1 = sm.lookup_byte_offset(BytePos(23));
28 assert_eq!(srcfbp1.sf.name, PathBuf::from("blork.rs").into());
29 assert_eq!(srcfbp1.pos, BytePos(23));
31 let srcfbp1 = sm.lookup_byte_offset(BytePos(24));
32 assert_eq!(srcfbp1.sf.name, PathBuf::from("empty.rs").into());
33 assert_eq!(srcfbp1.pos, BytePos(0));
35 let srcfbp2 = sm.lookup_byte_offset(BytePos(25));
36 assert_eq!(srcfbp2.sf.name, PathBuf::from("blork2.rs").into());
37 assert_eq!(srcfbp2.pos, BytePos(0));
40 /// Tests `bytepos_to_file_charpos`.
43 let sm = init_source_map();
45 let cp1 = sm.bytepos_to_file_charpos(BytePos(22));
46 assert_eq!(cp1, CharPos(22));
48 let cp2 = sm.bytepos_to_file_charpos(BytePos(25));
49 assert_eq!(cp2, CharPos(0));
52 /// Tests zero-length `SourceFile`s.
55 let sm = init_source_map();
57 let loc1 = sm.lookup_char_pos(BytePos(22));
58 assert_eq!(loc1.file.name, PathBuf::from("blork.rs").into());
59 assert_eq!(loc1.line, 2);
60 assert_eq!(loc1.col, CharPos(10));
62 let loc2 = sm.lookup_char_pos(BytePos(25));
63 assert_eq!(loc2.file.name, PathBuf::from("blork2.rs").into());
64 assert_eq!(loc2.line, 1);
65 assert_eq!(loc2.col, CharPos(0));
68 fn init_source_map_mbc() -> SourceMap {
69 let sm = SourceMap::new(FilePathMapping::empty());
70 // "€" is a three-byte UTF8 char.
71 sm.new_source_file(PathBuf::from("blork.rs").into(),
72 "fir€st €€€€ line.\nsecond line".to_string());
73 sm.new_source_file(PathBuf::from("blork2.rs").into(),
74 "first line€€.\n€ second line".to_string());
78 /// Tests `bytepos_to_file_charpos` in the presence of multi-byte chars.
81 let sm = init_source_map_mbc();
83 let cp1 = sm.bytepos_to_file_charpos(BytePos(3));
84 assert_eq!(cp1, CharPos(3));
86 let cp2 = sm.bytepos_to_file_charpos(BytePos(6));
87 assert_eq!(cp2, CharPos(4));
89 let cp3 = sm.bytepos_to_file_charpos(BytePos(56));
90 assert_eq!(cp3, CharPos(12));
92 let cp4 = sm.bytepos_to_file_charpos(BytePos(61));
93 assert_eq!(cp4, CharPos(15));
96 /// Test `span_to_lines` for a span ending at the end of a `SourceFile`.
99 let sm = init_source_map();
100 let span = Span::with_root_ctxt(BytePos(12), BytePos(23));
101 let file_lines = sm.span_to_lines(span).unwrap();
103 assert_eq!(file_lines.file.name, PathBuf::from("blork.rs").into());
104 assert_eq!(file_lines.lines.len(), 1);
105 assert_eq!(file_lines.lines[0].line_index, 1);
108 /// Given a string like " ~~~~~~~~~~~~ ", produces a span
109 /// converting that range. The idea is that the string has the same
110 /// length as the input, and we uncover the byte positions. Note
111 /// that this can span lines and so on.
112 fn span_from_selection(input: &str, selection: &str) -> Span {
113 assert_eq!(input.len(), selection.len());
114 let left_index = selection.find('~').unwrap() as u32;
115 let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
116 Span::with_root_ctxt(BytePos(left_index), BytePos(right_index + 1))
119 /// Tests `span_to_snippet` and `span_to_lines` for a span converting 3
120 /// lines in the middle of a file.
122 fn span_to_snippet_and_lines_spanning_multiple_lines() {
123 let sm = SourceMap::new(FilePathMapping::empty());
124 let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
125 let selection = " \n ~~\n~~~\n~~~~~ \n \n";
126 sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_string());
127 let span = span_from_selection(inputtext, selection);
129 // Check that we are extracting the text we thought we were extracting.
130 assert_eq!(&sm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD");
132 // Check that span_to_lines gives us the complete result with the lines/cols we expected.
133 let lines = sm.span_to_lines(span).unwrap();
135 LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) },
136 LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) },
137 LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }
139 assert_eq!(lines.lines, expected);
142 /// Test span_to_snippet for a span ending at the end of a `SourceFile`.
145 let sm = init_source_map();
146 let span = Span::with_root_ctxt(BytePos(12), BytePos(23));
147 let snippet = sm.span_to_snippet(span);
149 assert_eq!(snippet, Ok("second line".to_string()));
152 /// Test `span_to_str` for a span ending at the end of a `SourceFile`.
155 let sm = init_source_map();
156 let span = Span::with_root_ctxt(BytePos(12), BytePos(23));
157 let sstr = sm.span_to_string(span);
159 assert_eq!(sstr, "blork.rs:2:1: 2:12");
162 /// Tests failing to merge two spans on different lines.
164 fn span_merging_fail() {
165 let sm = SourceMap::new(FilePathMapping::empty());
166 let inputtext = "bbbb BB\ncc CCC\n";
167 let selection1 = " ~~\n \n";
168 let selection2 = " \n ~~~\n";
169 sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_owned());
170 let span1 = span_from_selection(inputtext, selection1);
171 let span2 = span_from_selection(inputtext, selection2);
173 assert!(sm.merge_spans(span1, span2).is_none());
176 /// Returns the span corresponding to the `n`th occurrence of `substring` in `source_text`.
177 trait SourceMapExtension {
180 file: &Lrc<SourceFile>,
187 impl SourceMapExtension for SourceMap {
190 file: &Lrc<SourceFile>,
196 "span_substr(file={:?}/{:?}, substring={:?}, n={})",
197 file.name, file.start_pos, substring, n
202 let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
204 "source_text `{}` does not have {} occurrences of `{}`, only {}",
205 source_text, n, substring, i
208 let lo = hi + offset;
209 hi = lo + substring.len();
211 let span = Span::with_root_ctxt(
212 BytePos(lo as u32 + file.start_pos.0),
213 BytePos(hi as u32 + file.start_pos.0),
215 assert_eq!(&self.span_to_snippet(span).unwrap()[..], substring);