1 // Copyright 2012-2013 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 source positions and related helper functions
15 //! This API is completely unstable and subject to change.
17 #![crate_name = "syntax_pos"]
18 #![unstable(feature = "rustc_private", issue = "27812")]
19 #![crate_type = "dylib"]
20 #![crate_type = "rlib"]
21 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
22 html_favicon_url
= "https://doc.rust-lang.org/favicon.ico",
23 html_root_url
= "https://doc.rust-lang.org/nightly/")]
24 #![cfg_attr(not(stage0), deny(warnings))]
26 #![feature(custom_attribute)]
27 #![allow(unused_attributes)]
28 #![feature(rustc_private)]
29 #![feature(staged_api)]
30 #![cfg_attr(stage0, feature(question_mark))]
31 #![feature(specialization)]
33 use std
::cell
::{Cell, RefCell}
;
34 use std
::ops
::{Add, Sub}
;
40 use serialize
::{Encodable, Decodable, Encoder, Decoder}
;
42 extern crate serialize
;
43 extern crate serialize
as rustc_serialize
; // used by deriving
45 pub type FileName
= String
;
47 /// Spans represent a region of code, used for error reporting. Positions in spans
48 /// are *absolute* positions from the beginning of the codemap, not positions
49 /// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
50 /// to the original source.
51 /// You must be careful if the span crosses more than one file - you will not be
52 /// able to use many of the functions on spans in codemap and you cannot assume
53 /// that the length of the span = hi - lo; there may be space in the BytePos
54 /// range between files.
55 #[derive(Clone, Copy, Hash, PartialEq, Eq)]
59 /// Information about where the macro came from, if this piece of
60 /// code was created by a macro expansion.
64 /// A collection of spans. Spans have two orthogonal attributes:
66 /// - they can be *primary spans*. In this case they are the locus of
67 /// the error, and would be rendered with `^^^`.
68 /// - they can have a *label*. In this case, the label is written next
69 /// to the mark in the snippet when we render.
70 #[derive(Clone, Debug, PartialEq, Eq)]
71 pub struct MultiSpan
{
72 primary_spans
: Vec
<Span
>,
73 span_labels
: Vec
<(Span
, String
)>,
77 /// Returns a new span representing just the end-point of this span
78 pub fn end_point(self) -> Span
{
79 let lo
= cmp
::max(self.hi
.0 - 1, self.lo
.0);
80 Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id}
83 /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
84 pub fn substitute_dummy(self, other
: Span
) -> Span
{
85 if self.source_equal(&DUMMY_SP
) { other }
else { self }
88 pub fn contains(self, other
: Span
) -> bool
{
89 self.lo
<= other
.lo
&& other
.hi
<= self.hi
92 /// Return true if the spans are equal with regards to the source text.
94 /// Use this instead of `==` when either span could be generated code,
95 /// and you only care that they point to the same bytes of source text.
96 pub fn source_equal(&self, other
: &Span
) -> bool
{
97 self.lo
== other
.lo
&& self.hi
== other
.hi
100 /// Returns `Some(span)`, where the start is trimmed by the end of `other`
101 pub fn trim_start(self, other
: Span
) -> Option
<Span
> {
102 if self.hi
> other
.hi
{
103 Some(Span { lo: cmp::max(self.lo, other.hi), .. self }
)
110 #[derive(Clone, Debug)]
111 pub struct SpanLabel
{
112 /// The span we are going to include in the final snippet.
115 /// Is this a primary span? This is the "locus" of the message,
116 /// and is indicated with a `^^^^` underline, versus `----`.
117 pub is_primary
: bool
,
119 /// What label should we attach to this span (if any)?
120 pub label
: Option
<String
>,
123 impl serialize
::UseSpecializedEncodable
for Span
{
124 fn default_encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
125 s
.emit_struct("Span", 2, |s
| {
126 s
.emit_struct_field("lo", 0, |s
| {
130 s
.emit_struct_field("hi", 1, |s
| {
137 impl serialize
::UseSpecializedDecodable
for Span
{
138 fn default_decode
<D
: Decoder
>(d
: &mut D
) -> Result
<Span
, D
::Error
> {
139 d
.read_struct("Span", 2, |d
| {
140 let lo
= d
.read_struct_field("lo", 0, Decodable
::decode
)?
;
141 let hi
= d
.read_struct_field("hi", 1, Decodable
::decode
)?
;
147 fn default_span_debug(span
: Span
, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
148 write
!(f
, "Span {{ lo: {:?}, hi: {:?}, expn_id: {:?} }}",
149 span
.lo
, span
.hi
, span
.expn_id
)
152 impl fmt
::Debug
for Span
{
153 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
154 SPAN_DEBUG
.with(|span_debug
| span_debug
.get()(*self, f
))
158 pub const DUMMY_SP
: Span
= Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION }
;
160 // Generic span to be used for code originating from the command line
161 pub const COMMAND_LINE_SP
: Span
= Span
{ lo
: BytePos(0),
163 expn_id
: COMMAND_LINE_EXPN
};
166 pub fn new() -> MultiSpan
{
168 primary_spans
: vec
![],
173 pub fn from_span(primary_span
: Span
) -> MultiSpan
{
175 primary_spans
: vec
![primary_span
],
180 pub fn from_spans(vec
: Vec
<Span
>) -> MultiSpan
{
187 pub fn push_span_label(&mut self, span
: Span
, label
: String
) {
188 self.span_labels
.push((span
, label
));
191 /// Selects the first primary span (if any)
192 pub fn primary_span(&self) -> Option
<Span
> {
193 self.primary_spans
.first().cloned()
196 /// Returns all primary spans.
197 pub fn primary_spans(&self) -> &[Span
] {
201 /// Replaces all occurances of one Span with another. Used to move Spans in areas that don't
202 /// display well (like std macros). Returns true if replacements occurred.
203 pub fn replace(&mut self, before
: Span
, after
: Span
) -> bool
{
204 let mut replacements_occurred
= false;
205 for primary_span
in &mut self.primary_spans
{
206 if *primary_span
== before
{
207 *primary_span
= after
;
208 replacements_occurred
= true;
211 for span_label
in &mut self.span_labels
{
212 if span_label
.0 == before
{
213 span_label
.0 = after
;
214 replacements_occurred
= true;
217 replacements_occurred
220 /// Returns the strings to highlight. We always ensure that there
221 /// is an entry for each of the primary spans -- for each primary
222 /// span P, if there is at least one label with span P, we return
223 /// those labels (marked as primary). But otherwise we return
224 /// `SpanLabel` instances with empty labels.
225 pub fn span_labels(&self) -> Vec
<SpanLabel
> {
226 let is_primary
= |span
| self.primary_spans
.contains(&span
);
227 let mut span_labels
= vec
![];
229 for &(span
, ref label
) in &self.span_labels
{
230 span_labels
.push(SpanLabel
{
232 is_primary
: is_primary(span
),
233 label
: Some(label
.clone())
237 for &span
in &self.primary_spans
{
238 if !span_labels
.iter().any(|sl
| sl
.span
== span
) {
239 span_labels
.push(SpanLabel
{
251 impl From
<Span
> for MultiSpan
{
252 fn from(span
: Span
) -> MultiSpan
{
253 MultiSpan
::from_span(span
)
257 #[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)]
258 pub struct ExpnId(pub u32);
260 pub const NO_EXPANSION
: ExpnId
= ExpnId(!0);
261 // For code appearing from the command line
262 pub const COMMAND_LINE_EXPN
: ExpnId
= ExpnId(!1);
264 // For code generated by a procedural macro, without knowing which
266 pub const PROC_EXPN
: ExpnId
= ExpnId(!2);
269 pub fn from_u32(id
: u32) -> ExpnId
{
273 pub fn into_u32(self) -> u32 {
278 /// Identifies an offset of a multi-byte character in a FileMap
279 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
280 pub struct MultiByteChar
{
281 /// The absolute offset of the character in the CodeMap
283 /// The number of bytes, >=2
287 /// A single source in the CodeMap.
289 /// The name of the file that the source came from, source that doesn't
290 /// originate from files has names between angle brackets by convention,
293 /// The absolute path of the file that the source came from.
294 pub abs_path
: Option
<FileName
>,
295 /// The complete source code
296 pub src
: Option
<Rc
<String
>>,
297 /// The start position of this source in the CodeMap
298 pub start_pos
: BytePos
,
299 /// The end position of this source in the CodeMap
300 pub end_pos
: BytePos
,
301 /// Locations of lines beginnings in the source code
302 pub lines
: RefCell
<Vec
<BytePos
>>,
303 /// Locations of multi-byte characters in the source code
304 pub multibyte_chars
: RefCell
<Vec
<MultiByteChar
>>,
307 impl Encodable
for FileMap
{
308 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
309 s
.emit_struct("FileMap", 6, |s
| {
310 s
.emit_struct_field("name", 0, |s
| self.name
.encode(s
))?
;
311 s
.emit_struct_field("abs_path", 1, |s
| self.abs_path
.encode(s
))?
;
312 s
.emit_struct_field("start_pos", 2, |s
| self.start_pos
.encode(s
))?
;
313 s
.emit_struct_field("end_pos", 3, |s
| self.end_pos
.encode(s
))?
;
314 s
.emit_struct_field("lines", 4, |s
| {
315 let lines
= self.lines
.borrow();
317 s
.emit_u32(lines
.len() as u32)?
;
319 if !lines
.is_empty() {
320 // In order to preserve some space, we exploit the fact that
321 // the lines list is sorted and individual lines are
322 // probably not that long. Because of that we can store lines
323 // as a difference list, using as little space as possible
324 // for the differences.
325 let max_line_length
= if lines
.len() == 1 {
329 .map(|w
| w
[1] - w
[0])
330 .map(|bp
| bp
.to_usize())
335 let bytes_per_diff
: u8 = match max_line_length
{
337 0x100 ... 0xFFFF => 2,
341 // Encode the number of bytes used per diff.
342 bytes_per_diff
.encode(s
)?
;
344 // Encode the first element.
347 let diff_iter
= (&lines
[..]).windows(2)
348 .map(|w
| (w
[1] - w
[0]));
350 match bytes_per_diff
{
351 1 => for diff
in diff_iter { (diff.0 as u8).encode(s)? }
,
352 2 => for diff
in diff_iter { (diff.0 as u16).encode(s)? }
,
353 4 => for diff
in diff_iter { diff.0.encode(s)? }
,
360 s
.emit_struct_field("multibyte_chars", 5, |s
| {
361 (*self.multibyte_chars
.borrow()).encode(s
)
367 impl Decodable
for FileMap
{
368 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<FileMap
, D
::Error
> {
370 d
.read_struct("FileMap", 6, |d
| {
371 let name
: String
= d
.read_struct_field("name", 0, |d
| Decodable
::decode(d
))?
;
372 let abs_path
: Option
<String
> =
373 d
.read_struct_field("abs_path", 1, |d
| Decodable
::decode(d
))?
;
374 let start_pos
: BytePos
= d
.read_struct_field("start_pos", 2, |d
| Decodable
::decode(d
))?
;
375 let end_pos
: BytePos
= d
.read_struct_field("end_pos", 3, |d
| Decodable
::decode(d
))?
;
376 let lines
: Vec
<BytePos
> = d
.read_struct_field("lines", 4, |d
| {
377 let num_lines
: u32 = Decodable
::decode(d
)?
;
378 let mut lines
= Vec
::with_capacity(num_lines
as usize);
381 // Read the number of bytes used per diff.
382 let bytes_per_diff
: u8 = Decodable
::decode(d
)?
;
384 // Read the first element.
385 let mut line_start
: BytePos
= Decodable
::decode(d
)?
;
386 lines
.push(line_start
);
388 for _
in 1..num_lines
{
389 let diff
= match bytes_per_diff
{
390 1 => d
.read_u8()?
as u32,
391 2 => d
.read_u16()?
as u32,
396 line_start
= line_start
+ BytePos(diff
);
398 lines
.push(line_start
);
404 let multibyte_chars
: Vec
<MultiByteChar
> =
405 d
.read_struct_field("multibyte_chars", 5, |d
| Decodable
::decode(d
))?
;
409 start_pos
: start_pos
,
412 lines
: RefCell
::new(lines
),
413 multibyte_chars
: RefCell
::new(multibyte_chars
)
419 impl fmt
::Debug
for FileMap
{
420 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
421 write
!(fmt
, "FileMap({})", self.name
)
426 /// EFFECT: register a start-of-line offset in the
427 /// table of line-beginnings.
428 /// UNCHECKED INVARIANT: these offsets must be added in the right
429 /// order and must be in the right places; there is shared knowledge
430 /// about what ends a line between this file and parse.rs
431 /// WARNING: pos param here is the offset relative to start of CodeMap,
432 /// and CodeMap will append a newline when adding a filemap without a newline at the end,
433 /// so the safe way to call this is with value calculated as
434 /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap.
435 pub fn next_line(&self, pos
: BytePos
) {
436 // the new charpos must be > the last one (or it's the first one).
437 let mut lines
= self.lines
.borrow_mut();
438 let line_len
= lines
.len();
439 assert
!(line_len
== 0 || ((*lines
)[line_len
- 1] < pos
));
443 /// get a line from the list of pre-computed line-beginnings.
444 /// line-number here is 0-based.
445 pub fn get_line(&self, line_number
: usize) -> Option
<&str> {
448 let lines
= self.lines
.borrow();
449 lines
.get(line_number
).map(|&line
| {
450 let begin
: BytePos
= line
- self.start_pos
;
451 let begin
= begin
.to_usize();
452 // We can't use `lines.get(line_number+1)` because we might
453 // be parsing when we call this function and thus the current
454 // line is the last one we have line info for.
455 let slice
= &src
[begin
..];
456 match slice
.find('
\n'
) {
457 Some(e
) => &slice
[..e
],
466 pub fn record_multibyte_char(&self, pos
: BytePos
, bytes
: usize) {
467 assert
!(bytes
>=2 && bytes
<= 4);
468 let mbc
= MultiByteChar
{
472 self.multibyte_chars
.borrow_mut().push(mbc
);
475 pub fn is_real_file(&self) -> bool
{
476 !(self.name
.starts_with("<") &&
477 self.name
.ends_with(">"))
480 pub fn is_imported(&self) -> bool
{
484 pub fn byte_length(&self) -> u32 {
485 self.end_pos
.0 - self.start_pos
.0
487 pub fn count_lines(&self) -> usize {
488 self.lines
.borrow().len()
491 /// Find the line containing the given position. The return value is the
492 /// index into the `lines` array of this FileMap, not the 1-based line
493 /// number. If the filemap is empty or the position is located before the
494 /// first line, None is returned.
495 pub fn lookup_line(&self, pos
: BytePos
) -> Option
<usize> {
496 let lines
= self.lines
.borrow();
497 if lines
.len() == 0 {
501 let line_index
= lookup_line(&lines
[..], pos
);
502 assert
!(line_index
< lines
.len() as isize);
504 Some(line_index
as usize)
510 pub fn line_bounds(&self, line_index
: usize) -> (BytePos
, BytePos
) {
511 if self.start_pos
== self.end_pos
{
512 return (self.start_pos
, self.end_pos
);
515 let lines
= self.lines
.borrow();
516 assert
!(line_index
< lines
.len());
517 if line_index
== (lines
.len() - 1) {
518 (lines
[line_index
], self.end_pos
)
520 (lines
[line_index
], lines
[line_index
+ 1])
525 // _____________________________________________________________________________
526 // Pos, BytePos, CharPos
530 fn from_usize(n
: usize) -> Self;
531 fn to_usize(&self) -> usize;
534 /// A byte offset. Keep this small (currently 32-bits), as AST contains
536 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
537 pub struct BytePos(pub u32);
539 /// A character offset. Because of multibyte utf8 characters, a byte offset
540 /// is not equivalent to a character offset. The CodeMap will convert BytePos
541 /// values to CharPos values as necessary.
542 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
543 pub struct CharPos(pub usize);
545 // FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
546 // have been unsuccessful
548 impl Pos
for BytePos
{
549 fn from_usize(n
: usize) -> BytePos { BytePos(n as u32) }
550 fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize }
553 impl Add
for BytePos
{
554 type Output
= BytePos
;
556 fn add(self, rhs
: BytePos
) -> BytePos
{
557 BytePos((self.to_usize() + rhs
.to_usize()) as u32)
561 impl Sub
for BytePos
{
562 type Output
= BytePos
;
564 fn sub(self, rhs
: BytePos
) -> BytePos
{
565 BytePos((self.to_usize() - rhs
.to_usize()) as u32)
569 impl Encodable
for BytePos
{
570 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
575 impl Decodable
for BytePos
{
576 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<BytePos
, D
::Error
> {
577 Ok(BytePos(d
.read_u32()?
))
581 impl Pos
for CharPos
{
582 fn from_usize(n
: usize) -> CharPos { CharPos(n) }
583 fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
586 impl Add
for CharPos
{
587 type Output
= CharPos
;
589 fn add(self, rhs
: CharPos
) -> CharPos
{
590 CharPos(self.to_usize() + rhs
.to_usize())
594 impl Sub
for CharPos
{
595 type Output
= CharPos
;
597 fn sub(self, rhs
: CharPos
) -> CharPos
{
598 CharPos(self.to_usize() - rhs
.to_usize())
602 // _____________________________________________________________________________
603 // Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
606 /// A source code location used for error reporting
607 #[derive(Debug, Clone)]
609 /// Information about the original source
610 pub file
: Rc
<FileMap
>,
611 /// The (1-based) line number
613 /// The (0-based) column offset
617 /// A source code location used as the result of lookup_char_pos_adj
618 // Actually, *none* of the clients use the filename *or* file field;
619 // perhaps they should just be removed.
621 pub struct LocWithOpt
{
622 pub filename
: FileName
,
625 pub file
: Option
<Rc
<FileMap
>>,
628 // used to be structural records. Better names, anyone?
630 pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
632 pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
634 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
635 pub struct LineInfo
{
636 /// Index of line, starting from 0.
637 pub line_index
: usize,
639 /// Column in line where span begins, starting from 0.
640 pub start_col
: CharPos
,
642 /// Column in line where span ends, starting from 0, exclusive.
643 pub end_col
: CharPos
,
646 pub struct FileLines
{
647 pub file
: Rc
<FileMap
>,
648 pub lines
: Vec
<LineInfo
>
651 thread_local
!(pub static SPAN_DEBUG
: Cell
<fn(Span
, &mut fmt
::Formatter
) -> fmt
::Result
> =
652 Cell
::new(default_span_debug
));
654 /* assuming that we're not in macro expansion */
655 pub fn mk_sp(lo
: BytePos
, hi
: BytePos
) -> Span
{
656 Span {lo: lo, hi: hi, expn_id: NO_EXPANSION}
659 pub struct MacroBacktrace
{
660 /// span where macro was applied to generate this code
663 /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
664 pub macro_decl_name
: String
,
666 /// span where macro was defined (if known)
667 pub def_site_span
: Option
<Span
>,
670 // _____________________________________________________________________________
671 // SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
674 pub type FileLinesResult
= Result
<FileLines
, SpanLinesError
>;
676 #[derive(Clone, PartialEq, Eq, Debug)]
677 pub enum SpanLinesError
{
679 DistinctSources(DistinctSources
),
682 #[derive(Clone, PartialEq, Eq, Debug)]
683 pub enum SpanSnippetError
{
685 DistinctSources(DistinctSources
),
686 MalformedForCodemap(MalformedCodemapPositions
),
687 SourceNotAvailable { filename: String }
690 #[derive(Clone, PartialEq, Eq, Debug)]
691 pub struct DistinctSources
{
692 pub begin
: (String
, BytePos
),
693 pub end
: (String
, BytePos
)
696 #[derive(Clone, PartialEq, Eq, Debug)]
697 pub struct MalformedCodemapPositions
{
699 pub source_len
: usize,
700 pub begin_pos
: BytePos
,
704 // Given a slice of line start positions and a position, returns the index of
705 // the line the position is on. Returns -1 if the position is located before
707 fn lookup_line(lines
: &[BytePos
], pos
: BytePos
) -> isize {
708 match lines
.binary_search(&pos
) {
709 Ok(line
) => line
as isize,
710 Err(line
) => line
as isize - 1
716 use super::{lookup_line, BytePos}
;
719 fn test_lookup_line() {
721 let lines
= &[BytePos(3), BytePos(17), BytePos(28)];
723 assert_eq
!(lookup_line(lines
, BytePos(0)), -1);
724 assert_eq
!(lookup_line(lines
, BytePos(3)), 0);
725 assert_eq
!(lookup_line(lines
, BytePos(4)), 0);
727 assert_eq
!(lookup_line(lines
, BytePos(16)), 0);
728 assert_eq
!(lookup_line(lines
, BytePos(17)), 1);
729 assert_eq
!(lookup_line(lines
, BytePos(18)), 1);
731 assert_eq
!(lookup_line(lines
, BytePos(28)), 2);
732 assert_eq
!(lookup_line(lines
, BytePos(29)), 2);