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.
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.
11 //! The CodeMap 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 //! CodeMap and are represented by FileMaps. Byte positions are stored in
16 //! `spans` and used pervasively in the compiler. They are absolute positions
17 //! within the CodeMap, which upon request can be converted to line and column
18 //! information, source code snippets, etc.
20 pub use self::ExpnFormat
::*;
22 use std
::cell
::{Cell, RefCell}
;
23 use std
::ops
::{Add, Sub}
;
29 use std
::io
::{self, Read}
;
31 use serialize
::{Encodable, Decodable, Encoder, Decoder}
;
35 use errors
::emitter
::MAX_HIGHLIGHT_LINES
;
37 // _____________________________________________________________________________
38 // Pos, BytePos, CharPos
42 fn from_usize(n
: usize) -> Self;
43 fn to_usize(&self) -> usize;
46 /// A byte offset. Keep this small (currently 32-bits), as AST contains
48 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
49 pub struct BytePos(pub u32);
51 /// A character offset. Because of multibyte utf8 characters, a byte offset
52 /// is not equivalent to a character offset. The CodeMap will convert BytePos
53 /// values to CharPos values as necessary.
54 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Debug)]
55 pub struct CharPos(pub usize);
57 // FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
58 // have been unsuccessful
60 impl Pos
for BytePos
{
61 fn from_usize(n
: usize) -> BytePos { BytePos(n as u32) }
62 fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize }
65 impl Add
for BytePos
{
66 type Output
= BytePos
;
68 fn add(self, rhs
: BytePos
) -> BytePos
{
69 BytePos((self.to_usize() + rhs
.to_usize()) as u32)
73 impl Sub
for BytePos
{
74 type Output
= BytePos
;
76 fn sub(self, rhs
: BytePos
) -> BytePos
{
77 BytePos((self.to_usize() - rhs
.to_usize()) as u32)
81 impl Encodable
for BytePos
{
82 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
87 impl Decodable
for BytePos
{
88 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<BytePos
, D
::Error
> {
89 Ok(BytePos(d
.read_u32()?
))
93 impl Pos
for CharPos
{
94 fn from_usize(n
: usize) -> CharPos { CharPos(n) }
95 fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
98 impl Add
for CharPos
{
99 type Output
= CharPos
;
101 fn add(self, rhs
: CharPos
) -> CharPos
{
102 CharPos(self.to_usize() + rhs
.to_usize())
106 impl Sub
for CharPos
{
107 type Output
= CharPos
;
109 fn sub(self, rhs
: CharPos
) -> CharPos
{
110 CharPos(self.to_usize() - rhs
.to_usize())
114 // _____________________________________________________________________________
115 // Span, MultiSpan, Spanned
118 /// Spans represent a region of code, used for error reporting. Positions in spans
119 /// are *absolute* positions from the beginning of the codemap, not positions
120 /// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
121 /// to the original source.
122 /// You must be careful if the span crosses more than one file - you will not be
123 /// able to use many of the functions on spans in codemap and you cannot assume
124 /// that the length of the span = hi - lo; there may be space in the BytePos
125 /// range between files.
126 #[derive(Clone, Copy, Hash, PartialEq, Eq)]
130 /// Information about where the macro came from, if this piece of
131 /// code was created by a macro expansion.
135 /// Spans are converted to MultiSpans just before error reporting, either automatically,
136 /// generated by line grouping, or manually constructed.
137 /// In the latter case care should be taken to ensure that spans are ordered, disjoint,
138 /// and point into the same FileMap.
140 pub struct MultiSpan
{
144 pub const DUMMY_SP
: Span
= Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION }
;
146 // Generic span to be used for code originating from the command line
147 pub const COMMAND_LINE_SP
: Span
= Span
{ lo
: BytePos(0),
149 expn_id
: COMMAND_LINE_EXPN
};
152 /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
153 pub fn substitute_dummy(self, other
: Span
) -> Span
{
154 if self.source_equal(&DUMMY_SP
) { other }
else { self }
157 pub fn contains(self, other
: Span
) -> bool
{
158 self.lo
<= other
.lo
&& other
.hi
<= self.hi
161 /// Return true if the spans are equal with regards to the source text.
163 /// Use this instead of `==` when either span could be generated code,
164 /// and you only care that they point to the same bytes of source text.
165 pub fn source_equal(&self, other
: &Span
) -> bool
{
166 self.lo
== other
.lo
&& self.hi
== other
.hi
169 /// Returns `Some(span)`, a union of `self` and `other`, on overlap.
170 pub fn merge(self, other
: Span
) -> Option
<Span
> {
171 if self.expn_id
!= other
.expn_id
{
175 if (self.lo
<= other
.lo
&& self.hi
> other
.lo
) ||
176 (self.lo
>= other
.lo
&& self.lo
< other
.hi
) {
178 lo
: cmp
::min(self.lo
, other
.lo
),
179 hi
: cmp
::max(self.hi
, other
.hi
),
180 expn_id
: self.expn_id
,
187 /// Returns `Some(span)`, where the start is trimmed by the end of `other`
188 pub fn trim_start(self, other
: Span
) -> Option
<Span
> {
189 if self.hi
> other
.hi
{
190 Some(Span { lo: cmp::max(self.lo, other.hi), .. self }
)
197 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
198 pub struct Spanned
<T
> {
203 impl Encodable
for Span
{
204 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
205 s
.emit_struct("Span", 2, |s
| {
206 s
.emit_struct_field("lo", 0, |s
| {
210 s
.emit_struct_field("hi", 1, |s
| {
217 impl Decodable
for Span
{
218 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<Span
, D
::Error
> {
219 d
.read_struct("Span", 2, |d
| {
220 let lo
= d
.read_struct_field("lo", 0, |d
| {
224 let hi
= d
.read_struct_field("hi", 1, |d
| {
233 fn default_span_debug(span
: Span
, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
234 write
!(f
, "Span {{ lo: {:?}, hi: {:?}, expn_id: {:?} }}",
235 span
.lo
, span
.hi
, span
.expn_id
)
238 thread_local
!(pub static SPAN_DEBUG
: Cell
<fn(Span
, &mut fmt
::Formatter
) -> fmt
::Result
> =
239 Cell
::new(default_span_debug
));
241 impl fmt
::Debug
for Span
{
242 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
243 SPAN_DEBUG
.with(|span_debug
| span_debug
.get()(*self, f
))
247 pub fn spanned
<T
>(lo
: BytePos
, hi
: BytePos
, t
: T
) -> Spanned
<T
> {
248 respan(mk_sp(lo
, hi
), t
)
251 pub fn respan
<T
>(sp
: Span
, t
: T
) -> Spanned
<T
> {
252 Spanned {node: t, span: sp}
255 pub fn dummy_spanned
<T
>(t
: T
) -> Spanned
<T
> {
259 /* assuming that we're not in macro expansion */
260 pub fn mk_sp(lo
: BytePos
, hi
: BytePos
) -> Span
{
261 Span {lo: lo, hi: hi, expn_id: NO_EXPANSION}
264 /// Return the span itself if it doesn't come from a macro expansion,
265 /// otherwise return the call site span up to the `enclosing_sp` by
266 /// following the `expn_info` chain.
267 pub fn original_sp(cm
: &CodeMap
, sp
: Span
, enclosing_sp
: Span
) -> Span
{
268 let call_site1
= cm
.with_expn_info(sp
.expn_id
, |ei
| ei
.map(|ei
| ei
.call_site
));
269 let call_site2
= cm
.with_expn_info(enclosing_sp
.expn_id
, |ei
| ei
.map(|ei
| ei
.call_site
));
270 match (call_site1
, call_site2
) {
272 (Some(call_site1
), Some(call_site2
)) if call_site1
== call_site2
=> sp
,
273 (Some(call_site1
), _
) => original_sp(cm
, call_site1
, enclosing_sp
),
278 pub fn new() -> MultiSpan
{
279 MultiSpan { spans: Vec::new() }
282 pub fn to_span_bounds(&self) -> Span
{
283 assert
!(!self.spans
.is_empty());
284 let Span { lo, expn_id, .. }
= *self.spans
.first().unwrap();
285 let Span { hi, .. }
= *self.spans
.last().unwrap();
286 Span { lo: lo, hi: hi, expn_id: expn_id }
289 /// Merges or inserts the given span into itself.
290 pub fn push_merge(&mut self, mut sp
: Span
) {
291 let mut idx_merged
= None
;
294 let cur
= match self.spans
.get(idx
) {
298 // Try to merge with a contained Span
299 if let Some(union) = cur
.merge(sp
) {
300 self.spans
[idx
] = union;
302 idx_merged
= Some(idx
);
305 // Or insert into the first sorted position
307 self.spans
.insert(idx
, sp
);
308 idx_merged
= Some(idx
);
312 if let Some(idx
) = idx_merged
{
313 // Merge with spans trailing the insertion/merging position
314 while (idx
+ 1) < self.spans
.len() {
315 if let Some(union) = self.spans
[idx
+ 1].merge(sp
) {
316 self.spans
[idx
] = union;
317 self.spans
.remove(idx
+ 1);
327 /// Inserts the given span into itself, for use with `end_highlight_lines`.
328 pub fn push_trim(&mut self, mut sp
: Span
) {
329 let mut prev
= mk_sp(BytePos(0), BytePos(0));
331 if let Some(first
) = self.spans
.get_mut(0) {
332 if first
.lo
> sp
.lo
{
333 // Prevent us here from spanning fewer lines
334 // because of trimming the start of the span
335 // (this should not be visible, because this method ought
336 // to not be used in conjunction with `highlight_lines`)
342 if let Some(sp_trim
) = sp
.trim_start(prev
) {
343 // Implies `sp.hi > prev.hi`
344 let cur
= match self.spans
.get(idx
) {
351 // `cur` may overlap with `sp_trim`
352 if let Some(cur_trim
) = cur
.trim_start(sp_trim
) {
353 // Implies `sp.hi < cur.hi`
354 self.spans
.insert(idx
, sp_trim
);
355 self.spans
[idx
+ 1] = cur_trim
;
357 } else if sp
.hi
== cur
.hi
{
367 impl From
<Span
> for MultiSpan
{
368 fn from(span
: Span
) -> MultiSpan
{
369 MultiSpan { spans: vec![span] }
373 // _____________________________________________________________________________
374 // Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
377 /// A source code location used for error reporting
380 /// Information about the original source
381 pub file
: Rc
<FileMap
>,
382 /// The (1-based) line number
384 /// The (0-based) column offset
388 /// A source code location used as the result of lookup_char_pos_adj
389 // Actually, *none* of the clients use the filename *or* file field;
390 // perhaps they should just be removed.
392 pub struct LocWithOpt
{
393 pub filename
: FileName
,
396 pub file
: Option
<Rc
<FileMap
>>,
399 // used to be structural records. Better names, anyone?
401 pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
403 pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
406 // _____________________________________________________________________________
407 // ExpnFormat, NameAndSpan, ExpnInfo, ExpnId
410 /// The source of expansion.
411 #[derive(Clone, Hash, Debug, PartialEq, Eq)]
412 pub enum ExpnFormat
{
413 /// e.g. #[derive(...)] <item>
414 MacroAttribute(Name
),
419 #[derive(Clone, Hash, Debug)]
420 pub struct NameAndSpan
{
421 /// The format with which the macro was invoked.
422 pub format
: ExpnFormat
,
423 /// Whether the macro is allowed to use #[unstable]/feature-gated
424 /// features internally without forcing the whole crate to opt-in
426 pub allow_internal_unstable
: bool
,
427 /// The span of the macro definition itself. The macro may not
428 /// have a sensible definition span (e.g. something defined
429 /// completely inside libsyntax) in which case this is None.
430 pub span
: Option
<Span
>
434 pub fn name(&self) -> Name
{
436 ExpnFormat
::MacroAttribute(s
) => s
,
437 ExpnFormat
::MacroBang(s
) => s
,
442 /// Extra information for tracking spans of macro and syntax sugar expansion
443 #[derive(Hash, Debug)]
444 pub struct ExpnInfo
{
445 /// The location of the actual macro invocation or syntax sugar , e.g.
446 /// `let x = foo!();` or `if let Some(y) = x {}`
448 /// This may recursively refer to other macro invocations, e.g. if
449 /// `foo!()` invoked `bar!()` internally, and there was an
450 /// expression inside `bar!`; the call_site of the expression in
451 /// the expansion would point to the `bar!` invocation; that
452 /// call_site span would have its own ExpnInfo, with the call_site
453 /// pointing to the `foo!` invocation.
455 /// Information about the expansion.
456 pub callee
: NameAndSpan
459 #[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)]
460 pub struct ExpnId(u32);
462 pub const NO_EXPANSION
: ExpnId
= ExpnId(!0);
463 // For code appearing from the command line
464 pub const COMMAND_LINE_EXPN
: ExpnId
= ExpnId(!1);
467 pub fn from_u32(id
: u32) -> ExpnId
{
471 pub fn into_u32(self) -> u32 {
476 // _____________________________________________________________________________
477 // FileMap, MultiByteChar, FileName, FileLines
480 pub type FileName
= String
;
482 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
483 pub struct LineInfo
{
484 /// Index of line, starting from 0.
485 pub line_index
: usize,
487 /// Column in line where span begins, starting from 0.
488 pub start_col
: CharPos
,
490 /// Column in line where span ends, starting from 0, exclusive.
491 pub end_col
: CharPos
,
494 pub struct FileLines
{
495 pub file
: Rc
<FileMap
>,
496 pub lines
: Vec
<LineInfo
>
499 /// Identifies an offset of a multi-byte character in a FileMap
500 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
501 pub struct MultiByteChar
{
502 /// The absolute offset of the character in the CodeMap
504 /// The number of bytes, >=2
508 /// A single source in the CodeMap.
510 /// The name of the file that the source came from, source that doesn't
511 /// originate from files has names between angle brackets by convention,
514 /// The complete source code
515 pub src
: Option
<Rc
<String
>>,
516 /// The start position of this source in the CodeMap
517 pub start_pos
: BytePos
,
518 /// The end position of this source in the CodeMap
519 pub end_pos
: BytePos
,
520 /// Locations of lines beginnings in the source code
521 pub lines
: RefCell
<Vec
<BytePos
>>,
522 /// Locations of multi-byte characters in the source code
523 pub multibyte_chars
: RefCell
<Vec
<MultiByteChar
>>,
526 impl Encodable
for FileMap
{
527 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
528 s
.emit_struct("FileMap", 5, |s
| {
529 s
.emit_struct_field("name", 0, |s
| self.name
.encode(s
))?
;
530 s
.emit_struct_field("start_pos", 1, |s
| self.start_pos
.encode(s
))?
;
531 s
.emit_struct_field("end_pos", 2, |s
| self.end_pos
.encode(s
))?
;
532 s
.emit_struct_field("lines", 3, |s
| {
533 let lines
= self.lines
.borrow();
535 s
.emit_u32(lines
.len() as u32)?
;
537 if !lines
.is_empty() {
538 // In order to preserve some space, we exploit the fact that
539 // the lines list is sorted and individual lines are
540 // probably not that long. Because of that we can store lines
541 // as a difference list, using as little space as possible
542 // for the differences.
543 let max_line_length
= if lines
.len() == 1 {
547 .map(|w
| w
[1] - w
[0])
548 .map(|bp
| bp
.to_usize())
553 let bytes_per_diff
: u8 = match max_line_length
{
555 0x100 ... 0xFFFF => 2,
559 // Encode the number of bytes used per diff.
560 bytes_per_diff
.encode(s
)?
;
562 // Encode the first element.
565 let diff_iter
= (&lines
[..]).windows(2)
566 .map(|w
| (w
[1] - w
[0]));
568 match bytes_per_diff
{
569 1 => for diff
in diff_iter { (diff.0 as u8).encode(s)? }
,
570 2 => for diff
in diff_iter { (diff.0 as u16).encode(s)? }
,
571 4 => for diff
in diff_iter { diff.0.encode(s)? }
,
578 s
.emit_struct_field("multibyte_chars", 4, |s
| {
579 (*self.multibyte_chars
.borrow()).encode(s
)
585 impl Decodable
for FileMap
{
586 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<FileMap
, D
::Error
> {
588 d
.read_struct("FileMap", 5, |d
| {
589 let name
: String
= d
.read_struct_field("name", 0, |d
| Decodable
::decode(d
))?
;
590 let start_pos
: BytePos
= d
.read_struct_field("start_pos", 1, |d
| Decodable
::decode(d
))?
;
591 let end_pos
: BytePos
= d
.read_struct_field("end_pos", 2, |d
| Decodable
::decode(d
))?
;
592 let lines
: Vec
<BytePos
> = d
.read_struct_field("lines", 3, |d
| {
593 let num_lines
: u32 = Decodable
::decode(d
)?
;
594 let mut lines
= Vec
::with_capacity(num_lines
as usize);
597 // Read the number of bytes used per diff.
598 let bytes_per_diff
: u8 = Decodable
::decode(d
)?
;
600 // Read the first element.
601 let mut line_start
: BytePos
= Decodable
::decode(d
)?
;
602 lines
.push(line_start
);
604 for _
in 1..num_lines
{
605 let diff
= match bytes_per_diff
{
606 1 => d
.read_u8()?
as u32,
607 2 => d
.read_u16()?
as u32,
612 line_start
= line_start
+ BytePos(diff
);
614 lines
.push(line_start
);
620 let multibyte_chars
: Vec
<MultiByteChar
> =
621 d
.read_struct_field("multibyte_chars", 4, |d
| Decodable
::decode(d
))?
;
624 start_pos
: start_pos
,
627 lines
: RefCell
::new(lines
),
628 multibyte_chars
: RefCell
::new(multibyte_chars
)
634 impl fmt
::Debug
for FileMap
{
635 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
636 write
!(fmt
, "FileMap({})", self.name
)
641 /// EFFECT: register a start-of-line offset in the
642 /// table of line-beginnings.
643 /// UNCHECKED INVARIANT: these offsets must be added in the right
644 /// order and must be in the right places; there is shared knowledge
645 /// about what ends a line between this file and parse.rs
646 /// WARNING: pos param here is the offset relative to start of CodeMap,
647 /// and CodeMap will append a newline when adding a filemap without a newline at the end,
648 /// so the safe way to call this is with value calculated as
649 /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap.
650 pub fn next_line(&self, pos
: BytePos
) {
651 // the new charpos must be > the last one (or it's the first one).
652 let mut lines
= self.lines
.borrow_mut();
653 let line_len
= lines
.len();
654 assert
!(line_len
== 0 || ((*lines
)[line_len
- 1] < pos
));
658 /// get a line from the list of pre-computed line-beginnings.
659 /// line-number here is 0-based.
660 pub fn get_line(&self, line_number
: usize) -> Option
<&str> {
663 let lines
= self.lines
.borrow();
664 lines
.get(line_number
).map(|&line
| {
665 let begin
: BytePos
= line
- self.start_pos
;
666 let begin
= begin
.to_usize();
667 // We can't use `lines.get(line_number+1)` because we might
668 // be parsing when we call this function and thus the current
669 // line is the last one we have line info for.
670 let slice
= &src
[begin
..];
671 match slice
.find('
\n'
) {
672 Some(e
) => &slice
[..e
],
681 pub fn record_multibyte_char(&self, pos
: BytePos
, bytes
: usize) {
682 assert
!(bytes
>=2 && bytes
<= 4);
683 let mbc
= MultiByteChar
{
687 self.multibyte_chars
.borrow_mut().push(mbc
);
690 pub fn is_real_file(&self) -> bool
{
691 !(self.name
.starts_with("<") &&
692 self.name
.ends_with(">"))
695 pub fn is_imported(&self) -> bool
{
699 fn count_lines(&self) -> usize {
700 self.lines
.borrow().len()
704 /// An abstraction over the fs operations used by the Parser.
705 pub trait FileLoader
{
706 /// Query the existence of a file.
707 fn file_exists(&self, path
: &Path
) -> bool
;
709 /// Read the contents of an UTF-8 file into memory.
710 fn read_file(&self, path
: &Path
) -> io
::Result
<String
>;
713 /// A FileLoader that uses std::fs to load real files.
714 pub struct RealFileLoader
;
716 impl FileLoader
for RealFileLoader
{
717 fn file_exists(&self, path
: &Path
) -> bool
{
718 fs
::metadata(path
).is_ok()
721 fn read_file(&self, path
: &Path
) -> io
::Result
<String
> {
722 let mut src
= String
::new();
723 fs
::File
::open(path
)?
.read_to_string(&mut src
)?
;
728 // _____________________________________________________________________________
733 pub files
: RefCell
<Vec
<Rc
<FileMap
>>>,
734 expansions
: RefCell
<Vec
<ExpnInfo
>>,
735 file_loader
: Box
<FileLoader
>
739 pub fn new() -> CodeMap
{
741 files
: RefCell
::new(Vec
::new()),
742 expansions
: RefCell
::new(Vec
::new()),
743 file_loader
: Box
::new(RealFileLoader
)
747 pub fn with_file_loader(file_loader
: Box
<FileLoader
>) -> CodeMap
{
749 files
: RefCell
::new(Vec
::new()),
750 expansions
: RefCell
::new(Vec
::new()),
751 file_loader
: file_loader
755 pub fn file_exists(&self, path
: &Path
) -> bool
{
756 self.file_loader
.file_exists(path
)
759 pub fn load_file(&self, path
: &Path
) -> io
::Result
<Rc
<FileMap
>> {
760 let src
= self.file_loader
.read_file(path
)?
;
761 Ok(self.new_filemap(path
.to_str().unwrap().to_string(), src
))
764 fn next_start_pos(&self) -> usize {
765 let files
= self.files
.borrow();
768 // Add one so there is some space between files. This lets us distinguish
769 // positions in the codemap, even in the presence of zero-length files.
770 Some(last
) => last
.end_pos
.to_usize() + 1,
774 /// Creates a new filemap without setting its line information. If you don't
775 /// intend to set the line information yourself, you should use new_filemap_and_lines.
776 pub fn new_filemap(&self, filename
: FileName
, mut src
: String
) -> Rc
<FileMap
> {
777 let start_pos
= self.next_start_pos();
778 let mut files
= self.files
.borrow_mut();
780 // Remove utf-8 BOM if any.
781 if src
.starts_with("\u{feff}") {
785 let end_pos
= start_pos
+ src
.len();
787 let filemap
= Rc
::new(FileMap
{
789 src
: Some(Rc
::new(src
)),
790 start_pos
: Pos
::from_usize(start_pos
),
791 end_pos
: Pos
::from_usize(end_pos
),
792 lines
: RefCell
::new(Vec
::new()),
793 multibyte_chars
: RefCell
::new(Vec
::new()),
796 files
.push(filemap
.clone());
801 /// Creates a new filemap and sets its line information.
802 pub fn new_filemap_and_lines(&self, filename
: &str, src
: &str) -> Rc
<FileMap
> {
803 let fm
= self.new_filemap(filename
.to_string(), src
.to_owned());
804 let mut byte_pos
: u32 = 0;
805 for line
in src
.lines() {
806 // register the start of this line
807 fm
.next_line(BytePos(byte_pos
));
809 // update byte_pos to include this line and the \n at the end
810 byte_pos
+= line
.len() as u32 + 1;
816 /// Allocates a new FileMap representing a source file from an external
817 /// crate. The source code of such an "imported filemap" is not available,
818 /// but we still know enough to generate accurate debuginfo location
819 /// information for things inlined from other crates.
820 pub fn new_imported_filemap(&self,
823 mut file_local_lines
: Vec
<BytePos
>,
824 mut file_local_multibyte_chars
: Vec
<MultiByteChar
>)
826 let start_pos
= self.next_start_pos();
827 let mut files
= self.files
.borrow_mut();
829 let end_pos
= Pos
::from_usize(start_pos
+ source_len
);
830 let start_pos
= Pos
::from_usize(start_pos
);
832 for pos
in &mut file_local_lines
{
833 *pos
= *pos
+ start_pos
;
836 for mbc
in &mut file_local_multibyte_chars
{
837 mbc
.pos
= mbc
.pos
+ start_pos
;
840 let filemap
= Rc
::new(FileMap
{
843 start_pos
: start_pos
,
845 lines
: RefCell
::new(file_local_lines
),
846 multibyte_chars
: RefCell
::new(file_local_multibyte_chars
),
849 files
.push(filemap
.clone());
854 pub fn mk_substr_filename(&self, sp
: Span
) -> String
{
855 let pos
= self.lookup_char_pos(sp
.lo
);
856 (format
!("<{}:{}:{}>",
859 pos
.col
.to_usize() + 1)).to_string()
862 /// Lookup source information about a BytePos
863 pub fn lookup_char_pos(&self, pos
: BytePos
) -> Loc
{
864 let chpos
= self.bytepos_to_file_charpos(pos
);
865 match self.lookup_line(pos
) {
866 Ok(FileMapAndLine { fm: f, line: a }
) => {
867 let line
= a
+ 1; // Line numbers start at 1
868 let linebpos
= (*f
.lines
.borrow())[a
];
869 let linechpos
= self.bytepos_to_file_charpos(linebpos
);
870 debug
!("byte pos {:?} is on the line at byte pos {:?}",
872 debug
!("char pos {:?} is on the line at char pos {:?}",
874 debug
!("byte is on line: {}", line
);
875 assert
!(chpos
>= linechpos
);
879 col
: chpos
- linechpos
,
892 // If the relevant filemap is empty, we don't return a line number.
893 fn lookup_line(&self, pos
: BytePos
) -> Result
<FileMapAndLine
, Rc
<FileMap
>> {
894 let idx
= self.lookup_filemap_idx(pos
);
896 let files
= self.files
.borrow();
897 let f
= (*files
)[idx
].clone();
899 let len
= f
.lines
.borrow().len();
906 let lines
= f
.lines
.borrow();
907 let mut b
= lines
.len();
910 if (*lines
)[m
] > pos
{
916 assert
!(a
<= lines
.len());
918 Ok(FileMapAndLine { fm: f, line: a }
)
921 pub fn lookup_char_pos_adj(&self, pos
: BytePos
) -> LocWithOpt
{
922 let loc
= self.lookup_char_pos(pos
);
924 filename
: loc
.file
.name
.to_string(),
931 pub fn span_to_string(&self, sp
: Span
) -> String
{
932 if self.files
.borrow().is_empty() && sp
.source_equal(&DUMMY_SP
) {
933 return "no-location".to_string();
936 let lo
= self.lookup_char_pos_adj(sp
.lo
);
937 let hi
= self.lookup_char_pos_adj(sp
.hi
);
938 return (format
!("{}:{}:{}: {}:{}",
941 lo
.col
.to_usize() + 1,
943 hi
.col
.to_usize() + 1)).to_string()
946 // Returns true if two spans have the same callee
947 // (Assumes the same ExpnFormat implies same callee)
948 fn match_callees(&self, sp_a
: &Span
, sp_b
: &Span
) -> bool
{
950 .with_expn_info(sp_a
.expn_id
,
951 |ei
| ei
.map(|ei
| ei
.callee
.format
.clone()));
954 .with_expn_info(sp_b
.expn_id
,
955 |ei
| ei
.map(|ei
| ei
.callee
.format
.clone()));
959 /// Returns a formatted string showing the expansion chain of a span
961 /// Spans are printed in the following format:
963 /// filename:start_line:col: end_line:col
970 /// Callees and callsites are printed recursively (if available, otherwise header
971 /// and span is omitted), expanding into their own callee/callsite spans.
972 /// Each layer of recursion has an increased indent, and snippets are truncated
973 /// to at most 50 characters. Finally, recursive calls to the same macro are squashed,
974 /// with '...' used to represent any number of recursive calls.
975 pub fn span_to_expanded_string(&self, sp
: Span
) -> String
{
976 self.span_to_expanded_string_internal(sp
, "")
979 fn span_to_expanded_string_internal(&self, sp
:Span
, indent
: &str) -> String
{
980 let mut indent
= indent
.to_owned();
981 let mut output
= "".to_owned();
982 let span_str
= self.span_to_string(sp
);
983 let mut span_snip
= self.span_to_snippet(sp
)
984 .unwrap_or("Snippet unavailable".to_owned());
986 // Truncate by code points - in worst case this will be more than 50 characters,
987 // but ensures at least 50 characters and respects byte boundaries.
988 let char_vec
: Vec
<(usize, char)> = span_snip
.char_indices().collect();
989 if char_vec
.len() > 50 {
990 span_snip
.truncate(char_vec
[49].0);
991 span_snip
.push_str("...");
994 output
.push_str(&format
!("{}{}\n{}`{}`\n", indent
, span_str
, indent
, span_snip
));
996 if sp
.expn_id
== NO_EXPANSION
|| sp
.expn_id
== COMMAND_LINE_EXPN
{
1000 let mut callee
= self.with_expn_info(sp
.expn_id
,
1001 |ei
| ei
.and_then(|ei
| ei
.callee
.span
.clone()));
1002 let mut callsite
= self.with_expn_info(sp
.expn_id
,
1003 |ei
| ei
.map(|ei
| ei
.call_site
.clone()));
1005 indent
.push_str(" ");
1006 let mut is_recursive
= false;
1008 while callee
.is_some() && self.match_callees(&sp
, &callee
.unwrap()) {
1009 callee
= self.with_expn_info(callee
.unwrap().expn_id
,
1010 |ei
| ei
.and_then(|ei
| ei
.callee
.span
.clone()));
1011 is_recursive
= true;
1013 if let Some(span
) = callee
{
1014 output
.push_str(&indent
);
1015 output
.push_str("Callee:\n");
1017 output
.push_str(&indent
);
1018 output
.push_str("...\n");
1020 output
.push_str(&(self.span_to_expanded_string_internal(span
, &indent
)));
1023 is_recursive
= false;
1024 while callsite
.is_some() && self.match_callees(&sp
, &callsite
.unwrap()) {
1025 callsite
= self.with_expn_info(callsite
.unwrap().expn_id
,
1026 |ei
| ei
.map(|ei
| ei
.call_site
.clone()));
1027 is_recursive
= true;
1029 if let Some(span
) = callsite
{
1030 output
.push_str(&indent
);
1031 output
.push_str("Callsite:\n");
1033 output
.push_str(&indent
);
1034 output
.push_str("...\n");
1036 output
.push_str(&(self.span_to_expanded_string_internal(span
, &indent
)));
1041 /// Return the source span - this is either the supplied span, or the span for
1042 /// the macro callsite that expanded to it.
1043 pub fn source_callsite(&self, sp
: Span
) -> Span
{
1045 // Special case - if a macro is parsed as an argument to another macro, the source
1046 // callsite is the first callsite, which is also source-equivalent to the span.
1047 let mut first
= true;
1048 while span
.expn_id
!= NO_EXPANSION
&& span
.expn_id
!= COMMAND_LINE_EXPN
{
1049 if let Some(callsite
) = self.with_expn_info(span
.expn_id
,
1050 |ei
| ei
.map(|ei
| ei
.call_site
.clone())) {
1051 if first
&& span
.source_equal(&callsite
) {
1052 if self.lookup_char_pos(span
.lo
).file
.is_real_file() {
1053 return Span { expn_id: NO_EXPANSION, .. span }
;
1066 /// Return the source callee.
1068 /// Returns None if the supplied span has no expansion trace,
1069 /// else returns the NameAndSpan for the macro definition
1070 /// corresponding to the source callsite.
1071 pub fn source_callee(&self, sp
: Span
) -> Option
<NameAndSpan
> {
1073 // Special case - if a macro is parsed as an argument to another macro, the source
1074 // callsite is source-equivalent to the span, and the source callee is the first callee.
1075 let mut first
= true;
1076 while let Some(callsite
) = self.with_expn_info(span
.expn_id
,
1077 |ei
| ei
.map(|ei
| ei
.call_site
.clone())) {
1078 if first
&& span
.source_equal(&callsite
) {
1079 if self.lookup_char_pos(span
.lo
).file
.is_real_file() {
1080 return self.with_expn_info(span
.expn_id
,
1081 |ei
| ei
.map(|ei
| ei
.callee
.clone()));
1085 if let Some(_
) = self.with_expn_info(callsite
.expn_id
,
1086 |ei
| ei
.map(|ei
| ei
.call_site
.clone())) {
1090 return self.with_expn_info(span
.expn_id
,
1091 |ei
| ei
.map(|ei
| ei
.callee
.clone()));
1097 pub fn span_to_filename(&self, sp
: Span
) -> FileName
{
1098 self.lookup_char_pos(sp
.lo
).file
.name
.to_string()
1101 pub fn span_to_lines(&self, sp
: Span
) -> FileLinesResult
{
1103 return Err(SpanLinesError
::IllFormedSpan(sp
));
1106 let lo
= self.lookup_char_pos(sp
.lo
);
1107 let hi
= self.lookup_char_pos(sp
.hi
);
1109 if lo
.file
.start_pos
!= hi
.file
.start_pos
{
1110 return Err(SpanLinesError
::DistinctSources(DistinctSources
{
1111 begin
: (lo
.file
.name
.clone(), lo
.file
.start_pos
),
1112 end
: (hi
.file
.name
.clone(), hi
.file
.start_pos
),
1115 assert
!(hi
.line
>= lo
.line
);
1117 let mut lines
= Vec
::with_capacity(hi
.line
- lo
.line
+ 1);
1119 // The span starts partway through the first line,
1120 // but after that it starts from offset 0.
1121 let mut start_col
= lo
.col
;
1123 // For every line but the last, it extends from `start_col`
1124 // and to the end of the line. Be careful because the line
1125 // numbers in Loc are 1-based, so we subtract 1 to get 0-based
1127 for line_index
in lo
.line
-1 .. hi
.line
-1 {
1128 let line_len
= lo
.file
.get_line(line_index
).map(|s
| s
.len()).unwrap_or(0);
1129 lines
.push(LineInfo
{ line_index
: line_index
,
1130 start_col
: start_col
,
1131 end_col
: CharPos
::from_usize(line_len
) });
1132 start_col
= CharPos
::from_usize(0);
1135 // For the last line, it extends from `start_col` to `hi.col`:
1136 lines
.push(LineInfo
{ line_index
: hi
.line
- 1,
1137 start_col
: start_col
,
1140 Ok(FileLines {file: lo.file, lines: lines}
)
1143 pub fn span_to_snippet(&self, sp
: Span
) -> Result
<String
, SpanSnippetError
> {
1145 return Err(SpanSnippetError
::IllFormedSpan(sp
));
1148 let local_begin
= self.lookup_byte_offset(sp
.lo
);
1149 let local_end
= self.lookup_byte_offset(sp
.hi
);
1151 if local_begin
.fm
.start_pos
!= local_end
.fm
.start_pos
{
1152 return Err(SpanSnippetError
::DistinctSources(DistinctSources
{
1153 begin
: (local_begin
.fm
.name
.clone(),
1154 local_begin
.fm
.start_pos
),
1155 end
: (local_end
.fm
.name
.clone(),
1156 local_end
.fm
.start_pos
)
1159 match local_begin
.fm
.src
{
1161 let start_index
= local_begin
.pos
.to_usize();
1162 let end_index
= local_end
.pos
.to_usize();
1163 let source_len
= (local_begin
.fm
.end_pos
-
1164 local_begin
.fm
.start_pos
).to_usize();
1166 if start_index
> end_index
|| end_index
> source_len
{
1167 return Err(SpanSnippetError
::MalformedForCodemap(
1168 MalformedCodemapPositions
{
1169 name
: local_begin
.fm
.name
.clone(),
1170 source_len
: source_len
,
1171 begin_pos
: local_begin
.pos
,
1172 end_pos
: local_end
.pos
,
1176 return Ok((&src
[start_index
..end_index
]).to_string())
1179 return Err(SpanSnippetError
::SourceNotAvailable
{
1180 filename
: local_begin
.fm
.name
.clone()
1187 /// Groups and sorts spans by lines into `MultiSpan`s, where `push` adds them to their group,
1188 /// specifying the unification behaviour for overlapping spans.
1189 /// Spans overflowing a line are put into their own one-element-group.
1190 pub fn custom_group_spans
<F
>(&self, mut spans
: Vec
<Span
>, push
: F
) -> Vec
<MultiSpan
>
1191 where F
: Fn(&mut MultiSpan
, Span
)
1193 spans
.sort_by(|a
, b
| a
.lo
.cmp(&b
.lo
));
1194 let mut groups
= Vec
::<MultiSpan
>::new();
1195 let mut overflowing
= vec
![];
1196 let mut prev_expn
= ExpnId(!2u32);
1197 let mut prev_file
= !0usize
;
1198 let mut prev_line
= !0usize
;
1199 let mut err_size
= 0;
1202 let line
= self.lookup_char_pos(sp
.lo
).line
;
1203 let line_hi
= self.lookup_char_pos(sp
.hi
).line
;
1204 if line
!= line_hi
{
1205 overflowing
.push(sp
.into());
1208 let file
= self.lookup_filemap_idx(sp
.lo
);
1210 if err_size
< MAX_HIGHLIGHT_LINES
&& sp
.expn_id
== prev_expn
&& file
== prev_file
{
1211 // `push` takes care of sorting, trimming, and merging
1212 push(&mut groups
.last_mut().unwrap(), sp
);
1213 if line
!= prev_line
{
1217 groups
.push(sp
.into());
1220 prev_expn
= sp
.expn_id
;
1224 groups
.extend(overflowing
);
1228 /// Groups and sorts spans by lines into `MultiSpan`s, merging overlapping spans.
1229 /// Spans overflowing a line are put into their own one-element-group.
1230 pub fn group_spans(&self, spans
: Vec
<Span
>) -> Vec
<MultiSpan
> {
1231 self.custom_group_spans(spans
, |msp
, sp
| msp
.push_merge(sp
))
1234 /// Like `group_spans`, but trims overlapping spans instead of
1235 /// merging them (for use with `end_highlight_lines`)
1236 pub fn end_group_spans(&self, spans
: Vec
<Span
>) -> Vec
<MultiSpan
> {
1237 self.custom_group_spans(spans
, |msp
, sp
| msp
.push_trim(sp
))
1240 pub fn get_filemap(&self, filename
: &str) -> Rc
<FileMap
> {
1241 for fm
in self.files
.borrow().iter() {
1242 if filename
== fm
.name
{
1246 panic
!("asking for {} which we don't know about", filename
);
1249 /// For a global BytePos compute the local offset within the containing FileMap
1250 pub fn lookup_byte_offset(&self, bpos
: BytePos
) -> FileMapAndBytePos
{
1251 let idx
= self.lookup_filemap_idx(bpos
);
1252 let fm
= (*self.files
.borrow())[idx
].clone();
1253 let offset
= bpos
- fm
.start_pos
;
1254 FileMapAndBytePos {fm: fm, pos: offset}
1257 /// Converts an absolute BytePos to a CharPos relative to the filemap.
1258 pub fn bytepos_to_file_charpos(&self, bpos
: BytePos
) -> CharPos
{
1259 let idx
= self.lookup_filemap_idx(bpos
);
1260 let files
= self.files
.borrow();
1261 let map
= &(*files
)[idx
];
1263 // The number of extra bytes due to multibyte chars in the FileMap
1264 let mut total_extra_bytes
= 0;
1266 for mbc
in map
.multibyte_chars
.borrow().iter() {
1267 debug
!("{}-byte char at {:?}", mbc
.bytes
, mbc
.pos
);
1269 // every character is at least one byte, so we only
1270 // count the actual extra bytes.
1271 total_extra_bytes
+= mbc
.bytes
- 1;
1272 // We should never see a byte position in the middle of a
1274 assert
!(bpos
.to_usize() >= mbc
.pos
.to_usize() + mbc
.bytes
);
1280 assert
!(map
.start_pos
.to_usize() + total_extra_bytes
<= bpos
.to_usize());
1281 CharPos(bpos
.to_usize() - map
.start_pos
.to_usize() - total_extra_bytes
)
1284 // Return the index of the filemap (in self.files) which contains pos.
1285 fn lookup_filemap_idx(&self, pos
: BytePos
) -> usize {
1286 let files
= self.files
.borrow();
1287 let files
= &*files
;
1288 let count
= files
.len();
1290 // Binary search for the filemap.
1294 let m
= (a
+ b
) / 2;
1295 if files
[m
].start_pos
> pos
{
1302 assert
!(a
< count
, "position {} does not resolve to a source location", pos
.to_usize());
1307 /// Check if the backtrace `subtrace` contains `suptrace` as a prefix.
1308 pub fn more_specific_trace(&self,
1309 mut subtrace
: ExpnId
,
1313 if subtrace
== suptrace
{
1317 let stop
= self.with_expn_info(subtrace
, |opt_expn_info
| {
1318 if let Some(expn_info
) = opt_expn_info
{
1319 subtrace
= expn_info
.call_site
.expn_id
;
1332 pub fn record_expansion(&self, expn_info
: ExpnInfo
) -> ExpnId
{
1333 let mut expansions
= self.expansions
.borrow_mut();
1334 expansions
.push(expn_info
);
1335 let len
= expansions
.len();
1336 if len
> u32::max_value() as usize {
1337 panic
!("too many ExpnInfo's!");
1339 ExpnId(len
as u32 - 1)
1342 pub fn with_expn_info
<T
, F
>(&self, id
: ExpnId
, f
: F
) -> T
where
1343 F
: FnOnce(Option
<&ExpnInfo
>) -> T
,
1346 NO_EXPANSION
| COMMAND_LINE_EXPN
=> f(None
),
1347 ExpnId(i
) => f(Some(&(*self.expansions
.borrow())[i
as usize]))
1351 /// Check if a span is "internal" to a macro in which #[unstable]
1352 /// items can be used (that is, a macro marked with
1353 /// `#[allow_internal_unstable]`).
1354 pub fn span_allows_unstable(&self, span
: Span
) -> bool
{
1355 debug
!("span_allows_unstable(span = {:?})", span
);
1356 let mut allows_unstable
= false;
1357 let mut expn_id
= span
.expn_id
;
1359 let quit
= self.with_expn_info(expn_id
, |expninfo
| {
1360 debug
!("span_allows_unstable: expninfo = {:?}", expninfo
);
1361 expninfo
.map_or(/* hit the top level */ true, |info
| {
1363 let span_comes_from_this_expansion
=
1364 info
.callee
.span
.map_or(span
.source_equal(&info
.call_site
), |mac_span
| {
1365 mac_span
.contains(span
)
1368 debug
!("span_allows_unstable: span: {:?} call_site: {:?} callee: {:?}",
1370 (info
.call_site
.lo
, info
.call_site
.hi
),
1371 info
.callee
.span
.map(|x
| (x
.lo
, x
.hi
)));
1372 debug
!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
1373 span_comes_from_this_expansion
,
1374 info
.callee
.allow_internal_unstable
);
1375 if span_comes_from_this_expansion
{
1376 allows_unstable
= info
.callee
.allow_internal_unstable
;
1377 // we've found the right place, stop looking
1380 // not the right place, keep looking
1381 expn_id
= info
.call_site
.expn_id
;
1390 debug
!("span_allows_unstable? {}", allows_unstable
);
1394 pub fn count_lines(&self) -> usize {
1395 self.files
.borrow().iter().fold(0, |a
, f
| a
+ f
.count_lines())
1399 // _____________________________________________________________________________
1400 // SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
1403 pub type FileLinesResult
= Result
<FileLines
, SpanLinesError
>;
1405 #[derive(Clone, PartialEq, Eq, Debug)]
1406 pub enum SpanLinesError
{
1407 IllFormedSpan(Span
),
1408 DistinctSources(DistinctSources
),
1411 #[derive(Clone, PartialEq, Eq, Debug)]
1412 pub enum SpanSnippetError
{
1413 IllFormedSpan(Span
),
1414 DistinctSources(DistinctSources
),
1415 MalformedForCodemap(MalformedCodemapPositions
),
1416 SourceNotAvailable { filename: String }
1419 #[derive(Clone, PartialEq, Eq, Debug)]
1420 pub struct DistinctSources
{
1421 begin
: (String
, BytePos
),
1422 end
: (String
, BytePos
)
1425 #[derive(Clone, PartialEq, Eq, Debug)]
1426 pub struct MalformedCodemapPositions
{
1434 // _____________________________________________________________________________
1444 let cm
= CodeMap
::new();
1445 let fm
= cm
.new_filemap("blork.rs".to_string(),
1446 "first line.\nsecond line".to_string());
1447 fm
.next_line(BytePos(0));
1448 // Test we can get lines with partial line info.
1449 assert_eq
!(fm
.get_line(0), Some("first line."));
1450 // TESTING BROKEN BEHAVIOR: line break declared before actual line break.
1451 fm
.next_line(BytePos(10));
1452 assert_eq
!(fm
.get_line(1), Some("."));
1453 fm
.next_line(BytePos(12));
1454 assert_eq
!(fm
.get_line(2), Some("second line"));
1460 let cm
= CodeMap
::new();
1461 let fm
= cm
.new_filemap("blork.rs".to_string(),
1462 "first line.\nsecond line".to_string());
1463 // TESTING *REALLY* BROKEN BEHAVIOR:
1464 fm
.next_line(BytePos(0));
1465 fm
.next_line(BytePos(10));
1466 fm
.next_line(BytePos(2));
1469 fn init_code_map() -> CodeMap
{
1470 let cm
= CodeMap
::new();
1471 let fm1
= cm
.new_filemap("blork.rs".to_string(),
1472 "first line.\nsecond line".to_string());
1473 let fm2
= cm
.new_filemap("empty.rs".to_string(),
1475 let fm3
= cm
.new_filemap("blork2.rs".to_string(),
1476 "first line.\nsecond line".to_string());
1478 fm1
.next_line(BytePos(0));
1479 fm1
.next_line(BytePos(12));
1480 fm2
.next_line(fm2
.start_pos
);
1481 fm3
.next_line(fm3
.start_pos
);
1482 fm3
.next_line(fm3
.start_pos
+ BytePos(12));
1489 // Test lookup_byte_offset
1490 let cm
= init_code_map();
1492 let fmabp1
= cm
.lookup_byte_offset(BytePos(23));
1493 assert_eq
!(fmabp1
.fm
.name
, "blork.rs");
1494 assert_eq
!(fmabp1
.pos
, BytePos(23));
1496 let fmabp1
= cm
.lookup_byte_offset(BytePos(24));
1497 assert_eq
!(fmabp1
.fm
.name
, "empty.rs");
1498 assert_eq
!(fmabp1
.pos
, BytePos(0));
1500 let fmabp2
= cm
.lookup_byte_offset(BytePos(25));
1501 assert_eq
!(fmabp2
.fm
.name
, "blork2.rs");
1502 assert_eq
!(fmabp2
.pos
, BytePos(0));
1507 // Test bytepos_to_file_charpos
1508 let cm
= init_code_map();
1510 let cp1
= cm
.bytepos_to_file_charpos(BytePos(22));
1511 assert_eq
!(cp1
, CharPos(22));
1513 let cp2
= cm
.bytepos_to_file_charpos(BytePos(25));
1514 assert_eq
!(cp2
, CharPos(0));
1519 // Test zero-length filemaps.
1520 let cm
= init_code_map();
1522 let loc1
= cm
.lookup_char_pos(BytePos(22));
1523 assert_eq
!(loc1
.file
.name
, "blork.rs");
1524 assert_eq
!(loc1
.line
, 2);
1525 assert_eq
!(loc1
.col
, CharPos(10));
1527 let loc2
= cm
.lookup_char_pos(BytePos(25));
1528 assert_eq
!(loc2
.file
.name
, "blork2.rs");
1529 assert_eq
!(loc2
.line
, 1);
1530 assert_eq
!(loc2
.col
, CharPos(0));
1533 fn init_code_map_mbc() -> CodeMap
{
1534 let cm
= CodeMap
::new();
1535 // € is a three byte utf8 char.
1537 cm
.new_filemap("blork.rs".to_string(),
1538 "fir€st €€€€ line.\nsecond line".to_string());
1539 let fm2
= cm
.new_filemap("blork2.rs".to_string(),
1540 "first line€€.\n€ second line".to_string());
1542 fm1
.next_line(BytePos(0));
1543 fm1
.next_line(BytePos(28));
1544 fm2
.next_line(fm2
.start_pos
);
1545 fm2
.next_line(fm2
.start_pos
+ BytePos(20));
1547 fm1
.record_multibyte_char(BytePos(3), 3);
1548 fm1
.record_multibyte_char(BytePos(9), 3);
1549 fm1
.record_multibyte_char(BytePos(12), 3);
1550 fm1
.record_multibyte_char(BytePos(15), 3);
1551 fm1
.record_multibyte_char(BytePos(18), 3);
1552 fm2
.record_multibyte_char(fm2
.start_pos
+ BytePos(10), 3);
1553 fm2
.record_multibyte_char(fm2
.start_pos
+ BytePos(13), 3);
1554 fm2
.record_multibyte_char(fm2
.start_pos
+ BytePos(18), 3);
1561 // Test bytepos_to_file_charpos in the presence of multi-byte chars
1562 let cm
= init_code_map_mbc();
1564 let cp1
= cm
.bytepos_to_file_charpos(BytePos(3));
1565 assert_eq
!(cp1
, CharPos(3));
1567 let cp2
= cm
.bytepos_to_file_charpos(BytePos(6));
1568 assert_eq
!(cp2
, CharPos(4));
1570 let cp3
= cm
.bytepos_to_file_charpos(BytePos(56));
1571 assert_eq
!(cp3
, CharPos(12));
1573 let cp4
= cm
.bytepos_to_file_charpos(BytePos(61));
1574 assert_eq
!(cp4
, CharPos(15));
1579 // Test span_to_lines for a span ending at the end of filemap
1580 let cm
= init_code_map();
1581 let span
= Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION}
;
1582 let file_lines
= cm
.span_to_lines(span
).unwrap();
1584 assert_eq
!(file_lines
.file
.name
, "blork.rs");
1585 assert_eq
!(file_lines
.lines
.len(), 1);
1586 assert_eq
!(file_lines
.lines
[0].line_index
, 1);
1589 /// Given a string like " ^~~~~~~~~~~~ ", produces a span
1590 /// coverting that range. The idea is that the string has the same
1591 /// length as the input, and we uncover the byte positions. Note
1592 /// that this can span lines and so on.
1593 fn span_from_selection(input
: &str, selection
: &str) -> Span
{
1594 assert_eq
!(input
.len(), selection
.len());
1595 let left_index
= selection
.find('
^').unwrap() as u32;
1596 let right_index
= selection
.rfind('
~'
).map(|x
|x
as u32).unwrap_or(left_index
);
1597 Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
1600 /// Test span_to_snippet and span_to_lines for a span coverting 3
1601 /// lines in the middle of a file.
1603 fn span_to_snippet_and_lines_spanning_multiple_lines() {
1604 let cm
= CodeMap
::new();
1605 let inputtext
= "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
1606 let selection
= " \n ^~\n~~~\n~~~~~ \n \n";
1607 cm
.new_filemap_and_lines("blork.rs", inputtext
);
1608 let span
= span_from_selection(inputtext
, selection
);
1610 // check that we are extracting the text we thought we were extracting
1611 assert_eq
!(&cm
.span_to_snippet(span
).unwrap(), "BB\nCCC\nDDDDD");
1613 // check that span_to_lines gives us the complete result with the lines/cols we expected
1614 let lines
= cm
.span_to_lines(span
).unwrap();
1615 let expected
= vec
![
1616 LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) }
,
1617 LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) }
,
1618 LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }
1620 assert_eq
!(lines
.lines
, expected
);
1625 // Test span_to_snippet for a span ending at the end of filemap
1626 let cm
= init_code_map();
1627 let span
= Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION}
;
1628 let snippet
= cm
.span_to_snippet(span
);
1630 assert_eq
!(snippet
, Ok("second line".to_string()));
1635 // Test span_to_str for a span ending at the end of filemap
1636 let cm
= init_code_map();
1637 let span
= Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION}
;
1638 let sstr
= cm
.span_to_string(span
);
1640 assert_eq
!(sstr
, "blork.rs:2:1: 2:12");
1645 // Test span_to_expanded_string works in base case (no expansion)
1646 let cm
= init_code_map();
1647 let span
= Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION }
;
1648 let sstr
= cm
.span_to_expanded_string(span
);
1649 assert_eq
!(sstr
, "blork.rs:1:1: 1:12\n`first line.`\n");
1651 let span
= Span { lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION }
;
1652 let sstr
= cm
.span_to_expanded_string(span
);
1653 assert_eq
!(sstr
, "blork.rs:2:1: 2:12\n`second line`\n");
1658 // Test span_to_expanded_string works with expansion
1660 let cm
= init_code_map();
1661 let root
= Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION }
;
1662 let format
= ExpnFormat
::MacroBang(Name(0u32));
1663 let callee
= NameAndSpan
{ format
: format
,
1664 allow_internal_unstable
: false,
1667 let info
= ExpnInfo { call_site: root, callee: callee }
;
1668 let id
= cm
.record_expansion(info
);
1669 let sp
= Span { lo: BytePos(12), hi: BytePos(23), expn_id: id }
;
1671 let sstr
= cm
.span_to_expanded_string(sp
);
1673 "blork.rs:2:1: 2:12\n`second line`\n Callsite:\n \
1674 blork.rs:1:1: 1:12\n `first line.`\n");
1677 fn init_expansion_chain(cm
: &CodeMap
) -> Span
{
1678 // Creates an expansion chain containing two recursive calls
1679 // root -> expA -> expA -> expB -> expB -> end
1682 let root
= Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION }
;
1684 let format_root
= ExpnFormat
::MacroBang(Name(0u32));
1685 let callee_root
= NameAndSpan
{ format
: format_root
,
1686 allow_internal_unstable
: false,
1689 let info_a1
= ExpnInfo { call_site: root, callee: callee_root }
;
1690 let id_a1
= cm
.record_expansion(info_a1
);
1691 let span_a1
= Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a1 }
;
1693 let format_a
= ExpnFormat
::MacroBang(Name(1u32));
1694 let callee_a
= NameAndSpan
{ format
: format_a
,
1695 allow_internal_unstable
: false,
1696 span
: Some(span_a1
) };
1698 let info_a2
= ExpnInfo { call_site: span_a1, callee: callee_a.clone() }
;
1699 let id_a2
= cm
.record_expansion(info_a2
);
1700 let span_a2
= Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a2 }
;
1702 let info_b1
= ExpnInfo { call_site: span_a2, callee: callee_a }
;
1703 let id_b1
= cm
.record_expansion(info_b1
);
1704 let span_b1
= Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b1 }
;
1706 let format_b
= ExpnFormat
::MacroBang(Name(2u32));
1707 let callee_b
= NameAndSpan
{ format
: format_b
,
1708 allow_internal_unstable
: false,
1711 let info_b2
= ExpnInfo { call_site: span_b1, callee: callee_b.clone() }
;
1712 let id_b2
= cm
.record_expansion(info_b2
);
1713 let span_b2
= Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b2 }
;
1715 let info_end
= ExpnInfo { call_site: span_b2, callee: callee_b }
;
1716 let id_end
= cm
.record_expansion(info_end
);
1717 Span { lo: BytePos(37), hi: BytePos(48), expn_id: id_end }
1722 // Test span_to_expanded_string collapses recursive macros and handles
1723 // recursive callsite and callee expansions
1724 let cm
= init_code_map();
1725 let end
= init_expansion_chain(&cm
);
1726 let sstr
= cm
.span_to_expanded_string(end
);
1728 r
"blork2.rs:2:1: 2:12
1754 assert_eq
!(sstr
, res_str
);
1759 // Test that collecting multiple spans into line-groups works correctly
1760 let cm
= CodeMap
::new();
1761 let inp
= "_aaaaa__bbb\nvv\nw\nx\ny\nz\ncccccc__ddddee__";
1762 let sp1
= " ^~~~~ \n \n \n \n \n \n ";
1763 let sp2
= " \n \n \n \n \n^\n ";
1764 let sp3
= " ^~~\n~~\n \n \n \n \n ";
1765 let sp4
= " \n \n \n \n \n \n^~~~~~ ";
1766 let sp5
= " \n \n \n \n \n \n ^~~~ ";
1767 let sp6
= " \n \n \n \n \n \n ^~~~ ";
1768 let sp_trim
= " \n \n \n \n \n \n ^~ ";
1769 let sp_merge
= " \n \n \n \n \n \n ^~~~~~ ";
1770 let sp7
= " \n ^\n \n \n \n \n ";
1771 let sp8
= " \n \n^\n \n \n \n ";
1772 let sp9
= " \n \n \n^\n \n \n ";
1773 let sp10
= " \n \n \n \n^\n \n ";
1775 let span
= |sp
, expected
| {
1776 let sp
= span_from_selection(inp
, sp
);
1777 assert_eq
!(&cm
.span_to_snippet(sp
).unwrap(), expected
);
1781 cm
.new_filemap_and_lines("blork.rs", inp
);
1782 let sp1
= span(sp1
, "aaaaa");
1783 let sp2
= span(sp2
, "z");
1784 let sp3
= span(sp3
, "bbb\nvv");
1785 let sp4
= span(sp4
, "cccccc");
1786 let sp5
= span(sp5
, "dddd");
1787 let sp6
= span(sp6
, "ddee");
1788 let sp7
= span(sp7
, "v");
1789 let sp8
= span(sp8
, "w");
1790 let sp9
= span(sp9
, "x");
1791 let sp10
= span(sp10
, "y");
1792 let sp_trim
= span(sp_trim
, "ee");
1793 let sp_merge
= span(sp_merge
, "ddddee");
1795 let spans
= vec
![sp5
, sp2
, sp4
, sp9
, sp10
, sp7
, sp3
, sp8
, sp1
, sp6
];
1797 macro_rules
! check_next
{
1798 ($groups
: expr
, $expected
: expr
) => ({
1799 let actual
= $groups
.next().map(|g
|&g
.spans
[..]);
1800 let expected
= $expected
;
1801 println
!("actual:\n{:?}\n", actual
);
1802 println
!("expected:\n{:?}\n", expected
);
1803 assert_eq
!(actual
, expected
.as_ref().map(|x
|&x
[..]));
1807 let _groups
= cm
.group_spans(spans
.clone());
1808 let it
= &mut _groups
.iter();
1810 check_next
!(it
, Some([sp1
, sp7
, sp8
, sp9
, sp10
, sp2
]));
1811 // New group because we're exceeding MAX_HIGHLIGHT_LINES
1812 check_next
!(it
, Some([sp4
, sp_merge
]));
1813 check_next
!(it
, Some([sp3
]));
1814 check_next
!(it
, None
::<[Span
; 0]>);
1816 let _groups
= cm
.end_group_spans(spans
);
1817 let it
= &mut _groups
.iter();
1819 check_next
!(it
, Some([sp1
, sp7
, sp8
, sp9
, sp10
, sp2
]));
1820 // New group because we're exceeding MAX_HIGHLIGHT_LINES
1821 check_next
!(it
, Some([sp4
, sp5
, sp_trim
]));
1822 check_next
!(it
, Some([sp3
]));
1823 check_next
!(it
, None
::<[Span
; 0]>);