]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/source_map.rs
Auto merge of #64878 - XAMPPRocky:relnotes-1.39.0, r=XAMPPRocky
[rust.git] / src / libsyntax / source_map.rs
1 //! The `SourceMap` tracks all the source code used within a single crate, mapping
2 //! from integer byte positions to the original source code location. Each bit
3 //! of source parsed during crate parsing (typically files, in-memory strings,
4 //! or various bits of macro expansion) cover a continuous range of bytes in the
5 //! `SourceMap` and are represented by `SourceFile`s. Byte positions are stored in
6 //! `Span` and used pervasively in the compiler. They are absolute positions
7 //! within the `SourceMap`, which upon request can be converted to line and column
8 //! information, source code snippets, etc.
9
10 pub use syntax_pos::*;
11 pub use syntax_pos::hygiene::{ExpnKind, ExpnData};
12
13 use rustc_data_structures::fx::FxHashMap;
14 use rustc_data_structures::stable_hasher::StableHasher;
15 use rustc_data_structures::sync::{Lrc, Lock, LockGuard, MappedLockGuard};
16 use std::cmp;
17 use std::hash::Hash;
18 use std::path::{Path, PathBuf};
19
20 use std::env;
21 use std::fs;
22 use std::io;
23 use log::debug;
24
25 use errors::SourceMapper;
26
27 #[cfg(test)]
28 mod tests;
29
30 /// Returns the span itself if it doesn't come from a macro expansion,
31 /// otherwise return the call site span up to the `enclosing_sp` by
32 /// following the `expn_data` chain.
33 pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span {
34     let expn_data1 = sp.ctxt().outer_expn_data();
35     let expn_data2 = enclosing_sp.ctxt().outer_expn_data();
36     if expn_data1.is_root() ||
37        !expn_data2.is_root() && expn_data1.call_site == expn_data2.call_site {
38         sp
39     } else {
40         original_sp(expn_data1.call_site, enclosing_sp)
41     }
42 }
43
44 #[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy)]
45 pub struct Spanned<T> {
46     pub node: T,
47     pub span: Span,
48 }
49
50 pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
51     Spanned {node: t, span: sp}
52 }
53
54 pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
55     respan(DUMMY_SP, t)
56 }
57
58 // _____________________________________________________________________________
59 // SourceFile, MultiByteChar, FileName, FileLines
60 //
61
62 /// An abstraction over the fs operations used by the Parser.
63 pub trait FileLoader {
64     /// Query the existence of a file.
65     fn file_exists(&self, path: &Path) -> bool;
66
67     /// Returns an absolute path to a file, if possible.
68     fn abs_path(&self, path: &Path) -> Option<PathBuf>;
69
70     /// Read the contents of an UTF-8 file into memory.
71     fn read_file(&self, path: &Path) -> io::Result<String>;
72 }
73
74 /// A FileLoader that uses std::fs to load real files.
75 pub struct RealFileLoader;
76
77 impl FileLoader for RealFileLoader {
78     fn file_exists(&self, path: &Path) -> bool {
79         fs::metadata(path).is_ok()
80     }
81
82     fn abs_path(&self, path: &Path) -> Option<PathBuf> {
83         if path.is_absolute() {
84             Some(path.to_path_buf())
85         } else {
86             env::current_dir()
87                 .ok()
88                 .map(|cwd| cwd.join(path))
89         }
90     }
91
92     fn read_file(&self, path: &Path) -> io::Result<String> {
93         fs::read_to_string(path)
94     }
95 }
96
97 // This is a `SourceFile` identifier that is used to correlate `SourceFile`s between
98 // subsequent compilation sessions (which is something we need to do during
99 // incremental compilation).
100 #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
101 pub struct StableSourceFileId(u128);
102
103 impl StableSourceFileId {
104     pub fn new(source_file: &SourceFile) -> StableSourceFileId {
105         StableSourceFileId::new_from_pieces(&source_file.name,
106                                             source_file.name_was_remapped,
107                                             source_file.unmapped_path.as_ref())
108     }
109
110     pub fn new_from_pieces(name: &FileName,
111                            name_was_remapped: bool,
112                            unmapped_path: Option<&FileName>) -> StableSourceFileId {
113         let mut hasher = StableHasher::new();
114
115         name.hash(&mut hasher);
116         name_was_remapped.hash(&mut hasher);
117         unmapped_path.hash(&mut hasher);
118
119         StableSourceFileId(hasher.finish())
120     }
121 }
122
123 // _____________________________________________________________________________
124 // SourceMap
125 //
126
127 #[derive(Default)]
128 pub(super) struct SourceMapFiles {
129     source_files: Vec<Lrc<SourceFile>>,
130     stable_id_to_source_file: FxHashMap<StableSourceFileId, Lrc<SourceFile>>
131 }
132
133 pub struct SourceMap {
134     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 `SourceFile`s allocated within this `SourceMap`.
138     path_mapping: FilePathMapping,
139 }
140
141 impl SourceMap {
142     pub fn new(path_mapping: FilePathMapping) -> SourceMap {
143         SourceMap {
144             files: Default::default(),
145             file_loader: Box::new(RealFileLoader),
146             path_mapping,
147         }
148     }
149
150     pub fn with_file_loader(file_loader: Box<dyn FileLoader + Sync + Send>,
151                             path_mapping: FilePathMapping)
152                             -> SourceMap {
153         SourceMap {
154             files: Default::default(),
155             file_loader,
156             path_mapping,
157         }
158     }
159
160     pub fn path_mapping(&self) -> &FilePathMapping {
161         &self.path_mapping
162     }
163
164     pub fn file_exists(&self, path: &Path) -> bool {
165         self.file_loader.file_exists(path)
166     }
167
168     pub fn load_file(&self, path: &Path) -> io::Result<Lrc<SourceFile>> {
169         let src = self.file_loader.read_file(path)?;
170         let filename = path.to_owned().into();
171         Ok(self.new_source_file(filename, src))
172     }
173
174     /// Loads source file as a binary blob.
175     ///
176     /// Unlike `load_file`, guarantees that no normalization like BOM-removal
177     /// takes place.
178     pub fn load_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
179         // Ideally, this should use `self.file_loader`, but it can't
180         // deal with binary files yet.
181         let bytes = fs::read(path)?;
182
183         // We need to add file to the `SourceMap`, so that it is present
184         // in dep-info. There's also an edge case that file might be both
185         // loaded as a binary via `include_bytes!` and as proper `SourceFile`
186         // via `mod`, so we try to use real file contents and not just an
187         // empty string.
188         let text = std::str::from_utf8(&bytes).unwrap_or("")
189             .to_string();
190         self.new_source_file(path.to_owned().into(), text);
191         Ok(bytes)
192     }
193
194     pub fn files(&self) -> MappedLockGuard<'_, Vec<Lrc<SourceFile>>> {
195         LockGuard::map(self.files.borrow(), |files| &mut files.source_files)
196     }
197
198     pub fn source_file_by_stable_id(&self, stable_id: StableSourceFileId) ->
199     Option<Lrc<SourceFile>> {
200         self.files.borrow().stable_id_to_source_file.get(&stable_id).map(|sf| sf.clone())
201     }
202
203     fn next_start_pos(&self) -> usize {
204         match self.files.borrow().source_files.last() {
205             None => 0,
206             // Add one so there is some space between files. This lets us distinguish
207             // positions in the `SourceMap`, even in the presence of zero-length files.
208             Some(last) => last.end_pos.to_usize() + 1,
209         }
210     }
211
212     /// Creates a new `SourceFile`.
213     /// If a file already exists in the `SourceMap` with the same ID, that file is returned
214     /// unmodified.
215     pub fn new_source_file(&self, filename: FileName, src: String) -> Lrc<SourceFile> {
216         self.try_new_source_file(filename, src)
217             .unwrap_or_else(|OffsetOverflowError| {
218                 eprintln!("fatal error: rustc does not support files larger than 4GB");
219                 errors::FatalError.raise()
220             })
221     }
222
223     fn try_new_source_file(
224         &self,
225         filename: FileName,
226         src: String
227     ) -> Result<Lrc<SourceFile>, OffsetOverflowError> {
228         let start_pos = self.next_start_pos();
229
230         // The path is used to determine the directory for loading submodules and
231         // include files, so it must be before remapping.
232         // Note that filename may not be a valid path, eg it may be `<anon>` etc,
233         // but this is okay because the directory determined by `path.pop()` will
234         // be empty, so the working directory will be used.
235         let unmapped_path = filename.clone();
236
237         let (filename, was_remapped) = match filename {
238             FileName::Real(filename) => {
239                 let (filename, was_remapped) = self.path_mapping.map_prefix(filename);
240                 (FileName::Real(filename), was_remapped)
241             },
242             other => (other, false),
243         };
244
245         let file_id = StableSourceFileId::new_from_pieces(&filename,
246                                                        was_remapped,
247                                                        Some(&unmapped_path));
248
249         let lrc_sf = match self.source_file_by_stable_id(file_id) {
250             Some(lrc_sf) => lrc_sf,
251             None => {
252                 let source_file = Lrc::new(SourceFile::new(
253                     filename,
254                     was_remapped,
255                     unmapped_path,
256                     src,
257                     Pos::from_usize(start_pos),
258                 )?);
259
260                 let mut files = self.files.borrow_mut();
261
262                 files.source_files.push(source_file.clone());
263                 files.stable_id_to_source_file.insert(file_id, source_file.clone());
264
265                 source_file
266             }
267         };
268         Ok(lrc_sf)
269     }
270
271     /// Allocates a new `SourceFile` representing a source file from an external
272     /// crate. The source code of such an "imported `SourceFile`" is not available,
273     /// but we still know enough to generate accurate debuginfo location
274     /// information for things inlined from other crates.
275     pub fn new_imported_source_file(
276         &self,
277         filename: FileName,
278         name_was_remapped: bool,
279         crate_of_origin: u32,
280         src_hash: u128,
281         name_hash: u128,
282         source_len: usize,
283         mut file_local_lines: Vec<BytePos>,
284         mut file_local_multibyte_chars: Vec<MultiByteChar>,
285         mut file_local_non_narrow_chars: Vec<NonNarrowChar>,
286         mut file_local_normalized_pos: Vec<NormalizedPos>,
287     ) -> Lrc<SourceFile> {
288         let start_pos = self.next_start_pos();
289
290         let end_pos = Pos::from_usize(start_pos + source_len);
291         let start_pos = Pos::from_usize(start_pos);
292
293         for pos in &mut file_local_lines {
294             *pos = *pos + start_pos;
295         }
296
297         for mbc in &mut file_local_multibyte_chars {
298             mbc.pos = mbc.pos + start_pos;
299         }
300
301         for swc in &mut file_local_non_narrow_chars {
302             *swc = *swc + start_pos;
303         }
304
305         for nc in &mut file_local_normalized_pos {
306             nc.pos = nc.pos + start_pos;
307         }
308
309         let source_file = Lrc::new(SourceFile {
310             name: filename,
311             name_was_remapped,
312             unmapped_path: None,
313             crate_of_origin,
314             src: None,
315             src_hash,
316             external_src: Lock::new(ExternalSource::AbsentOk),
317             start_pos,
318             end_pos,
319             lines: file_local_lines,
320             multibyte_chars: file_local_multibyte_chars,
321             non_narrow_chars: file_local_non_narrow_chars,
322             normalized_pos: file_local_normalized_pos,
323             name_hash,
324         });
325
326         let mut files = self.files.borrow_mut();
327
328         files.source_files.push(source_file.clone());
329         files.stable_id_to_source_file.insert(StableSourceFileId::new(&source_file),
330                                               source_file.clone());
331
332         source_file
333     }
334
335     pub fn mk_substr_filename(&self, sp: Span) -> String {
336         let pos = self.lookup_char_pos(sp.lo());
337         format!("<{}:{}:{}>",
338                  pos.file.name,
339                  pos.line,
340                  pos.col.to_usize() + 1)
341     }
342
343     // If there is a doctest offset, applies it to the line.
344     pub fn doctest_offset_line(&self, file: &FileName, orig: usize) -> usize {
345         return match file {
346             FileName::DocTest(_, offset) => {
347                 return if *offset >= 0 {
348                     orig + *offset as usize
349                 } else {
350                     orig - (-(*offset)) as usize
351                 }
352             },
353             _ => orig
354         }
355     }
356
357     /// Looks up source information about a `BytePos`.
358     pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
359         let chpos = self.bytepos_to_file_charpos(pos);
360         match self.lookup_line(pos) {
361             Ok(SourceFileAndLine { sf: f, line: a }) => {
362                 let line = a + 1; // Line numbers start at 1
363                 let linebpos = f.lines[a];
364                 let linechpos = self.bytepos_to_file_charpos(linebpos);
365                 let col = chpos - linechpos;
366
367                 let col_display = {
368                     let start_width_idx = f
369                         .non_narrow_chars
370                         .binary_search_by_key(&linebpos, |x| x.pos())
371                         .unwrap_or_else(|x| x);
372                     let end_width_idx = f
373                         .non_narrow_chars
374                         .binary_search_by_key(&pos, |x| x.pos())
375                         .unwrap_or_else(|x| x);
376                     let special_chars = end_width_idx - start_width_idx;
377                     let non_narrow: usize = f
378                         .non_narrow_chars[start_width_idx..end_width_idx]
379                         .into_iter()
380                         .map(|x| x.width())
381                         .sum();
382                     col.0 - special_chars + non_narrow
383                 };
384                 debug!("byte pos {:?} is on the line at byte pos {:?}",
385                        pos, linebpos);
386                 debug!("char pos {:?} is on the line at char pos {:?}",
387                        chpos, linechpos);
388                 debug!("byte is on line: {}", line);
389                 assert!(chpos >= linechpos);
390                 Loc {
391                     file: f,
392                     line,
393                     col,
394                     col_display,
395                 }
396             }
397             Err(f) => {
398                 let col_display = {
399                     let end_width_idx = f
400                         .non_narrow_chars
401                         .binary_search_by_key(&pos, |x| x.pos())
402                         .unwrap_or_else(|x| x);
403                     let non_narrow: usize = f
404                         .non_narrow_chars[0..end_width_idx]
405                         .into_iter()
406                         .map(|x| x.width())
407                         .sum();
408                     chpos.0 - end_width_idx + non_narrow
409                 };
410                 Loc {
411                     file: f,
412                     line: 0,
413                     col: chpos,
414                     col_display,
415                 }
416             }
417         }
418     }
419
420     // If the corresponding `SourceFile` is empty, does not return a line number.
421     pub fn lookup_line(&self, pos: BytePos) -> Result<SourceFileAndLine, Lrc<SourceFile>> {
422         let idx = self.lookup_source_file_idx(pos);
423
424         let f = (*self.files.borrow().source_files)[idx].clone();
425
426         match f.lookup_line(pos) {
427             Some(line) => Ok(SourceFileAndLine { sf: f, line }),
428             None => Err(f)
429         }
430     }
431
432     /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If
433     /// there are gaps between LHS and RHS, the resulting union will cross these gaps.
434     /// For this to work,
435     ///
436     ///    * the syntax contexts of both spans much match,
437     ///    * the LHS span needs to end on the same line the RHS span begins,
438     ///    * the LHS span must start at or before the RHS span.
439     pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
440         // Ensure we're at the same expansion ID.
441         if sp_lhs.ctxt() != sp_rhs.ctxt() {
442             return None;
443         }
444
445         let lhs_end = match self.lookup_line(sp_lhs.hi()) {
446             Ok(x) => x,
447             Err(_) => return None
448         };
449         let rhs_begin = match self.lookup_line(sp_rhs.lo()) {
450             Ok(x) => x,
451             Err(_) => return None
452         };
453
454         // If we must cross lines to merge, don't merge.
455         if lhs_end.line != rhs_begin.line {
456             return None;
457         }
458
459         // Ensure these follow the expected order and that we don't overlap.
460         if (sp_lhs.lo() <= sp_rhs.lo()) && (sp_lhs.hi() <= sp_rhs.lo()) {
461             Some(sp_lhs.to(sp_rhs))
462         } else {
463             None
464         }
465     }
466
467     pub fn span_to_string(&self, sp: Span) -> String {
468         if self.files.borrow().source_files.is_empty() && sp.is_dummy() {
469             return "no-location".to_string();
470         }
471
472         let lo = self.lookup_char_pos(sp.lo());
473         let hi = self.lookup_char_pos(sp.hi());
474         format!("{}:{}:{}: {}:{}",
475             lo.file.name,
476             lo.line,
477             lo.col.to_usize() + 1,
478             hi.line,
479             hi.col.to_usize() + 1,
480         )
481     }
482
483     pub fn span_to_filename(&self, sp: Span) -> FileName {
484         self.lookup_char_pos(sp.lo()).file.name.clone()
485     }
486
487     pub fn span_to_unmapped_path(&self, sp: Span) -> FileName {
488         self.lookup_char_pos(sp.lo()).file.unmapped_path.clone()
489             .expect("`SourceMap::span_to_unmapped_path` called for imported `SourceFile`?")
490     }
491
492     pub fn is_multiline(&self, sp: Span) -> bool {
493         let lo = self.lookup_char_pos(sp.lo());
494         let hi = self.lookup_char_pos(sp.hi());
495         lo.line != hi.line
496     }
497
498     pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
499         debug!("span_to_lines(sp={:?})", sp);
500
501         if sp.lo() > sp.hi() {
502             return Err(SpanLinesError::IllFormedSpan(sp));
503         }
504
505         let lo = self.lookup_char_pos(sp.lo());
506         debug!("span_to_lines: lo={:?}", lo);
507         let hi = self.lookup_char_pos(sp.hi());
508         debug!("span_to_lines: hi={:?}", hi);
509
510         if lo.file.start_pos != hi.file.start_pos {
511             return Err(SpanLinesError::DistinctSources(DistinctSources {
512                 begin: (lo.file.name.clone(), lo.file.start_pos),
513                 end: (hi.file.name.clone(), hi.file.start_pos),
514             }));
515         }
516         assert!(hi.line >= lo.line);
517
518         let mut lines = Vec::with_capacity(hi.line - lo.line + 1);
519
520         // The span starts partway through the first line,
521         // but after that it starts from offset 0.
522         let mut start_col = lo.col;
523
524         // For every line but the last, it extends from `start_col`
525         // and to the end of the line. Be careful because the line
526         // numbers in Loc are 1-based, so we subtract 1 to get 0-based
527         // lines.
528         for line_index in lo.line-1 .. hi.line-1 {
529             let line_len = lo.file.get_line(line_index)
530                                   .map(|s| s.chars().count())
531                                   .unwrap_or(0);
532             lines.push(LineInfo { line_index,
533                                   start_col,
534                                   end_col: CharPos::from_usize(line_len) });
535             start_col = CharPos::from_usize(0);
536         }
537
538         // For the last line, it extends from `start_col` to `hi.col`:
539         lines.push(LineInfo { line_index: hi.line - 1,
540                               start_col,
541                               end_col: hi.col });
542
543         Ok(FileLines {file: lo.file, lines})
544     }
545
546     /// Extracts the source surrounding the given `Span` using the `extract_source` function. The
547     /// extract function takes three arguments: a string slice containing the source, an index in
548     /// the slice for the beginning of the span and an index in the slice for the end of the span.
549     fn span_to_source<F>(&self, sp: Span, extract_source: F) -> Result<String, SpanSnippetError>
550         where F: Fn(&str, usize, usize) -> Result<String, SpanSnippetError>
551     {
552         if sp.lo() > sp.hi() {
553             return Err(SpanSnippetError::IllFormedSpan(sp));
554         }
555
556         let local_begin = self.lookup_byte_offset(sp.lo());
557         let local_end = self.lookup_byte_offset(sp.hi());
558
559         if local_begin.sf.start_pos != local_end.sf.start_pos {
560             return Err(SpanSnippetError::DistinctSources(DistinctSources {
561                 begin: (local_begin.sf.name.clone(),
562                         local_begin.sf.start_pos),
563                 end: (local_end.sf.name.clone(),
564                       local_end.sf.start_pos)
565             }));
566         } else {
567             self.ensure_source_file_source_present(local_begin.sf.clone());
568
569             let start_index = local_begin.pos.to_usize();
570             let end_index = local_end.pos.to_usize();
571             let source_len = (local_begin.sf.end_pos -
572                               local_begin.sf.start_pos).to_usize();
573
574             if start_index > end_index || end_index > source_len {
575                 return Err(SpanSnippetError::MalformedForSourcemap(
576                     MalformedSourceMapPositions {
577                         name: local_begin.sf.name.clone(),
578                         source_len,
579                         begin_pos: local_begin.pos,
580                         end_pos: local_end.pos,
581                     }));
582             }
583
584             if let Some(ref src) = local_begin.sf.src {
585                 return extract_source(src, start_index, end_index);
586             } else if let Some(src) = local_begin.sf.external_src.borrow().get_source() {
587                 return extract_source(src, start_index, end_index);
588             } else {
589                 return Err(SpanSnippetError::SourceNotAvailable {
590                     filename: local_begin.sf.name.clone()
591                 });
592             }
593         }
594     }
595
596     /// Returns the source snippet as `String` corresponding to the given `Span`.
597     pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
598         self.span_to_source(sp, |src, start_index, end_index| src.get(start_index..end_index)
599             .map(|s| s.to_string())
600             .ok_or_else(|| SpanSnippetError::IllFormedSpan(sp)))
601     }
602
603     pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
604         match self.span_to_prev_source(sp) {
605             Err(_) => None,
606             Ok(source) => source.split('\n').last().map(|last_line| {
607                 last_line.len() - last_line.trim_start().len()
608             })
609         }
610     }
611
612     /// Returns the source snippet as `String` before the given `Span`.
613     pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
614         self.span_to_source(sp, |src, start_index, _| src.get(..start_index)
615             .map(|s| s.to_string())
616             .ok_or_else(|| SpanSnippetError::IllFormedSpan(sp)))
617     }
618
619     /// Extends the given `Span` to just after the previous occurrence of `c`. Return the same span
620     /// if no character could be found or if an error occurred while retrieving the code snippet.
621     pub fn span_extend_to_prev_char(&self, sp: Span, c: char) -> Span {
622         if let Ok(prev_source) = self.span_to_prev_source(sp) {
623             let prev_source = prev_source.rsplit(c).nth(0).unwrap_or("").trim_start();
624             if !prev_source.is_empty() && !prev_source.contains('\n') {
625                 return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
626             }
627         }
628
629         sp
630     }
631
632     /// Extends the given `Span` to just after the previous occurrence of `pat` when surrounded by
633     /// whitespace. Returns the same span if no character could be found or if an error occurred
634     /// while retrieving the code snippet.
635     pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str, accept_newlines: bool) -> Span {
636         // assure that the pattern is delimited, to avoid the following
637         //     fn my_fn()
638         //           ^^^^ returned span without the check
639         //     ---------- correct span
640         for ws in &[" ", "\t", "\n"] {
641             let pat = pat.to_owned() + ws;
642             if let Ok(prev_source) = self.span_to_prev_source(sp) {
643                 let prev_source = prev_source.rsplit(&pat).nth(0).unwrap_or("").trim_start();
644                 if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) {
645                     return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
646                 }
647             }
648         }
649
650         sp
651     }
652
653     /// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char`
654     /// `c`.
655     pub fn span_until_char(&self, sp: Span, c: char) -> Span {
656         match self.span_to_snippet(sp) {
657             Ok(snippet) => {
658                 let snippet = snippet.split(c).nth(0).unwrap_or("").trim_end();
659                 if !snippet.is_empty() && !snippet.contains('\n') {
660                     sp.with_hi(BytePos(sp.lo().0 + snippet.len() as u32))
661                 } else {
662                     sp
663                 }
664             }
665             _ => sp,
666         }
667     }
668
669     /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char`
670     /// `c`.
671     pub fn span_through_char(&self, sp: Span, c: char) -> Span {
672         if let Ok(snippet) = self.span_to_snippet(sp) {
673             if let Some(offset) = snippet.find(c) {
674                 return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32));
675             }
676         }
677         sp
678     }
679
680     /// Given a `Span`, gets a new `Span` covering the first token and all its trailing whitespace
681     /// or the original `Span`.
682     ///
683     /// If `sp` points to `"let mut x"`, then a span pointing at `"let "` will be returned.
684     pub fn span_until_non_whitespace(&self, sp: Span) -> Span {
685         let mut whitespace_found = false;
686
687         self.span_take_while(sp, |c| {
688             if !whitespace_found && c.is_whitespace() {
689                 whitespace_found = true;
690             }
691
692             if whitespace_found && !c.is_whitespace() {
693                 false
694             } else {
695                 true
696             }
697         })
698     }
699
700     /// Given a `Span`, gets a new `Span` covering the first token without its trailing whitespace
701     /// or the original `Span` in case of error.
702     ///
703     /// If `sp` points to `"let mut x"`, then a span pointing at `"let"` will be returned.
704     pub fn span_until_whitespace(&self, sp: Span) -> Span {
705         self.span_take_while(sp, |c| !c.is_whitespace())
706     }
707
708     /// Given a `Span`, gets a shorter one until `predicate` yields `false`.
709     pub fn span_take_while<P>(&self, sp: Span, predicate: P) -> Span
710         where P: for <'r> FnMut(&'r char) -> bool
711     {
712         if let Ok(snippet) = self.span_to_snippet(sp) {
713             let offset = snippet.chars()
714                 .take_while(predicate)
715                 .map(|c| c.len_utf8())
716                 .sum::<usize>();
717
718             sp.with_hi(BytePos(sp.lo().0 + (offset as u32)))
719         } else {
720             sp
721         }
722     }
723
724     pub fn def_span(&self, sp: Span) -> Span {
725         self.span_until_char(sp, '{')
726     }
727
728     /// Returns a new span representing just the start point of this span.
729     pub fn start_point(&self, sp: Span) -> Span {
730         let pos = sp.lo().0;
731         let width = self.find_width_of_character_at_span(sp, false);
732         let corrected_start_position = pos.checked_add(width).unwrap_or(pos);
733         let end_point = BytePos(cmp::max(corrected_start_position, sp.lo().0));
734         sp.with_hi(end_point)
735     }
736
737     /// Returns a new span representing just the end point of this span.
738     pub fn end_point(&self, sp: Span) -> Span {
739         let pos = sp.hi().0;
740
741         let width = self.find_width_of_character_at_span(sp, false);
742         let corrected_end_position = pos.checked_sub(width).unwrap_or(pos);
743
744         let end_point = BytePos(cmp::max(corrected_end_position, sp.lo().0));
745         sp.with_lo(end_point)
746     }
747
748     /// Returns a new span representing the next character after the end-point of this span.
749     pub fn next_point(&self, sp: Span) -> Span {
750         let start_of_next_point = sp.hi().0;
751
752         let width = self.find_width_of_character_at_span(sp, true);
753         // If the width is 1, then the next span should point to the same `lo` and `hi`. However,
754         // in the case of a multibyte character, where the width != 1, the next span should
755         // span multiple bytes to include the whole character.
756         let end_of_next_point = start_of_next_point.checked_add(
757             width - 1).unwrap_or(start_of_next_point);
758
759         let end_of_next_point = BytePos(cmp::max(sp.lo().0 + 1, end_of_next_point));
760         Span::new(BytePos(start_of_next_point), end_of_next_point, sp.ctxt())
761     }
762
763     /// Finds the width of a character, either before or after the provided span.
764     fn find_width_of_character_at_span(&self, sp: Span, forwards: bool) -> u32 {
765         // Disregard malformed spans and assume a one-byte wide character.
766         if sp.lo() >= sp.hi() {
767             debug!("find_width_of_character_at_span: early return malformed span");
768             return 1;
769         }
770
771         let local_begin = self.lookup_byte_offset(sp.lo());
772         let local_end = self.lookup_byte_offset(sp.hi());
773         debug!("find_width_of_character_at_span: local_begin=`{:?}`, local_end=`{:?}`",
774                local_begin, local_end);
775
776         if local_begin.sf.start_pos != local_end.sf.start_pos {
777             debug!("find_width_of_character_at_span: begin and end are in different files");
778             return 1;
779         }
780
781         let start_index = local_begin.pos.to_usize();
782         let end_index = local_end.pos.to_usize();
783         debug!("find_width_of_character_at_span: start_index=`{:?}`, end_index=`{:?}`",
784                start_index, end_index);
785
786         // Disregard indexes that are at the start or end of their spans, they can't fit bigger
787         // characters.
788         if (!forwards && end_index == usize::min_value()) ||
789             (forwards && start_index == usize::max_value()) {
790             debug!("find_width_of_character_at_span: start or end of span, cannot be multibyte");
791             return 1;
792         }
793
794         let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize();
795         debug!("find_width_of_character_at_span: source_len=`{:?}`", source_len);
796         // Ensure indexes are also not malformed.
797         if start_index > end_index || end_index > source_len {
798             debug!("find_width_of_character_at_span: source indexes are malformed");
799             return 1;
800         }
801
802         let src = local_begin.sf.external_src.borrow();
803
804         // We need to extend the snippet to the end of the src rather than to end_index so when
805         // searching forwards for boundaries we've got somewhere to search.
806         let snippet = if let Some(ref src) = local_begin.sf.src {
807             let len = src.len();
808             (&src[start_index..len])
809         } else if let Some(src) = src.get_source() {
810             let len = src.len();
811             (&src[start_index..len])
812         } else {
813             return 1;
814         };
815         debug!("find_width_of_character_at_span: snippet=`{:?}`", snippet);
816
817         let mut target = if forwards { end_index + 1 } else { end_index - 1 };
818         debug!("find_width_of_character_at_span: initial target=`{:?}`", target);
819
820         while !snippet.is_char_boundary(target - start_index) && target < source_len {
821             target = if forwards {
822                 target + 1
823             } else {
824                 match target.checked_sub(1) {
825                     Some(target) => target,
826                     None => {
827                         break;
828                     }
829                 }
830             };
831             debug!("find_width_of_character_at_span: target=`{:?}`", target);
832         }
833         debug!("find_width_of_character_at_span: final target=`{:?}`", target);
834
835         if forwards {
836             (target - end_index) as u32
837         } else {
838             (end_index - target) as u32
839         }
840     }
841
842     pub fn get_source_file(&self, filename: &FileName) -> Option<Lrc<SourceFile>> {
843         for sf in self.files.borrow().source_files.iter() {
844             if *filename == sf.name {
845                 return Some(sf.clone());
846             }
847         }
848         None
849     }
850
851     /// For a global `BytePos`, computes the local offset within the containing `SourceFile`.
852     pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos {
853         let idx = self.lookup_source_file_idx(bpos);
854         let sf = (*self.files.borrow().source_files)[idx].clone();
855         let offset = bpos - sf.start_pos;
856         SourceFileAndBytePos {sf, pos: offset}
857     }
858
859     /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`.
860     pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos {
861         let idx = self.lookup_source_file_idx(bpos);
862         let map = &(*self.files.borrow().source_files)[idx];
863
864         // The number of extra bytes due to multibyte chars in the `SourceFile`.
865         let mut total_extra_bytes = 0;
866
867         for mbc in map.multibyte_chars.iter() {
868             debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos);
869             if mbc.pos < bpos {
870                 // Every character is at least one byte, so we only
871                 // count the actual extra bytes.
872                 total_extra_bytes += mbc.bytes as u32 - 1;
873                 // We should never see a byte position in the middle of a
874                 // character.
875                 assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32);
876             } else {
877                 break;
878             }
879         }
880
881         assert!(map.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32());
882         CharPos(bpos.to_usize() - map.start_pos.to_usize() - total_extra_bytes as usize)
883     }
884
885     // Returns the index of the `SourceFile` (in `self.files`) that contains `pos`.
886     pub fn lookup_source_file_idx(&self, pos: BytePos) -> usize {
887         self.files.borrow().source_files.binary_search_by_key(&pos, |key| key.start_pos)
888             .unwrap_or_else(|p| p - 1)
889     }
890
891     pub fn count_lines(&self) -> usize {
892         self.files().iter().fold(0, |a, f| a + f.count_lines())
893     }
894
895
896     pub fn generate_fn_name_span(&self, span: Span) -> Option<Span> {
897         let prev_span = self.span_extend_to_prev_str(span, "fn", true);
898         self.span_to_snippet(prev_span).map(|snippet| {
899             let len = snippet.find(|c: char| !c.is_alphanumeric() && c != '_')
900                 .expect("no label after fn");
901             prev_span.with_hi(BytePos(prev_span.lo().0 + len as u32))
902         }).ok()
903     }
904
905     /// Takes the span of a type parameter in a function signature and try to generate a span for
906     /// the function name (with generics) and a new snippet for this span with the pointed type
907     /// parameter as a new local type parameter.
908     ///
909     /// For instance:
910     /// ```rust,ignore (pseudo-Rust)
911     /// // Given span
912     /// fn my_function(param: T)
913     /// //                    ^ Original span
914     ///
915     /// // Result
916     /// fn my_function(param: T)
917     /// // ^^^^^^^^^^^ Generated span with snippet `my_function<T>`
918     /// ```
919     ///
920     /// Attention: The method used is very fragile since it essentially duplicates the work of the
921     /// parser. If you need to use this function or something similar, please consider updating the
922     /// `SourceMap` functions and this function to something more robust.
923     pub fn generate_local_type_param_snippet(&self, span: Span) -> Option<(Span, String)> {
924         // Try to extend the span to the previous "fn" keyword to retrieve the function
925         // signature.
926         let sugg_span = self.span_extend_to_prev_str(span, "fn", false);
927         if sugg_span != span {
928             if let Ok(snippet) = self.span_to_snippet(sugg_span) {
929                 // Consume the function name.
930                 let mut offset = snippet.find(|c: char| !c.is_alphanumeric() && c != '_')
931                     .expect("no label after fn");
932
933                 // Consume the generics part of the function signature.
934                 let mut bracket_counter = 0;
935                 let mut last_char = None;
936                 for c in snippet[offset..].chars() {
937                     match c {
938                         '<' => bracket_counter += 1,
939                         '>' => bracket_counter -= 1,
940                         '(' => if bracket_counter == 0 { break; }
941                         _ => {}
942                     }
943                     offset += c.len_utf8();
944                     last_char = Some(c);
945                 }
946
947                 // Adjust the suggestion span to encompass the function name with its generics.
948                 let sugg_span = sugg_span.with_hi(BytePos(sugg_span.lo().0 + offset as u32));
949
950                 // Prepare the new suggested snippet to append the type parameter that triggered
951                 // the error in the generics of the function signature.
952                 let mut new_snippet = if last_char == Some('>') {
953                     format!("{}, ", &snippet[..(offset - '>'.len_utf8())])
954                 } else {
955                     format!("{}<", &snippet[..offset])
956                 };
957                 new_snippet.push_str(
958                     &self.span_to_snippet(span).unwrap_or_else(|_| "T".to_string()));
959                 new_snippet.push('>');
960
961                 return Some((sugg_span, new_snippet));
962             }
963         }
964
965         None
966     }
967 }
968
969 impl SourceMapper for SourceMap {
970     fn lookup_char_pos(&self, pos: BytePos) -> Loc {
971         self.lookup_char_pos(pos)
972     }
973     fn span_to_lines(&self, sp: Span) -> FileLinesResult {
974         self.span_to_lines(sp)
975     }
976     fn span_to_string(&self, sp: Span) -> String {
977         self.span_to_string(sp)
978     }
979     fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
980         self.span_to_snippet(sp)
981     }
982     fn span_to_filename(&self, sp: Span) -> FileName {
983         self.span_to_filename(sp)
984     }
985     fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
986         self.merge_spans(sp_lhs, sp_rhs)
987     }
988     fn call_span_if_macro(&self, sp: Span) -> Span {
989         if self.span_to_filename(sp.clone()).is_macros() {
990             let v = sp.macro_backtrace();
991             if let Some(use_site) = v.last() {
992                 return use_site.call_site;
993             }
994         }
995         sp
996     }
997     fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
998         source_file.add_external_src(
999             || match source_file.name {
1000                 FileName::Real(ref name) => self.file_loader.read_file(name).ok(),
1001                 _ => None,
1002             }
1003         )
1004     }
1005     fn doctest_offset_line(&self, file: &FileName, line: usize) -> usize {
1006         self.doctest_offset_line(file, line)
1007     }
1008 }
1009
1010 #[derive(Clone)]
1011 pub struct FilePathMapping {
1012     mapping: Vec<(PathBuf, PathBuf)>,
1013 }
1014
1015 impl FilePathMapping {
1016     pub fn empty() -> FilePathMapping {
1017         FilePathMapping {
1018             mapping: vec![]
1019         }
1020     }
1021
1022     pub fn new(mapping: Vec<(PathBuf, PathBuf)>) -> FilePathMapping {
1023         FilePathMapping {
1024             mapping,
1025         }
1026     }
1027
1028     /// Applies any path prefix substitution as defined by the mapping.
1029     /// The return value is the remapped path and a boolean indicating whether
1030     /// the path was affected by the mapping.
1031     pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) {
1032         // NOTE: We are iterating over the mapping entries from last to first
1033         //       because entries specified later on the command line should
1034         //       take precedence.
1035         for &(ref from, ref to) in self.mapping.iter().rev() {
1036             if let Ok(rest) = path.strip_prefix(from) {
1037                 return (to.join(rest), true);
1038             }
1039         }
1040
1041         (path, false)
1042     }
1043 }