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 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
18 html_favicon_url
= "https://doc.rust-lang.org/favicon.ico",
19 html_root_url
= "https://doc.rust-lang.org/nightly/")]
23 #![feature(custom_attribute)]
24 #![feature(i128_type)]
25 #![feature(optin_builtin_traits)]
26 #![allow(unused_attributes)]
27 #![feature(specialization)]
30 use std
::cell
::{Cell, RefCell}
;
31 use std
::cmp
::{self, Ordering}
;
33 use std
::hash
::Hasher
;
34 use std
::ops
::{Add, Sub}
;
35 use std
::path
::PathBuf
;
38 use rustc_data_structures
::stable_hasher
::StableHasher
;
40 extern crate rustc_data_structures
;
42 use serialize
::{Encodable, Decodable, Encoder, Decoder}
;
44 extern crate serialize
;
45 extern crate serialize
as rustc_serialize
; // used by deriving
47 extern crate unicode_width
;
50 pub use hygiene
::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind}
;
53 pub use span_encoding
::{Span, DUMMY_SP}
;
57 pub type FileName
= String
;
59 /// Spans represent a region of code, used for error reporting. Positions in spans
60 /// are *absolute* positions from the beginning of the codemap, not positions
61 /// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
62 /// to the original source.
63 /// You must be careful if the span crosses more than one file - you will not be
64 /// able to use many of the functions on spans in codemap and you cannot assume
65 /// that the length of the span = hi - lo; there may be space in the BytePos
66 /// range between files.
68 /// `SpanData` is public because `Span` uses a thread-local interner and can't be
69 /// sent to other threads, but some pieces of performance infra run in a separate thread.
70 /// Using `Span` is generally preferred.
71 #[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)]
75 /// Information about where the macro came from, if this piece of
76 /// code was created by a macro expansion.
77 pub ctxt
: SyntaxContext
,
82 pub fn with_lo(&self, lo
: BytePos
) -> Span
{
83 Span
::new(lo
, self.hi
, self.ctxt
)
86 pub fn with_hi(&self, hi
: BytePos
) -> Span
{
87 Span
::new(self.lo
, hi
, self.ctxt
)
90 pub fn with_ctxt(&self, ctxt
: SyntaxContext
) -> Span
{
91 Span
::new(self.lo
, self.hi
, ctxt
)
95 // The interner in thread-local, so `Span` shouldn't move between threads.
96 impl !Send
for Span {}
97 impl !Sync
for Span {}
99 impl PartialOrd
for Span
{
100 fn partial_cmp(&self, rhs
: &Self) -> Option
<Ordering
> {
101 PartialOrd
::partial_cmp(&self.data(), &rhs
.data())
105 fn cmp(&self, rhs
: &Self) -> Ordering
{
106 Ord
::cmp(&self.data(), &rhs
.data())
110 /// A collection of spans. Spans have two orthogonal attributes:
112 /// - they can be *primary spans*. In this case they are the locus of
113 /// the error, and would be rendered with `^^^`.
114 /// - they can have a *label*. In this case, the label is written next
115 /// to the mark in the snippet when we render.
116 #[derive(Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
117 pub struct MultiSpan
{
118 primary_spans
: Vec
<Span
>,
119 span_labels
: Vec
<(Span
, String
)>,
124 pub fn lo(self) -> BytePos
{
128 pub fn with_lo(self, lo
: BytePos
) -> Span
{
129 self.data().with_lo(lo
)
132 pub fn hi(self) -> BytePos
{
136 pub fn with_hi(self, hi
: BytePos
) -> Span
{
137 self.data().with_hi(hi
)
140 pub fn ctxt(self) -> SyntaxContext
{
144 pub fn with_ctxt(self, ctxt
: SyntaxContext
) -> Span
{
145 self.data().with_ctxt(ctxt
)
148 /// Returns a new span representing just the end-point of this span
149 pub fn end_point(self) -> Span
{
150 let span
= self.data();
151 let lo
= cmp
::max(span
.hi
.0 - 1, span
.lo
.0);
152 span
.with_lo(BytePos(lo
))
155 /// Returns a new span representing the next character after the end-point of this span
156 pub fn next_point(self) -> Span
{
157 let span
= self.data();
158 let lo
= cmp
::max(span
.hi
.0, span
.lo
.0 + 1);
159 Span
::new(BytePos(lo
), BytePos(lo
), span
.ctxt
)
162 /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
163 pub fn substitute_dummy(self, other
: Span
) -> Span
{
164 if self.source_equal(&DUMMY_SP
) { other }
else { self }
167 /// Return true if `self` fully encloses `other`.
168 pub fn contains(self, other
: Span
) -> bool
{
169 let span
= self.data();
170 let other
= other
.data();
171 span
.lo
<= other
.lo
&& other
.hi
<= span
.hi
174 /// Return true if the spans are equal with regards to the source text.
176 /// Use this instead of `==` when either span could be generated code,
177 /// and you only care that they point to the same bytes of source text.
178 pub fn source_equal(&self, other
: &Span
) -> bool
{
179 let span
= self.data();
180 let other
= other
.data();
181 span
.lo
== other
.lo
&& span
.hi
== other
.hi
184 /// Returns `Some(span)`, where the start is trimmed by the end of `other`
185 pub fn trim_start(self, other
: Span
) -> Option
<Span
> {
186 let span
= self.data();
187 let other
= other
.data();
188 if span
.hi
> other
.hi
{
189 Some(span
.with_lo(cmp
::max(span
.lo
, other
.hi
)))
195 /// Return the source span - this is either the supplied span, or the span for
196 /// the macro callsite that expanded to it.
197 pub fn source_callsite(self) -> Span
{
198 self.ctxt().outer().expn_info().map(|info
| info
.call_site
.source_callsite()).unwrap_or(self)
201 /// Return the source callee.
203 /// Returns None if the supplied span has no expansion trace,
204 /// else returns the NameAndSpan for the macro definition
205 /// corresponding to the source callsite.
206 pub fn source_callee(self) -> Option
<NameAndSpan
> {
207 fn source_callee(info
: ExpnInfo
) -> NameAndSpan
{
208 match info
.call_site
.ctxt().outer().expn_info() {
209 Some(info
) => source_callee(info
),
213 self.ctxt().outer().expn_info().map(source_callee
)
216 /// Check if a span is "internal" to a macro in which #[unstable]
217 /// items can be used (that is, a macro marked with
218 /// `#[allow_internal_unstable]`).
219 pub fn allows_unstable(&self) -> bool
{
220 match self.ctxt().outer().expn_info() {
221 Some(info
) => info
.callee
.allow_internal_unstable
,
226 /// Check if this span arises from a compiler desugaring of kind `kind`.
227 pub fn is_compiler_desugaring(&self, kind
: CompilerDesugaringKind
) -> bool
{
228 match self.ctxt().outer().expn_info() {
229 Some(info
) => match info
.callee
.format
{
230 ExpnFormat
::CompilerDesugaring(k
) => k
== kind
,
237 /// Return the compiler desugaring that created this span, or None
238 /// if this span is not from a desugaring.
239 pub fn compiler_desugaring_kind(&self) -> Option
<CompilerDesugaringKind
> {
240 match self.ctxt().outer().expn_info() {
241 Some(info
) => match info
.callee
.format
{
242 ExpnFormat
::CompilerDesugaring(k
) => Some(k
),
249 /// Check if a span is "internal" to a macro in which `unsafe`
250 /// can be used without triggering the `unsafe_code` lint
251 // (that is, a macro marked with `#[allow_internal_unsafe]`).
252 pub fn allows_unsafe(&self) -> bool
{
253 match self.ctxt().outer().expn_info() {
254 Some(info
) => info
.callee
.allow_internal_unsafe
,
259 pub fn macro_backtrace(mut self) -> Vec
<MacroBacktrace
> {
260 let mut prev_span
= DUMMY_SP
;
261 let mut result
= vec
![];
263 let info
= match self.ctxt().outer().expn_info() {
268 let (pre
, post
) = match info
.callee
.format
{
269 ExpnFormat
::MacroAttribute(..) => ("#[", "]"),
270 ExpnFormat
::MacroBang(..) => ("", "!"),
271 ExpnFormat
::CompilerDesugaring(..) => ("desugaring of `", "`"),
273 let macro_decl_name
= format
!("{}{}{}", pre
, info
.callee
.name(), post
);
274 let def_site_span
= info
.callee
.span
;
276 // Don't print recursive invocations
277 if !info
.call_site
.source_equal(&prev_span
) {
278 result
.push(MacroBacktrace
{
279 call_site
: info
.call_site
,
286 self = info
.call_site
;
291 /// Return a `Span` that would enclose both `self` and `end`.
292 pub fn to(self, end
: Span
) -> Span
{
293 let span
= self.data();
294 let end
= end
.data();
296 cmp
::min(span
.lo
, end
.lo
),
297 cmp
::max(span
.hi
, end
.hi
),
298 // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480)
299 if span
.ctxt
== SyntaxContext
::empty() { end.ctxt }
else { span.ctxt }
,
303 /// Return a `Span` between the end of `self` to the beginning of `end`.
304 pub fn between(self, end
: Span
) -> Span
{
305 let span
= self.data();
306 let end
= end
.data();
310 if end
.ctxt
== SyntaxContext
::empty() { end.ctxt }
else { span.ctxt }
,
314 /// Return a `Span` between the beginning of `self` to the beginning of `end`.
315 pub fn until(self, end
: Span
) -> Span
{
316 let span
= self.data();
317 let end
= end
.data();
321 if end
.ctxt
== SyntaxContext
::empty() { end.ctxt }
else { span.ctxt }
,
326 #[derive(Clone, Debug)]
327 pub struct SpanLabel
{
328 /// The span we are going to include in the final snippet.
331 /// Is this a primary span? This is the "locus" of the message,
332 /// and is indicated with a `^^^^` underline, versus `----`.
333 pub is_primary
: bool
,
335 /// What label should we attach to this span (if any)?
336 pub label
: Option
<String
>,
339 impl Default
for Span
{
340 fn default() -> Self {
345 impl serialize
::UseSpecializedEncodable
for Span
{
346 fn default_encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
347 let span
= self.data();
348 s
.emit_struct("Span", 2, |s
| {
349 s
.emit_struct_field("lo", 0, |s
| {
353 s
.emit_struct_field("hi", 1, |s
| {
360 impl serialize
::UseSpecializedDecodable
for Span
{
361 fn default_decode
<D
: Decoder
>(d
: &mut D
) -> Result
<Span
, D
::Error
> {
362 d
.read_struct("Span", 2, |d
| {
363 let lo
= d
.read_struct_field("lo", 0, Decodable
::decode
)?
;
364 let hi
= d
.read_struct_field("hi", 1, Decodable
::decode
)?
;
365 Ok(Span
::new(lo
, hi
, NO_EXPANSION
))
370 fn default_span_debug(span
: Span
, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
371 f
.debug_struct("Span")
372 .field("lo", &span
.lo())
373 .field("hi", &span
.hi())
374 .field("ctxt", &span
.ctxt())
378 impl fmt
::Debug
for Span
{
379 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
380 SPAN_DEBUG
.with(|span_debug
| span_debug
.get()(*self, f
))
384 impl fmt
::Debug
for SpanData
{
385 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
386 SPAN_DEBUG
.with(|span_debug
| span_debug
.get()(Span
::new(self.lo
, self.hi
, self.ctxt
), f
))
391 pub fn new() -> MultiSpan
{
393 primary_spans
: vec
![],
398 pub fn from_span(primary_span
: Span
) -> MultiSpan
{
400 primary_spans
: vec
![primary_span
],
405 pub fn from_spans(vec
: Vec
<Span
>) -> MultiSpan
{
412 pub fn push_span_label(&mut self, span
: Span
, label
: String
) {
413 self.span_labels
.push((span
, label
));
416 /// Selects the first primary span (if any)
417 pub fn primary_span(&self) -> Option
<Span
> {
418 self.primary_spans
.first().cloned()
421 /// Returns all primary spans.
422 pub fn primary_spans(&self) -> &[Span
] {
426 /// Replaces all occurrences of one Span with another. Used to move Spans in areas that don't
427 /// display well (like std macros). Returns true if replacements occurred.
428 pub fn replace(&mut self, before
: Span
, after
: Span
) -> bool
{
429 let mut replacements_occurred
= false;
430 for primary_span
in &mut self.primary_spans
{
431 if *primary_span
== before
{
432 *primary_span
= after
;
433 replacements_occurred
= true;
436 for span_label
in &mut self.span_labels
{
437 if span_label
.0 == before
{
438 span_label
.0 = after
;
439 replacements_occurred
= true;
442 replacements_occurred
445 /// Returns the strings to highlight. We always ensure that there
446 /// is an entry for each of the primary spans -- for each primary
447 /// span P, if there is at least one label with span P, we return
448 /// those labels (marked as primary). But otherwise we return
449 /// `SpanLabel` instances with empty labels.
450 pub fn span_labels(&self) -> Vec
<SpanLabel
> {
451 let is_primary
= |span
| self.primary_spans
.contains(&span
);
452 let mut span_labels
= vec
![];
454 for &(span
, ref label
) in &self.span_labels
{
455 span_labels
.push(SpanLabel
{
457 is_primary
: is_primary(span
),
458 label
: Some(label
.clone())
462 for &span
in &self.primary_spans
{
463 if !span_labels
.iter().any(|sl
| sl
.span
== span
) {
464 span_labels
.push(SpanLabel
{
476 impl From
<Span
> for MultiSpan
{
477 fn from(span
: Span
) -> MultiSpan
{
478 MultiSpan
::from_span(span
)
482 impl From
<Vec
<Span
>> for MultiSpan
{
483 fn from(spans
: Vec
<Span
>) -> MultiSpan
{
484 MultiSpan
::from_spans(spans
)
488 pub const NO_EXPANSION
: SyntaxContext
= SyntaxContext
::empty();
490 /// Identifies an offset of a multi-byte character in a FileMap
491 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
492 pub struct MultiByteChar
{
493 /// The absolute offset of the character in the CodeMap
495 /// The number of bytes, >=2
499 /// Identifies an offset of a non-narrow character in a FileMap
500 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
501 pub enum NonNarrowChar
{
502 /// Represents a zero-width character
504 /// Represents a wide (fullwidth) character
509 fn new(pos
: BytePos
, width
: usize) -> Self {
511 0 => NonNarrowChar
::ZeroWidth(pos
),
512 2 => NonNarrowChar
::Wide(pos
),
513 _
=> panic
!("width {} given for non-narrow character", width
),
517 /// Returns the absolute offset of the character in the CodeMap
518 pub fn pos(&self) -> BytePos
{
520 NonNarrowChar
::ZeroWidth(p
) |
521 NonNarrowChar
::Wide(p
) => p
,
525 /// Returns the width of the character, 0 (zero-width) or 2 (wide)
526 pub fn width(&self) -> usize {
528 NonNarrowChar
::ZeroWidth(_
) => 0,
529 NonNarrowChar
::Wide(_
) => 2,
534 impl Add
<BytePos
> for NonNarrowChar
{
537 fn add(self, rhs
: BytePos
) -> Self {
539 NonNarrowChar
::ZeroWidth(pos
) => NonNarrowChar
::ZeroWidth(pos
+ rhs
),
540 NonNarrowChar
::Wide(pos
) => NonNarrowChar
::Wide(pos
+ rhs
),
545 impl Sub
<BytePos
> for NonNarrowChar
{
548 fn sub(self, rhs
: BytePos
) -> Self {
550 NonNarrowChar
::ZeroWidth(pos
) => NonNarrowChar
::ZeroWidth(pos
- rhs
),
551 NonNarrowChar
::Wide(pos
) => NonNarrowChar
::Wide(pos
- rhs
),
556 /// The state of the lazy external source loading mechanism of a FileMap.
557 #[derive(PartialEq, Eq, Clone)]
558 pub enum ExternalSource
{
559 /// The external source has been loaded already.
561 /// No attempt has been made to load the external source.
563 /// A failed attempt has been made to load the external source.
565 /// No external source has to be loaded, since the FileMap represents a local crate.
569 impl ExternalSource
{
570 pub fn is_absent(&self) -> bool
{
572 ExternalSource
::Present(_
) => false,
577 pub fn get_source(&self) -> Option
<&str> {
579 ExternalSource
::Present(ref src
) => Some(src
),
585 /// A single source in the CodeMap.
588 /// The name of the file that the source came from, source that doesn't
589 /// originate from files has names between angle brackets by convention,
592 /// True if the `name` field above has been modified by -Zremap-path-prefix
593 pub name_was_remapped
: bool
,
594 /// The unmapped path of the file that the source came from.
595 /// Set to `None` if the FileMap was imported from an external crate.
596 pub unmapped_path
: Option
<PathBuf
>,
597 /// Indicates which crate this FileMap was imported from.
598 pub crate_of_origin
: u32,
599 /// The complete source code
600 pub src
: Option
<Rc
<String
>>,
601 /// The source code's hash
603 /// The external source code (used for external crates, which will have a `None`
604 /// value as `self.src`.
605 pub external_src
: RefCell
<ExternalSource
>,
606 /// The start position of this source in the CodeMap
607 pub start_pos
: BytePos
,
608 /// The end position of this source in the CodeMap
609 pub end_pos
: BytePos
,
610 /// Locations of lines beginnings in the source code
611 pub lines
: RefCell
<Vec
<BytePos
>>,
612 /// Locations of multi-byte characters in the source code
613 pub multibyte_chars
: RefCell
<Vec
<MultiByteChar
>>,
614 /// Width of characters that are not narrow in the source code
615 pub non_narrow_chars
: RefCell
<Vec
<NonNarrowChar
>>,
618 impl Encodable
for FileMap
{
619 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
620 s
.emit_struct("FileMap", 8, |s
| {
621 s
.emit_struct_field("name", 0, |s
| self.name
.encode(s
))?
;
622 s
.emit_struct_field("name_was_remapped", 1, |s
| self.name_was_remapped
.encode(s
))?
;
623 s
.emit_struct_field("src_hash", 6, |s
| self.src_hash
.encode(s
))?
;
624 s
.emit_struct_field("start_pos", 2, |s
| self.start_pos
.encode(s
))?
;
625 s
.emit_struct_field("end_pos", 3, |s
| self.end_pos
.encode(s
))?
;
626 s
.emit_struct_field("lines", 4, |s
| {
627 let lines
= self.lines
.borrow();
629 s
.emit_u32(lines
.len() as u32)?
;
631 if !lines
.is_empty() {
632 // In order to preserve some space, we exploit the fact that
633 // the lines list is sorted and individual lines are
634 // probably not that long. Because of that we can store lines
635 // as a difference list, using as little space as possible
636 // for the differences.
637 let max_line_length
= if lines
.len() == 1 {
641 .map(|w
| w
[1] - w
[0])
642 .map(|bp
| bp
.to_usize())
647 let bytes_per_diff
: u8 = match max_line_length
{
649 0x100 ... 0xFFFF => 2,
653 // Encode the number of bytes used per diff.
654 bytes_per_diff
.encode(s
)?
;
656 // Encode the first element.
659 let diff_iter
= (&lines
[..]).windows(2)
660 .map(|w
| (w
[1] - w
[0]));
662 match bytes_per_diff
{
663 1 => for diff
in diff_iter { (diff.0 as u8).encode(s)? }
,
664 2 => for diff
in diff_iter { (diff.0 as u16).encode(s)? }
,
665 4 => for diff
in diff_iter { diff.0.encode(s)? }
,
672 s
.emit_struct_field("multibyte_chars", 5, |s
| {
673 (*self.multibyte_chars
.borrow()).encode(s
)
675 s
.emit_struct_field("non_narrow_chars", 7, |s
| {
676 (*self.non_narrow_chars
.borrow()).encode(s
)
682 impl Decodable
for FileMap
{
683 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<FileMap
, D
::Error
> {
685 d
.read_struct("FileMap", 8, |d
| {
686 let name
: String
= d
.read_struct_field("name", 0, |d
| Decodable
::decode(d
))?
;
687 let name_was_remapped
: bool
=
688 d
.read_struct_field("name_was_remapped", 1, |d
| Decodable
::decode(d
))?
;
690 d
.read_struct_field("src_hash", 6, |d
| Decodable
::decode(d
))?
;
691 let start_pos
: BytePos
=
692 d
.read_struct_field("start_pos", 2, |d
| Decodable
::decode(d
))?
;
693 let end_pos
: BytePos
= d
.read_struct_field("end_pos", 3, |d
| Decodable
::decode(d
))?
;
694 let lines
: Vec
<BytePos
> = d
.read_struct_field("lines", 4, |d
| {
695 let num_lines
: u32 = Decodable
::decode(d
)?
;
696 let mut lines
= Vec
::with_capacity(num_lines
as usize);
699 // Read the number of bytes used per diff.
700 let bytes_per_diff
: u8 = Decodable
::decode(d
)?
;
702 // Read the first element.
703 let mut line_start
: BytePos
= Decodable
::decode(d
)?
;
704 lines
.push(line_start
);
706 for _
in 1..num_lines
{
707 let diff
= match bytes_per_diff
{
708 1 => d
.read_u8()?
as u32,
709 2 => d
.read_u16()?
as u32,
714 line_start
= line_start
+ BytePos(diff
);
716 lines
.push(line_start
);
722 let multibyte_chars
: Vec
<MultiByteChar
> =
723 d
.read_struct_field("multibyte_chars", 5, |d
| Decodable
::decode(d
))?
;
724 let non_narrow_chars
: Vec
<NonNarrowChar
> =
725 d
.read_struct_field("non_narrow_chars", 7, |d
| Decodable
::decode(d
))?
;
730 // `crate_of_origin` has to be set by the importer.
731 // This value matches up with rustc::hir::def_id::INVALID_CRATE.
732 // That constant is not available here unfortunately :(
733 crate_of_origin
: ::std
::u32::MAX
- 1,
738 external_src
: RefCell
::new(ExternalSource
::AbsentOk
),
739 lines
: RefCell
::new(lines
),
740 multibyte_chars
: RefCell
::new(multibyte_chars
),
741 non_narrow_chars
: RefCell
::new(non_narrow_chars
)
747 impl fmt
::Debug
for FileMap
{
748 fn fmt(&self, fmt
: &mut fmt
::Formatter
) -> fmt
::Result
{
749 write
!(fmt
, "FileMap({})", self.name
)
754 pub fn new(name
: FileName
,
755 name_was_remapped
: bool
,
756 unmapped_path
: PathBuf
,
758 start_pos
: BytePos
) -> FileMap
{
759 remove_bom(&mut src
);
761 let mut hasher
: StableHasher
<u128
> = StableHasher
::new();
762 hasher
.write(src
.as_bytes());
763 let src_hash
= hasher
.finish();
765 let end_pos
= start_pos
.to_usize() + src
.len();
770 unmapped_path
: Some(unmapped_path
),
772 src
: Some(Rc
::new(src
)),
774 external_src
: RefCell
::new(ExternalSource
::Unneeded
),
776 end_pos
: Pos
::from_usize(end_pos
),
777 lines
: RefCell
::new(Vec
::new()),
778 multibyte_chars
: RefCell
::new(Vec
::new()),
779 non_narrow_chars
: RefCell
::new(Vec
::new()),
783 /// EFFECT: register a start-of-line offset in the
784 /// table of line-beginnings.
785 /// UNCHECKED INVARIANT: these offsets must be added in the right
786 /// order and must be in the right places; there is shared knowledge
787 /// about what ends a line between this file and parse.rs
788 /// WARNING: pos param here is the offset relative to start of CodeMap,
789 /// and CodeMap will append a newline when adding a filemap without a newline at the end,
790 /// so the safe way to call this is with value calculated as
791 /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap.
792 pub fn next_line(&self, pos
: BytePos
) {
793 // the new charpos must be > the last one (or it's the first one).
794 let mut lines
= self.lines
.borrow_mut();
795 let line_len
= lines
.len();
796 assert
!(line_len
== 0 || ((*lines
)[line_len
- 1] < pos
));
800 /// Add externally loaded source.
801 /// If the hash of the input doesn't match or no input is supplied via None,
802 /// it is interpreted as an error and the corresponding enum variant is set.
803 /// The return value signifies whether some kind of source is present.
804 pub fn add_external_src
<F
>(&self, get_src
: F
) -> bool
805 where F
: FnOnce() -> Option
<String
>
807 if *self.external_src
.borrow() == ExternalSource
::AbsentOk
{
809 let mut external_src
= self.external_src
.borrow_mut();
810 if let Some(src
) = src
{
811 let mut hasher
: StableHasher
<u128
> = StableHasher
::new();
812 hasher
.write(src
.as_bytes());
814 if hasher
.finish() == self.src_hash
{
815 *external_src
= ExternalSource
::Present(src
);
819 *external_src
= ExternalSource
::AbsentErr
;
824 self.src
.is_some() || self.external_src
.borrow().get_source().is_some()
828 /// Get a line from the list of pre-computed line-beginnings.
829 /// The line number here is 0-based.
830 pub fn get_line(&self, line_number
: usize) -> Option
<Cow
<str>> {
831 fn get_until_newline(src
: &str, begin
: usize) -> &str {
832 // We can't use `lines.get(line_number+1)` because we might
833 // be parsing when we call this function and thus the current
834 // line is the last one we have line info for.
835 let slice
= &src
[begin
..];
836 match slice
.find('
\n'
) {
837 Some(e
) => &slice
[..e
],
842 let lines
= self.lines
.borrow();
843 let line
= if let Some(line
) = lines
.get(line_number
) {
848 let begin
: BytePos
= *line
- self.start_pos
;
849 let begin
= begin
.to_usize();
851 if let Some(ref src
) = self.src
{
852 Some(Cow
::from(get_until_newline(src
, begin
)))
853 } else if let Some(src
) = self.external_src
.borrow().get_source() {
854 Some(Cow
::Owned(String
::from(get_until_newline(src
, begin
))))
860 pub fn record_multibyte_char(&self, pos
: BytePos
, bytes
: usize) {
861 assert
!(bytes
>=2 && bytes
<= 4);
862 let mbc
= MultiByteChar
{
866 self.multibyte_chars
.borrow_mut().push(mbc
);
869 pub fn record_width(&self, pos
: BytePos
, ch
: char) {
870 let width
= match ch
{
872 // Tabs will consume one column.
873 // Make newlines take one column so that displayed spans can point them.
876 // Assume control characters are zero width.
877 // FIXME: How can we decide between `width` and `width_cjk`?
878 unicode_width
::UnicodeWidthChar
::width(ch
).unwrap_or(0),
880 // Only record non-narrow characters.
882 self.non_narrow_chars
.borrow_mut().push(NonNarrowChar
::new(pos
, width
));
886 pub fn is_real_file(&self) -> bool
{
887 !(self.name
.starts_with("<") &&
888 self.name
.ends_with(">"))
891 pub fn is_imported(&self) -> bool
{
895 pub fn byte_length(&self) -> u32 {
896 self.end_pos
.0 - self.start_pos
.0
898 pub fn count_lines(&self) -> usize {
899 self.lines
.borrow().len()
902 /// Find the line containing the given position. The return value is the
903 /// index into the `lines` array of this FileMap, not the 1-based line
904 /// number. If the filemap is empty or the position is located before the
905 /// first line, None is returned.
906 pub fn lookup_line(&self, pos
: BytePos
) -> Option
<usize> {
907 let lines
= self.lines
.borrow();
908 if lines
.len() == 0 {
912 let line_index
= lookup_line(&lines
[..], pos
);
913 assert
!(line_index
< lines
.len() as isize);
915 Some(line_index
as usize)
921 pub fn line_bounds(&self, line_index
: usize) -> (BytePos
, BytePos
) {
922 if self.start_pos
== self.end_pos
{
923 return (self.start_pos
, self.end_pos
);
926 let lines
= self.lines
.borrow();
927 assert
!(line_index
< lines
.len());
928 if line_index
== (lines
.len() - 1) {
929 (lines
[line_index
], self.end_pos
)
931 (lines
[line_index
], lines
[line_index
+ 1])
936 /// Remove utf-8 BOM if any.
937 fn remove_bom(src
: &mut String
) {
938 if src
.starts_with("\u{feff}") {
943 // _____________________________________________________________________________
944 // Pos, BytePos, CharPos
948 fn from_usize(n
: usize) -> Self;
949 fn to_usize(&self) -> usize;
952 /// A byte offset. Keep this small (currently 32-bits), as AST contains
954 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
955 pub struct BytePos(pub u32);
957 /// A character offset. Because of multibyte utf8 characters, a byte offset
958 /// is not equivalent to a character offset. The CodeMap will convert BytePos
959 /// values to CharPos values as necessary.
960 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
961 pub struct CharPos(pub usize);
963 // FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
964 // have been unsuccessful
966 impl Pos
for BytePos
{
967 fn from_usize(n
: usize) -> BytePos { BytePos(n as u32) }
968 fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize }
971 impl Add
for BytePos
{
972 type Output
= BytePos
;
974 fn add(self, rhs
: BytePos
) -> BytePos
{
975 BytePos((self.to_usize() + rhs
.to_usize()) as u32)
979 impl Sub
for BytePos
{
980 type Output
= BytePos
;
982 fn sub(self, rhs
: BytePos
) -> BytePos
{
983 BytePos((self.to_usize() - rhs
.to_usize()) as u32)
987 impl Encodable
for BytePos
{
988 fn encode
<S
: Encoder
>(&self, s
: &mut S
) -> Result
<(), S
::Error
> {
993 impl Decodable
for BytePos
{
994 fn decode
<D
: Decoder
>(d
: &mut D
) -> Result
<BytePos
, D
::Error
> {
995 Ok(BytePos(d
.read_u32()?
))
999 impl Pos
for CharPos
{
1000 fn from_usize(n
: usize) -> CharPos { CharPos(n) }
1001 fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
1004 impl Add
for CharPos
{
1005 type Output
= CharPos
;
1007 fn add(self, rhs
: CharPos
) -> CharPos
{
1008 CharPos(self.to_usize() + rhs
.to_usize())
1012 impl Sub
for CharPos
{
1013 type Output
= CharPos
;
1015 fn sub(self, rhs
: CharPos
) -> CharPos
{
1016 CharPos(self.to_usize() - rhs
.to_usize())
1020 // _____________________________________________________________________________
1021 // Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
1024 /// A source code location used for error reporting
1025 #[derive(Debug, Clone)]
1027 /// Information about the original source
1028 pub file
: Rc
<FileMap
>,
1029 /// The (1-based) line number
1031 /// The (0-based) column offset
1033 /// The (0-based) column offset when displayed
1034 pub col_display
: usize,
1037 /// A source code location used as the result of lookup_char_pos_adj
1038 // Actually, *none* of the clients use the filename *or* file field;
1039 // perhaps they should just be removed.
1041 pub struct LocWithOpt
{
1042 pub filename
: FileName
,
1045 pub file
: Option
<Rc
<FileMap
>>,
1048 // used to be structural records. Better names, anyone?
1050 pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
1052 pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
1054 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
1055 pub struct LineInfo
{
1056 /// Index of line, starting from 0.
1057 pub line_index
: usize,
1059 /// Column in line where span begins, starting from 0.
1060 pub start_col
: CharPos
,
1062 /// Column in line where span ends, starting from 0, exclusive.
1063 pub end_col
: CharPos
,
1066 pub struct FileLines
{
1067 pub file
: Rc
<FileMap
>,
1068 pub lines
: Vec
<LineInfo
>
1071 thread_local
!(pub static SPAN_DEBUG
: Cell
<fn(Span
, &mut fmt
::Formatter
) -> fmt
::Result
> =
1072 Cell
::new(default_span_debug
));
1075 pub struct MacroBacktrace
{
1076 /// span where macro was applied to generate this code
1077 pub call_site
: Span
,
1079 /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
1080 pub macro_decl_name
: String
,
1082 /// span where macro was defined (if known)
1083 pub def_site_span
: Option
<Span
>,
1086 // _____________________________________________________________________________
1087 // SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
1090 pub type FileLinesResult
= Result
<FileLines
, SpanLinesError
>;
1092 #[derive(Clone, PartialEq, Eq, Debug)]
1093 pub enum SpanLinesError
{
1094 IllFormedSpan(Span
),
1095 DistinctSources(DistinctSources
),
1098 #[derive(Clone, PartialEq, Eq, Debug)]
1099 pub enum SpanSnippetError
{
1100 IllFormedSpan(Span
),
1101 DistinctSources(DistinctSources
),
1102 MalformedForCodemap(MalformedCodemapPositions
),
1103 SourceNotAvailable { filename: String }
1106 #[derive(Clone, PartialEq, Eq, Debug)]
1107 pub struct DistinctSources
{
1108 pub begin
: (String
, BytePos
),
1109 pub end
: (String
, BytePos
)
1112 #[derive(Clone, PartialEq, Eq, Debug)]
1113 pub struct MalformedCodemapPositions
{
1115 pub source_len
: usize,
1116 pub begin_pos
: BytePos
,
1117 pub end_pos
: BytePos
1120 // Given a slice of line start positions and a position, returns the index of
1121 // the line the position is on. Returns -1 if the position is located before
1123 fn lookup_line(lines
: &[BytePos
], pos
: BytePos
) -> isize {
1124 match lines
.binary_search(&pos
) {
1125 Ok(line
) => line
as isize,
1126 Err(line
) => line
as isize - 1
1132 use super::{lookup_line, BytePos}
;
1135 fn test_lookup_line() {
1137 let lines
= &[BytePos(3), BytePos(17), BytePos(28)];
1139 assert_eq
!(lookup_line(lines
, BytePos(0)), -1);
1140 assert_eq
!(lookup_line(lines
, BytePos(3)), 0);
1141 assert_eq
!(lookup_line(lines
, BytePos(4)), 0);
1143 assert_eq
!(lookup_line(lines
, BytePos(16)), 0);
1144 assert_eq
!(lookup_line(lines
, BytePos(17)), 1);
1145 assert_eq
!(lookup_line(lines
, BytePos(18)), 1);
1147 assert_eq
!(lookup_line(lines
, BytePos(28)), 2);
1148 assert_eq
!(lookup_line(lines
, BytePos(29)), 2);