]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/source_map.rs
Regression test for issue #54477.
[rust.git] / src / libsyntax / source_map.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! The SourceMap tracks all the source code used within a single crate, mapping
12 //! from integer byte positions to the original source code location. Each bit
13 //! of source parsed during crate parsing (typically files, in-memory strings,
14 //! or various bits of macro expansion) cover a continuous range of bytes in the
15 //! SourceMap and are represented by SourceFiles. Byte positions are stored in
16 //! `spans` and used pervasively in the compiler. They are absolute positions
17 //! within the SourceMap, which upon request can be converted to line and column
18 //! information, source code snippets, etc.
19
20
21 pub use syntax_pos::*;
22 pub use syntax_pos::hygiene::{ExpnFormat, ExpnInfo};
23 pub use self::ExpnFormat::*;
24
25 use rustc_data_structures::fx::FxHashMap;
26 use rustc_data_structures::stable_hasher::StableHasher;
27 use rustc_data_structures::sync::{Lrc, Lock, LockGuard, MappedLockGuard};
28 use std::cmp;
29 use std::hash::Hash;
30 use std::path::{Path, PathBuf};
31
32 use std::env;
33 use std::fs;
34 use std::io::{self, Read};
35 use errors::SourceMapper;
36
37 /// Return the span itself if it doesn't come from a macro expansion,
38 /// otherwise return the call site span up to the `enclosing_sp` by
39 /// following the `expn_info` chain.
40 pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span {
41     let call_site1 = sp.ctxt().outer().expn_info().map(|ei| ei.call_site);
42     let call_site2 = enclosing_sp.ctxt().outer().expn_info().map(|ei| ei.call_site);
43     match (call_site1, call_site2) {
44         (None, _) => sp,
45         (Some(call_site1), Some(call_site2)) if call_site1 == call_site2 => sp,
46         (Some(call_site1), _) => original_sp(call_site1, enclosing_sp),
47     }
48 }
49
50 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
51 pub struct Spanned<T> {
52     pub node: T,
53     pub span: Span,
54 }
55
56 pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
57     Spanned {node: t, span: sp}
58 }
59
60 pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
61     respan(DUMMY_SP, t)
62 }
63
64 // _____________________________________________________________________________
65 // SourceFile, MultiByteChar, FileName, FileLines
66 //
67
68 /// An abstraction over the fs operations used by the Parser.
69 pub trait FileLoader {
70     /// Query the existence of a file.
71     fn file_exists(&self, path: &Path) -> bool;
72
73     /// Return an absolute path to a file, if possible.
74     fn abs_path(&self, path: &Path) -> Option<PathBuf>;
75
76     /// Read the contents of an UTF-8 file into memory.
77     fn read_file(&self, path: &Path) -> io::Result<String>;
78 }
79
80 /// A FileLoader that uses std::fs to load real files.
81 pub struct RealFileLoader;
82
83 impl FileLoader for RealFileLoader {
84     fn file_exists(&self, path: &Path) -> bool {
85         fs::metadata(path).is_ok()
86     }
87
88     fn abs_path(&self, path: &Path) -> Option<PathBuf> {
89         if path.is_absolute() {
90             Some(path.to_path_buf())
91         } else {
92             env::current_dir()
93                 .ok()
94                 .map(|cwd| cwd.join(path))
95         }
96     }
97
98     fn read_file(&self, path: &Path) -> io::Result<String> {
99         let mut src = String::new();
100         fs::File::open(path)?.read_to_string(&mut src)?;
101         Ok(src)
102     }
103 }
104
105 // This is a SourceFile identifier that is used to correlate SourceFiles between
106 // subsequent compilation sessions (which is something we need to do during
107 // incremental compilation).
108 #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
109 pub struct StableFilemapId(u128);
110
111 impl StableFilemapId {
112     pub fn new(source_file: &SourceFile) -> StableFilemapId {
113         let mut hasher = StableHasher::new();
114
115         source_file.name.hash(&mut hasher);
116         source_file.name_was_remapped.hash(&mut hasher);
117         source_file.unmapped_path.hash(&mut hasher);
118
119         StableFilemapId(hasher.finish())
120     }
121 }
122
123 // _____________________________________________________________________________
124 // SourceMap
125 //
126
127 #[derive(Default)]
128 pub(super) struct SourceMapFiles {
129     pub(super) file_maps: Vec<Lrc<SourceFile>>,
130     stable_id_to_source_file: FxHashMap<StableFilemapId, Lrc<SourceFile>>
131 }
132
133 pub struct SourceMap {
134     pub(super) files: Lock<SourceMapFiles>,
135     file_loader: Box<dyn FileLoader + Sync + Send>,
136     // This is used to apply the file path remapping as specified via
137     // --remap-path-prefix to all SourceFiles allocated within this SourceMap.
138     path_mapping: FilePathMapping,
139     /// In case we are in a doctest, replace all file names with the PathBuf,
140     /// and add the given offsets to the line info
141     doctest_offset: Option<(FileName, isize)>,
142 }
143
144 impl SourceMap {
145     pub fn new(path_mapping: FilePathMapping) -> SourceMap {
146         SourceMap {
147             files: Default::default(),
148             file_loader: Box::new(RealFileLoader),
149             path_mapping,
150             doctest_offset: None,
151         }
152     }
153
154     pub fn new_doctest(path_mapping: FilePathMapping,
155                        file: FileName, line: isize) -> SourceMap {
156         SourceMap {
157             doctest_offset: Some((file, line)),
158             ..SourceMap::new(path_mapping)
159         }
160
161     }
162
163     pub fn with_file_loader(file_loader: Box<dyn FileLoader + Sync + Send>,
164                             path_mapping: FilePathMapping)
165                             -> SourceMap {
166         SourceMap {
167             files: Default::default(),
168             file_loader: file_loader,
169             path_mapping,
170             doctest_offset: None,
171         }
172     }
173
174     pub fn path_mapping(&self) -> &FilePathMapping {
175         &self.path_mapping
176     }
177
178     pub fn file_exists(&self, path: &Path) -> bool {
179         self.file_loader.file_exists(path)
180     }
181
182     pub fn load_file(&self, path: &Path) -> io::Result<Lrc<SourceFile>> {
183         let src = self.file_loader.read_file(path)?;
184         let filename = if let Some((ref name, _)) = self.doctest_offset {
185             name.clone()
186         } else {
187             path.to_owned().into()
188         };
189         Ok(self.new_source_file(filename, src))
190     }
191
192     pub fn files(&self) -> MappedLockGuard<Vec<Lrc<SourceFile>>> {
193         LockGuard::map(self.files.borrow(), |files| &mut files.file_maps)
194     }
195
196     pub fn source_file_by_stable_id(&self, stable_id: StableFilemapId) -> Option<Lrc<SourceFile>> {
197         self.files.borrow().stable_id_to_source_file.get(&stable_id).map(|fm| fm.clone())
198     }
199
200     fn next_start_pos(&self) -> usize {
201         match self.files.borrow().file_maps.last() {
202             None => 0,
203             // Add one so there is some space between files. This lets us distinguish
204             // positions in the source_map, even in the presence of zero-length files.
205             Some(last) => last.end_pos.to_usize() + 1,
206         }
207     }
208
209     /// Creates a new source_file.
210     /// This does not ensure that only one SourceFile exists per file name.
211     pub fn new_source_file(&self, filename: FileName, src: String) -> Lrc<SourceFile> {
212         let start_pos = self.next_start_pos();
213
214         // The path is used to determine the directory for loading submodules and
215         // include files, so it must be before remapping.
216         // Note that filename may not be a valid path, eg it may be `<anon>` etc,
217         // but this is okay because the directory determined by `path.pop()` will
218         // be empty, so the working directory will be used.
219         let unmapped_path = filename.clone();
220
221         let (filename, was_remapped) = match filename {
222             FileName::Real(filename) => {
223                 let (filename, was_remapped) = self.path_mapping.map_prefix(filename);
224                 (FileName::Real(filename), was_remapped)
225             },
226             other => (other, false),
227         };
228         let source_file = Lrc::new(SourceFile::new(
229             filename,
230             was_remapped,
231             unmapped_path,
232             src,
233             Pos::from_usize(start_pos),
234         ));
235
236         let mut files = self.files.borrow_mut();
237
238         files.file_maps.push(source_file.clone());
239         files.stable_id_to_source_file.insert(StableFilemapId::new(&source_file),
240                                               source_file.clone());
241
242         source_file
243     }
244
245     /// Allocates a new SourceFile representing a source file from an external
246     /// crate. The source code of such an "imported source_file" is not available,
247     /// but we still know enough to generate accurate debuginfo location
248     /// information for things inlined from other crates.
249     pub fn new_imported_source_file(
250         &self,
251         filename: FileName,
252         name_was_remapped: bool,
253         crate_of_origin: u32,
254         src_hash: u128,
255         name_hash: u128,
256         source_len: usize,
257         mut file_local_lines: Vec<BytePos>,
258         mut file_local_multibyte_chars: Vec<MultiByteChar>,
259         mut file_local_non_narrow_chars: Vec<NonNarrowChar>,
260     ) -> Lrc<SourceFile> {
261         let start_pos = self.next_start_pos();
262
263         let end_pos = Pos::from_usize(start_pos + source_len);
264         let start_pos = Pos::from_usize(start_pos);
265
266         for pos in &mut file_local_lines {
267             *pos = *pos + start_pos;
268         }
269
270         for mbc in &mut file_local_multibyte_chars {
271             mbc.pos = mbc.pos + start_pos;
272         }
273
274         for swc in &mut file_local_non_narrow_chars {
275             *swc = *swc + start_pos;
276         }
277
278         let source_file = Lrc::new(SourceFile {
279             name: filename,
280             name_was_remapped,
281             unmapped_path: None,
282             crate_of_origin,
283             src: None,
284             src_hash,
285             external_src: Lock::new(ExternalSource::AbsentOk),
286             start_pos,
287             end_pos,
288             lines: file_local_lines,
289             multibyte_chars: file_local_multibyte_chars,
290             non_narrow_chars: file_local_non_narrow_chars,
291             name_hash,
292         });
293
294         let mut files = self.files.borrow_mut();
295
296         files.file_maps.push(source_file.clone());
297         files.stable_id_to_source_file.insert(StableFilemapId::new(&source_file),
298                                               source_file.clone());
299
300         source_file
301     }
302
303     pub fn mk_substr_filename(&self, sp: Span) -> String {
304         let pos = self.lookup_char_pos(sp.lo());
305         format!("<{}:{}:{}>",
306                  pos.file.name,
307                  pos.line,
308                  pos.col.to_usize() + 1)
309     }
310
311     // If there is a doctest_offset, apply it to the line
312     pub fn doctest_offset_line(&self, mut orig: usize) -> usize {
313         if let Some((_, line)) = self.doctest_offset {
314             if line >= 0 {
315                 orig = orig + line as usize;
316             } else {
317                 orig = orig - (-line) as usize;
318             }
319         }
320         orig
321     }
322
323     /// Lookup source information about a BytePos
324     pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
325         let chpos = self.bytepos_to_file_charpos(pos);
326         match self.lookup_line(pos) {
327             Ok(SourceFileAndLine { fm: f, line: a }) => {
328                 let line = a + 1; // Line numbers start at 1
329                 let linebpos = f.lines[a];
330                 let linechpos = self.bytepos_to_file_charpos(linebpos);
331                 let col = chpos - linechpos;
332
333                 let col_display = {
334                     let start_width_idx = f
335                         .non_narrow_chars
336                         .binary_search_by_key(&linebpos, |x| x.pos())
337                         .unwrap_or_else(|x| x);
338                     let end_width_idx = f
339                         .non_narrow_chars
340                         .binary_search_by_key(&pos, |x| x.pos())
341                         .unwrap_or_else(|x| x);
342                     let special_chars = end_width_idx - start_width_idx;
343                     let non_narrow: usize = f
344                         .non_narrow_chars[start_width_idx..end_width_idx]
345                         .into_iter()
346                         .map(|x| x.width())
347                         .sum();
348                     col.0 - special_chars + non_narrow
349                 };
350                 debug!("byte pos {:?} is on the line at byte pos {:?}",
351                        pos, linebpos);
352                 debug!("char pos {:?} is on the line at char pos {:?}",
353                        chpos, linechpos);
354                 debug!("byte is on line: {}", line);
355                 assert!(chpos >= linechpos);
356                 Loc {
357                     file: f,
358                     line,
359                     col,
360                     col_display,
361                 }
362             }
363             Err(f) => {
364                 let col_display = {
365                     let end_width_idx = f
366                         .non_narrow_chars
367                         .binary_search_by_key(&pos, |x| x.pos())
368                         .unwrap_or_else(|x| x);
369                     let non_narrow: usize = f
370                         .non_narrow_chars[0..end_width_idx]
371                         .into_iter()
372                         .map(|x| x.width())
373                         .sum();
374                     chpos.0 - end_width_idx + non_narrow
375                 };
376                 Loc {
377                     file: f,
378                     line: 0,
379                     col: chpos,
380                     col_display,
381                 }
382             }
383         }
384     }
385
386     // If the relevant source_file is empty, we don't return a line number.
387     pub fn lookup_line(&self, pos: BytePos) -> Result<SourceFileAndLine, Lrc<SourceFile>> {
388         let idx = self.lookup_source_file_idx(pos);
389
390         let f = (*self.files.borrow().file_maps)[idx].clone();
391
392         match f.lookup_line(pos) {
393             Some(line) => Ok(SourceFileAndLine { fm: f, line: line }),
394             None => Err(f)
395         }
396     }
397
398     pub fn lookup_char_pos_adj(&self, pos: BytePos) -> LocWithOpt {
399         let loc = self.lookup_char_pos(pos);
400         LocWithOpt {
401             filename: loc.file.name.clone(),
402             line: loc.line,
403             col: loc.col,
404             file: Some(loc.file)
405         }
406     }
407
408     /// Returns `Some(span)`, a union of the lhs and rhs span.  The lhs must precede the rhs. If
409     /// there are gaps between lhs and rhs, the resulting union will cross these gaps.
410     /// For this to work, the spans have to be:
411     ///
412     ///    * the ctxt of both spans much match
413     ///    * the lhs span needs to end on the same line the rhs span begins
414     ///    * the lhs span must start at or before the rhs span
415     pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
416         // make sure we're at the same expansion id
417         if sp_lhs.ctxt() != sp_rhs.ctxt() {
418             return None;
419         }
420
421         let lhs_end = match self.lookup_line(sp_lhs.hi()) {
422             Ok(x) => x,
423             Err(_) => return None
424         };
425         let rhs_begin = match self.lookup_line(sp_rhs.lo()) {
426             Ok(x) => x,
427             Err(_) => return None
428         };
429
430         // if we must cross lines to merge, don't merge
431         if lhs_end.line != rhs_begin.line {
432             return None;
433         }
434
435         // ensure these follow the expected order and we don't overlap
436         if (sp_lhs.lo() <= sp_rhs.lo()) && (sp_lhs.hi() <= sp_rhs.lo()) {
437             Some(sp_lhs.to(sp_rhs))
438         } else {
439             None
440         }
441     }
442
443     pub fn span_to_string(&self, sp: Span) -> String {
444         if self.files.borrow().file_maps.is_empty() && sp.is_dummy() {
445             return "no-location".to_string();
446         }
447
448         let lo = self.lookup_char_pos_adj(sp.lo());
449         let hi = self.lookup_char_pos_adj(sp.hi());
450         format!("{}:{}:{}: {}:{}",
451                         lo.filename,
452                         lo.line,
453                         lo.col.to_usize() + 1,
454                         hi.line,
455                         hi.col.to_usize() + 1)
456     }
457
458     pub fn span_to_filename(&self, sp: Span) -> FileName {
459         self.lookup_char_pos(sp.lo()).file.name.clone()
460     }
461
462     pub fn span_to_unmapped_path(&self, sp: Span) -> FileName {
463         self.lookup_char_pos(sp.lo()).file.unmapped_path.clone()
464             .expect("SourceMap::span_to_unmapped_path called for imported SourceFile?")
465     }
466
467     pub fn is_multiline(&self, sp: Span) -> bool {
468         let lo = self.lookup_char_pos(sp.lo());
469         let hi = self.lookup_char_pos(sp.hi());
470         lo.line != hi.line
471     }
472
473     pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
474         debug!("span_to_lines(sp={:?})", sp);
475
476         if sp.lo() > sp.hi() {
477             return Err(SpanLinesError::IllFormedSpan(sp));
478         }
479
480         let lo = self.lookup_char_pos(sp.lo());
481         debug!("span_to_lines: lo={:?}", lo);
482         let hi = self.lookup_char_pos(sp.hi());
483         debug!("span_to_lines: hi={:?}", hi);
484
485         if lo.file.start_pos != hi.file.start_pos {
486             return Err(SpanLinesError::DistinctSources(DistinctSources {
487                 begin: (lo.file.name.clone(), lo.file.start_pos),
488                 end: (hi.file.name.clone(), hi.file.start_pos),
489             }));
490         }
491         assert!(hi.line >= lo.line);
492
493         let mut lines = Vec::with_capacity(hi.line - lo.line + 1);
494
495         // The span starts partway through the first line,
496         // but after that it starts from offset 0.
497         let mut start_col = lo.col;
498
499         // For every line but the last, it extends from `start_col`
500         // and to the end of the line. Be careful because the line
501         // numbers in Loc are 1-based, so we subtract 1 to get 0-based
502         // lines.
503         for line_index in lo.line-1 .. hi.line-1 {
504             let line_len = lo.file.get_line(line_index)
505                                   .map(|s| s.chars().count())
506                                   .unwrap_or(0);
507             lines.push(LineInfo { line_index,
508                                   start_col,
509                                   end_col: CharPos::from_usize(line_len) });
510             start_col = CharPos::from_usize(0);
511         }
512
513         // For the last line, it extends from `start_col` to `hi.col`:
514         lines.push(LineInfo { line_index: hi.line - 1,
515                               start_col,
516                               end_col: hi.col });
517
518         Ok(FileLines {file: lo.file, lines: lines})
519     }
520
521     /// Extract the source surrounding the given `Span` using the `extract_source` function. The
522     /// extract function takes three arguments: a string slice containing the source, an index in
523     /// the slice for the beginning of the span and an index in the slice for the end of the span.
524     fn span_to_source<F>(&self, sp: Span, extract_source: F) -> Result<String, SpanSnippetError>
525         where F: Fn(&str, usize, usize) -> String
526     {
527         if sp.lo() > sp.hi() {
528             return Err(SpanSnippetError::IllFormedSpan(sp));
529         }
530
531         let local_begin = self.lookup_byte_offset(sp.lo());
532         let local_end = self.lookup_byte_offset(sp.hi());
533
534         if local_begin.fm.start_pos != local_end.fm.start_pos {
535             return Err(SpanSnippetError::DistinctSources(DistinctSources {
536                 begin: (local_begin.fm.name.clone(),
537                         local_begin.fm.start_pos),
538                 end: (local_end.fm.name.clone(),
539                       local_end.fm.start_pos)
540             }));
541         } else {
542             self.ensure_source_file_source_present(local_begin.fm.clone());
543
544             let start_index = local_begin.pos.to_usize();
545             let end_index = local_end.pos.to_usize();
546             let source_len = (local_begin.fm.end_pos -
547                               local_begin.fm.start_pos).to_usize();
548
549             if start_index > end_index || end_index > source_len {
550                 return Err(SpanSnippetError::MalformedForCodemap(
551                     MalformedCodemapPositions {
552                         name: local_begin.fm.name.clone(),
553                         source_len,
554                         begin_pos: local_begin.pos,
555                         end_pos: local_end.pos,
556                     }));
557             }
558
559             if let Some(ref src) = local_begin.fm.src {
560                 return Ok(extract_source(src, start_index, end_index));
561             } else if let Some(src) = local_begin.fm.external_src.borrow().get_source() {
562                 return Ok(extract_source(src, start_index, end_index));
563             } else {
564                 return Err(SpanSnippetError::SourceNotAvailable {
565                     filename: local_begin.fm.name.clone()
566                 });
567             }
568         }
569     }
570
571     /// Return the source snippet as `String` corresponding to the given `Span`
572     pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
573         self.span_to_source(sp, |src, start_index, end_index| src[start_index..end_index]
574                                                                 .to_string())
575     }
576
577     pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
578         match self.span_to_prev_source(sp) {
579             Err(_) => None,
580             Ok(source) => source.split('\n').last().map(|last_line| {
581                 last_line.len() - last_line.trim_left().len()
582             })
583         }
584     }
585
586     /// Return the source snippet as `String` before the given `Span`
587     pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
588         self.span_to_source(sp, |src, start_index, _| src[..start_index].to_string())
589     }
590
591     /// Extend the given `Span` to just after the previous occurrence of `c`. Return the same span
592     /// if no character could be found or if an error occurred while retrieving the code snippet.
593     pub fn span_extend_to_prev_char(&self, sp: Span, c: char) -> Span {
594         if let Ok(prev_source) = self.span_to_prev_source(sp) {
595             let prev_source = prev_source.rsplit(c).nth(0).unwrap_or("").trim_left();
596             if !prev_source.is_empty() && !prev_source.contains('\n') {
597                 return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
598             }
599         }
600
601         sp
602     }
603
604     /// Extend the given `Span` to just after the previous occurrence of `pat` when surrounded by
605     /// whitespace. Return the same span if no character could be found or if an error occurred
606     /// while retrieving the code snippet.
607     pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str, accept_newlines: bool) -> Span {
608         // assure that the pattern is delimited, to avoid the following
609         //     fn my_fn()
610         //           ^^^^ returned span without the check
611         //     ---------- correct span
612         for ws in &[" ", "\t", "\n"] {
613             let pat = pat.to_owned() + ws;
614             if let Ok(prev_source) = self.span_to_prev_source(sp) {
615                 let prev_source = prev_source.rsplit(&pat).nth(0).unwrap_or("").trim_left();
616                 if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) {
617                     return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
618                 }
619             }
620         }
621
622         sp
623     }
624
625     /// Given a `Span`, try to get a shorter span ending before the first occurrence of `c` `char`
626     pub fn span_until_char(&self, sp: Span, c: char) -> Span {
627         match self.span_to_snippet(sp) {
628             Ok(snippet) => {
629                 let snippet = snippet.split(c).nth(0).unwrap_or("").trim_right();
630                 if !snippet.is_empty() && !snippet.contains('\n') {
631                     sp.with_hi(BytePos(sp.lo().0 + snippet.len() as u32))
632                 } else {
633                     sp
634                 }
635             }
636             _ => sp,
637         }
638     }
639
640     /// Given a `Span`, try to get a shorter span ending just after the first occurrence of `char`
641     /// `c`.
642     pub fn span_through_char(&self, sp: Span, c: char) -> Span {
643         if let Ok(snippet) = self.span_to_snippet(sp) {
644             if let Some(offset) = snippet.find(c) {
645                 return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32));
646             }
647         }
648         sp
649     }
650
651     /// Given a `Span`, get a new `Span` covering the first token and all its trailing whitespace or
652     /// the original `Span`.
653     ///
654     /// If `sp` points to `"let mut x"`, then a span pointing at `"let "` will be returned.
655     pub fn span_until_non_whitespace(&self, sp: Span) -> Span {
656         let mut whitespace_found = false;
657
658         self.span_take_while(sp, |c| {
659             if !whitespace_found && c.is_whitespace() {
660                 whitespace_found = true;
661             }
662
663             if whitespace_found && !c.is_whitespace() {
664                 false
665             } else {
666                 true
667             }
668         })
669     }
670
671     /// Given a `Span`, get a new `Span` covering the first token without its trailing whitespace or
672     /// the original `Span` in case of error.
673     ///
674     /// If `sp` points to `"let mut x"`, then a span pointing at `"let"` will be returned.
675     pub fn span_until_whitespace(&self, sp: Span) -> Span {
676         self.span_take_while(sp, |c| !c.is_whitespace())
677     }
678
679     /// Given a `Span`, get a shorter one until `predicate` yields false.
680     pub fn span_take_while<P>(&self, sp: Span, predicate: P) -> Span
681         where P: for <'r> FnMut(&'r char) -> bool
682     {
683         if let Ok(snippet) = self.span_to_snippet(sp) {
684             let offset = snippet.chars()
685                 .take_while(predicate)
686                 .map(|c| c.len_utf8())
687                 .sum::<usize>();
688
689             sp.with_hi(BytePos(sp.lo().0 + (offset as u32)))
690         } else {
691             sp
692         }
693     }
694
695     pub fn def_span(&self, sp: Span) -> Span {
696         self.span_until_char(sp, '{')
697     }
698
699     /// Returns a new span representing just the start-point of this span
700     pub fn start_point(&self, sp: Span) -> Span {
701         let pos = sp.lo().0;
702         let width = self.find_width_of_character_at_span(sp, false);
703         let corrected_start_position = pos.checked_add(width).unwrap_or(pos);
704         let end_point = BytePos(cmp::max(corrected_start_position, sp.lo().0));
705         sp.with_hi(end_point)
706     }
707
708     /// Returns a new span representing just the end-point of this span
709     pub fn end_point(&self, sp: Span) -> Span {
710         let pos = sp.hi().0;
711
712         let width = self.find_width_of_character_at_span(sp, false);
713         let corrected_end_position = pos.checked_sub(width).unwrap_or(pos);
714
715         let end_point = BytePos(cmp::max(corrected_end_position, sp.lo().0));
716         sp.with_lo(end_point)
717     }
718
719     /// Returns a new span representing the next character after the end-point of this span
720     pub fn next_point(&self, sp: Span) -> Span {
721         let start_of_next_point = sp.hi().0;
722
723         let width = self.find_width_of_character_at_span(sp, true);
724         // If the width is 1, then the next span should point to the same `lo` and `hi`. However,
725         // in the case of a multibyte character, where the width != 1, the next span should
726         // span multiple bytes to include the whole character.
727         let end_of_next_point = start_of_next_point.checked_add(
728             width - 1).unwrap_or(start_of_next_point);
729
730         let end_of_next_point = BytePos(cmp::max(sp.lo().0 + 1, end_of_next_point));
731         Span::new(BytePos(start_of_next_point), end_of_next_point, sp.ctxt())
732     }
733
734     /// Finds the width of a character, either before or after the provided span.
735     fn find_width_of_character_at_span(&self, sp: Span, forwards: bool) -> u32 {
736         // Disregard malformed spans and assume a one-byte wide character.
737         if sp.lo() >= sp.hi() {
738             debug!("find_width_of_character_at_span: early return malformed span");
739             return 1;
740         }
741
742         let local_begin = self.lookup_byte_offset(sp.lo());
743         let local_end = self.lookup_byte_offset(sp.hi());
744         debug!("find_width_of_character_at_span: local_begin=`{:?}`, local_end=`{:?}`",
745                local_begin, local_end);
746
747         let start_index = local_begin.pos.to_usize();
748         let end_index = local_end.pos.to_usize();
749         debug!("find_width_of_character_at_span: start_index=`{:?}`, end_index=`{:?}`",
750                start_index, end_index);
751
752         // Disregard indexes that are at the start or end of their spans, they can't fit bigger
753         // characters.
754         if (!forwards && end_index == usize::min_value()) ||
755             (forwards && start_index == usize::max_value()) {
756             debug!("find_width_of_character_at_span: start or end of span, cannot be multibyte");
757             return 1;
758         }
759
760         let source_len = (local_begin.fm.end_pos - local_begin.fm.start_pos).to_usize();
761         debug!("find_width_of_character_at_span: source_len=`{:?}`", source_len);
762         // Ensure indexes are also not malformed.
763         if start_index > end_index || end_index > source_len {
764             debug!("find_width_of_character_at_span: source indexes are malformed");
765             return 1;
766         }
767
768         let src = local_begin.fm.external_src.borrow();
769
770         // We need to extend the snippet to the end of the src rather than to end_index so when
771         // searching forwards for boundaries we've got somewhere to search.
772         let snippet = if let Some(ref src) = local_begin.fm.src {
773             let len = src.len();
774             (&src[start_index..len])
775         } else if let Some(src) = src.get_source() {
776             let len = src.len();
777             (&src[start_index..len])
778         } else {
779             return 1;
780         };
781         debug!("find_width_of_character_at_span: snippet=`{:?}`", snippet);
782
783         let mut target = if forwards { end_index + 1 } else { end_index - 1 };
784         debug!("find_width_of_character_at_span: initial target=`{:?}`", target);
785
786         while !snippet.is_char_boundary(target - start_index) && target < source_len {
787             target = if forwards {
788                 target + 1
789             } else {
790                 match target.checked_sub(1) {
791                     Some(target) => target,
792                     None => {
793                         break;
794                     }
795                 }
796             };
797             debug!("find_width_of_character_at_span: target=`{:?}`", target);
798         }
799         debug!("find_width_of_character_at_span: final target=`{:?}`", target);
800
801         if forwards {
802             (target - end_index) as u32
803         } else {
804             (end_index - target) as u32
805         }
806     }
807
808     pub fn get_source_file(&self, filename: &FileName) -> Option<Lrc<SourceFile>> {
809         for fm in self.files.borrow().file_maps.iter() {
810             if *filename == fm.name {
811                 return Some(fm.clone());
812             }
813         }
814         None
815     }
816
817     /// For a global BytePos compute the local offset within the containing SourceFile
818     pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos {
819         let idx = self.lookup_source_file_idx(bpos);
820         let fm = (*self.files.borrow().file_maps)[idx].clone();
821         let offset = bpos - fm.start_pos;
822         SourceFileAndBytePos {fm: fm, pos: offset}
823     }
824
825     /// Converts an absolute BytePos to a CharPos relative to the source_file.
826     pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos {
827         let idx = self.lookup_source_file_idx(bpos);
828         let map = &(*self.files.borrow().file_maps)[idx];
829
830         // The number of extra bytes due to multibyte chars in the SourceFile
831         let mut total_extra_bytes = 0;
832
833         for mbc in map.multibyte_chars.iter() {
834             debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos);
835             if mbc.pos < bpos {
836                 // every character is at least one byte, so we only
837                 // count the actual extra bytes.
838                 total_extra_bytes += mbc.bytes as u32 - 1;
839                 // We should never see a byte position in the middle of a
840                 // character
841                 assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32);
842             } else {
843                 break;
844             }
845         }
846
847         assert!(map.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32());
848         CharPos(bpos.to_usize() - map.start_pos.to_usize() - total_extra_bytes as usize)
849     }
850
851     // Return the index of the source_file (in self.files) which contains pos.
852     pub fn lookup_source_file_idx(&self, pos: BytePos) -> usize {
853         let files = self.files.borrow();
854         let files = &files.file_maps;
855         let count = files.len();
856
857         // Binary search for the source_file.
858         let mut a = 0;
859         let mut b = count;
860         while b - a > 1 {
861             let m = (a + b) / 2;
862             if files[m].start_pos > pos {
863                 b = m;
864             } else {
865                 a = m;
866             }
867         }
868
869         assert!(a < count, "position {} does not resolve to a source location", pos.to_usize());
870
871         return a;
872     }
873
874     pub fn count_lines(&self) -> usize {
875         self.files().iter().fold(0, |a, f| a + f.count_lines())
876     }
877
878
879     pub fn generate_fn_name_span(&self, span: Span) -> Option<Span> {
880         let prev_span = self.span_extend_to_prev_str(span, "fn", true);
881         self.span_to_snippet(prev_span).map(|snippet| {
882             let len = snippet.find(|c: char| !c.is_alphanumeric() && c != '_')
883                 .expect("no label after fn");
884             prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32))
885         }).ok()
886     }
887
888     /// Take the span of a type parameter in a function signature and try to generate a span for the
889     /// function name (with generics) and a new snippet for this span with the pointed type
890     /// parameter as a new local type parameter.
891     ///
892     /// For instance:
893     /// ```rust,ignore (pseudo-Rust)
894     /// // Given span
895     /// fn my_function(param: T)
896     /// //                    ^ Original span
897     ///
898     /// // Result
899     /// fn my_function(param: T)
900     /// // ^^^^^^^^^^^ Generated span with snippet `my_function<T>`
901     /// ```
902     ///
903     /// Attention: The method used is very fragile since it essentially duplicates the work of the
904     /// parser. If you need to use this function or something similar, please consider updating the
905     /// source_map functions and this function to something more robust.
906     pub fn generate_local_type_param_snippet(&self, span: Span) -> Option<(Span, String)> {
907         // Try to extend the span to the previous "fn" keyword to retrieve the function
908         // signature
909         let sugg_span = self.span_extend_to_prev_str(span, "fn", false);
910         if sugg_span != span {
911             if let Ok(snippet) = self.span_to_snippet(sugg_span) {
912                 // Consume the function name
913                 let mut offset = snippet.find(|c: char| !c.is_alphanumeric() && c != '_')
914                     .expect("no label after fn");
915
916                 // Consume the generics part of the function signature
917                 let mut bracket_counter = 0;
918                 let mut last_char = None;
919                 for c in snippet[offset..].chars() {
920                     match c {
921                         '<' => bracket_counter += 1,
922                         '>' => bracket_counter -= 1,
923                         '(' => if bracket_counter == 0 { break; }
924                         _ => {}
925                     }
926                     offset += c.len_utf8();
927                     last_char = Some(c);
928                 }
929
930                 // Adjust the suggestion span to encompass the function name with its generics
931                 let sugg_span = sugg_span.with_hi(BytePos(sugg_span.lo().0 + offset as u32));
932
933                 // Prepare the new suggested snippet to append the type parameter that triggered
934                 // the error in the generics of the function signature
935                 let mut new_snippet = if last_char == Some('>') {
936                     format!("{}, ", &snippet[..(offset - '>'.len_utf8())])
937                 } else {
938                     format!("{}<", &snippet[..offset])
939                 };
940                 new_snippet.push_str(
941                     &self.span_to_snippet(span).unwrap_or_else(|_| "T".to_string()));
942                 new_snippet.push('>');
943
944                 return Some((sugg_span, new_snippet));
945             }
946         }
947
948         None
949     }
950 }
951
952 impl SourceMapper for SourceMap {
953     fn lookup_char_pos(&self, pos: BytePos) -> Loc {
954         self.lookup_char_pos(pos)
955     }
956     fn span_to_lines(&self, sp: Span) -> FileLinesResult {
957         self.span_to_lines(sp)
958     }
959     fn span_to_string(&self, sp: Span) -> String {
960         self.span_to_string(sp)
961     }
962     fn span_to_filename(&self, sp: Span) -> FileName {
963         self.span_to_filename(sp)
964     }
965     fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
966         self.merge_spans(sp_lhs, sp_rhs)
967     }
968     fn call_span_if_macro(&self, sp: Span) -> Span {
969         if self.span_to_filename(sp.clone()).is_macros() {
970             let v = sp.macro_backtrace();
971             if let Some(use_site) = v.last() {
972                 return use_site.call_site;
973             }
974         }
975         sp
976     }
977     fn ensure_source_file_source_present(&self, file_map: Lrc<SourceFile>) -> bool {
978         file_map.add_external_src(
979             || match file_map.name {
980                 FileName::Real(ref name) => self.file_loader.read_file(name).ok(),
981                 _ => None,
982             }
983         )
984     }
985     fn doctest_offset_line(&self, line: usize) -> usize {
986         self.doctest_offset_line(line)
987     }
988 }
989
990 #[derive(Clone)]
991 pub struct FilePathMapping {
992     mapping: Vec<(PathBuf, PathBuf)>,
993 }
994
995 impl FilePathMapping {
996     pub fn empty() -> FilePathMapping {
997         FilePathMapping {
998             mapping: vec![]
999         }
1000     }
1001
1002     pub fn new(mapping: Vec<(PathBuf, PathBuf)>) -> FilePathMapping {
1003         FilePathMapping {
1004             mapping,
1005         }
1006     }
1007
1008     /// Applies any path prefix substitution as defined by the mapping.
1009     /// The return value is the remapped path and a boolean indicating whether
1010     /// the path was affected by the mapping.
1011     pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) {
1012         // NOTE: We are iterating over the mapping entries from last to first
1013         //       because entries specified later on the command line should
1014         //       take precedence.
1015         for &(ref from, ref to) in self.mapping.iter().rev() {
1016             if let Ok(rest) = path.strip_prefix(from) {
1017                 return (to.join(rest), true);
1018             }
1019         }
1020
1021         (path, false)
1022     }
1023 }
1024
1025 // _____________________________________________________________________________
1026 // Tests
1027 //
1028
1029 #[cfg(test)]
1030 mod tests {
1031     use super::*;
1032     use rustc_data_structures::sync::Lrc;
1033
1034     fn init_code_map() -> SourceMap {
1035         let cm = SourceMap::new(FilePathMapping::empty());
1036         cm.new_source_file(PathBuf::from("blork.rs").into(),
1037                        "first line.\nsecond line".to_string());
1038         cm.new_source_file(PathBuf::from("empty.rs").into(),
1039                        String::new());
1040         cm.new_source_file(PathBuf::from("blork2.rs").into(),
1041                        "first line.\nsecond line".to_string());
1042         cm
1043     }
1044
1045     #[test]
1046     fn t3() {
1047         // Test lookup_byte_offset
1048         let cm = init_code_map();
1049
1050         let fmabp1 = cm.lookup_byte_offset(BytePos(23));
1051         assert_eq!(fmabp1.fm.name, PathBuf::from("blork.rs").into());
1052         assert_eq!(fmabp1.pos, BytePos(23));
1053
1054         let fmabp1 = cm.lookup_byte_offset(BytePos(24));
1055         assert_eq!(fmabp1.fm.name, PathBuf::from("empty.rs").into());
1056         assert_eq!(fmabp1.pos, BytePos(0));
1057
1058         let fmabp2 = cm.lookup_byte_offset(BytePos(25));
1059         assert_eq!(fmabp2.fm.name, PathBuf::from("blork2.rs").into());
1060         assert_eq!(fmabp2.pos, BytePos(0));
1061     }
1062
1063     #[test]
1064     fn t4() {
1065         // Test bytepos_to_file_charpos
1066         let cm = init_code_map();
1067
1068         let cp1 = cm.bytepos_to_file_charpos(BytePos(22));
1069         assert_eq!(cp1, CharPos(22));
1070
1071         let cp2 = cm.bytepos_to_file_charpos(BytePos(25));
1072         assert_eq!(cp2, CharPos(0));
1073     }
1074
1075     #[test]
1076     fn t5() {
1077         // Test zero-length source_files.
1078         let cm = init_code_map();
1079
1080         let loc1 = cm.lookup_char_pos(BytePos(22));
1081         assert_eq!(loc1.file.name, PathBuf::from("blork.rs").into());
1082         assert_eq!(loc1.line, 2);
1083         assert_eq!(loc1.col, CharPos(10));
1084
1085         let loc2 = cm.lookup_char_pos(BytePos(25));
1086         assert_eq!(loc2.file.name, PathBuf::from("blork2.rs").into());
1087         assert_eq!(loc2.line, 1);
1088         assert_eq!(loc2.col, CharPos(0));
1089     }
1090
1091     fn init_code_map_mbc() -> SourceMap {
1092         let cm = SourceMap::new(FilePathMapping::empty());
1093         // € is a three byte utf8 char.
1094         cm.new_source_file(PathBuf::from("blork.rs").into(),
1095                        "fir€st €€€€ line.\nsecond line".to_string());
1096         cm.new_source_file(PathBuf::from("blork2.rs").into(),
1097                        "first line€€.\n€ second line".to_string());
1098         cm
1099     }
1100
1101     #[test]
1102     fn t6() {
1103         // Test bytepos_to_file_charpos in the presence of multi-byte chars
1104         let cm = init_code_map_mbc();
1105
1106         let cp1 = cm.bytepos_to_file_charpos(BytePos(3));
1107         assert_eq!(cp1, CharPos(3));
1108
1109         let cp2 = cm.bytepos_to_file_charpos(BytePos(6));
1110         assert_eq!(cp2, CharPos(4));
1111
1112         let cp3 = cm.bytepos_to_file_charpos(BytePos(56));
1113         assert_eq!(cp3, CharPos(12));
1114
1115         let cp4 = cm.bytepos_to_file_charpos(BytePos(61));
1116         assert_eq!(cp4, CharPos(15));
1117     }
1118
1119     #[test]
1120     fn t7() {
1121         // Test span_to_lines for a span ending at the end of source_file
1122         let cm = init_code_map();
1123         let span = Span::new(BytePos(12), BytePos(23), NO_EXPANSION);
1124         let file_lines = cm.span_to_lines(span).unwrap();
1125
1126         assert_eq!(file_lines.file.name, PathBuf::from("blork.rs").into());
1127         assert_eq!(file_lines.lines.len(), 1);
1128         assert_eq!(file_lines.lines[0].line_index, 1);
1129     }
1130
1131     /// Given a string like " ~~~~~~~~~~~~ ", produces a span
1132     /// converting that range. The idea is that the string has the same
1133     /// length as the input, and we uncover the byte positions.  Note
1134     /// that this can span lines and so on.
1135     fn span_from_selection(input: &str, selection: &str) -> Span {
1136         assert_eq!(input.len(), selection.len());
1137         let left_index = selection.find('~').unwrap() as u32;
1138         let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
1139         Span::new(BytePos(left_index), BytePos(right_index + 1), NO_EXPANSION)
1140     }
1141
1142     /// Test span_to_snippet and span_to_lines for a span converting 3
1143     /// lines in the middle of a file.
1144     #[test]
1145     fn span_to_snippet_and_lines_spanning_multiple_lines() {
1146         let cm = SourceMap::new(FilePathMapping::empty());
1147         let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
1148         let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
1149         cm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_string());
1150         let span = span_from_selection(inputtext, selection);
1151
1152         // check that we are extracting the text we thought we were extracting
1153         assert_eq!(&cm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD");
1154
1155         // check that span_to_lines gives us the complete result with the lines/cols we expected
1156         let lines = cm.span_to_lines(span).unwrap();
1157         let expected = vec![
1158             LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) },
1159             LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) },
1160             LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }
1161             ];
1162         assert_eq!(lines.lines, expected);
1163     }
1164
1165     #[test]
1166     fn t8() {
1167         // Test span_to_snippet for a span ending at the end of source_file
1168         let cm = init_code_map();
1169         let span = Span::new(BytePos(12), BytePos(23), NO_EXPANSION);
1170         let snippet = cm.span_to_snippet(span);
1171
1172         assert_eq!(snippet, Ok("second line".to_string()));
1173     }
1174
1175     #[test]
1176     fn t9() {
1177         // Test span_to_str for a span ending at the end of source_file
1178         let cm = init_code_map();
1179         let span = Span::new(BytePos(12), BytePos(23), NO_EXPANSION);
1180         let sstr =  cm.span_to_string(span);
1181
1182         assert_eq!(sstr, "blork.rs:2:1: 2:12");
1183     }
1184
1185     /// Test failing to merge two spans on different lines
1186     #[test]
1187     fn span_merging_fail() {
1188         let cm = SourceMap::new(FilePathMapping::empty());
1189         let inputtext  = "bbbb BB\ncc CCC\n";
1190         let selection1 = "     ~~\n      \n";
1191         let selection2 = "       \n   ~~~\n";
1192         cm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_owned());
1193         let span1 = span_from_selection(inputtext, selection1);
1194         let span2 = span_from_selection(inputtext, selection2);
1195
1196         assert!(cm.merge_spans(span1, span2).is_none());
1197     }
1198
1199     /// Returns the span corresponding to the `n`th occurrence of
1200     /// `substring` in `source_text`.
1201     trait SourceMapExtension {
1202         fn span_substr(&self,
1203                     file: &Lrc<SourceFile>,
1204                     source_text: &str,
1205                     substring: &str,
1206                     n: usize)
1207                     -> Span;
1208     }
1209
1210     impl SourceMapExtension for SourceMap {
1211         fn span_substr(&self,
1212                     file: &Lrc<SourceFile>,
1213                     source_text: &str,
1214                     substring: &str,
1215                     n: usize)
1216                     -> Span
1217         {
1218             println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
1219                     file.name, file.start_pos, substring, n);
1220             let mut i = 0;
1221             let mut hi = 0;
1222             loop {
1223                 let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
1224                     panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
1225                         source_text, n, substring, i);
1226                 });
1227                 let lo = hi + offset;
1228                 hi = lo + substring.len();
1229                 if i == n {
1230                     let span = Span::new(
1231                         BytePos(lo as u32 + file.start_pos.0),
1232                         BytePos(hi as u32 + file.start_pos.0),
1233                         NO_EXPANSION,
1234                     );
1235                     assert_eq!(&self.span_to_snippet(span).unwrap()[..],
1236                             substring);
1237                     return span;
1238                 }
1239                 i += 1;
1240             }
1241         }
1242     }
1243 }