2012-12-04 00:48:01 +00:00
|
|
|
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2014-12-22 17:04:23 +00:00
|
|
|
//! The CodeMap tracks all the source code used within a single crate, mapping
|
|
|
|
//! from integer byte positions to the original source code location. Each bit
|
|
|
|
//! of source parsed during crate parsing (typically files, in-memory strings,
|
|
|
|
//! or various bits of macro expansion) cover a continuous range of bytes in the
|
|
|
|
//! CodeMap and are represented by FileMaps. Byte positions are stored in
|
|
|
|
//! `spans` and used pervasively in the compiler. They are absolute positions
|
|
|
|
//! within the CodeMap, which upon request can be converted to line and column
|
|
|
|
//! information, source code snippets, etc.
|
2012-11-13 00:45:24 +00:00
|
|
|
|
2015-06-16 18:47:09 +00:00
|
|
|
pub use self::ExpnFormat::*;
|
2014-11-06 08:05:53 +00:00
|
|
|
|
2015-06-17 06:56:27 +00:00
|
|
|
use std::cell::{Cell, RefCell};
|
2014-12-22 17:04:23 +00:00
|
|
|
use std::ops::{Add, Sub};
|
2015-05-13 22:44:57 +00:00
|
|
|
use std::path::Path;
|
2014-03-16 18:56:24 +00:00
|
|
|
use std::rc::Rc;
|
2015-12-13 12:12:47 +00:00
|
|
|
use std::cmp;
|
2014-12-22 17:04:23 +00:00
|
|
|
|
2015-05-13 22:44:57 +00:00
|
|
|
use std::{fmt, fs};
|
|
|
|
use std::io::{self, Read};
|
2015-04-09 06:25:48 +00:00
|
|
|
|
2014-12-22 17:04:23 +00:00
|
|
|
use serialize::{Encodable, Decodable, Encoder, Decoder};
|
2012-05-15 20:40:18 +00:00
|
|
|
|
2015-08-27 00:11:53 +00:00
|
|
|
use ast::Name;
|
2015-02-11 17:29:49 +00:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Pos, BytePos, CharPos
|
|
|
|
//
|
|
|
|
|
2013-01-26 00:57:39 +00:00
|
|
|
pub trait Pos {
|
2015-01-17 23:49:08 +00:00
|
|
|
fn from_usize(n: usize) -> Self;
|
|
|
|
fn to_usize(&self) -> usize;
|
2012-11-13 03:32:48 +00:00
|
|
|
}
|
|
|
|
|
2013-11-19 17:15:49 +00:00
|
|
|
/// A byte offset. Keep this small (currently 32-bits), as AST contains
|
|
|
|
/// a lot of them.
|
2015-12-13 12:12:47 +00:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
2014-04-01 02:01:01 +00:00
|
|
|
pub struct BytePos(pub u32);
|
2013-11-19 17:15:49 +00:00
|
|
|
|
2012-11-16 23:14:11 +00:00
|
|
|
/// A character offset. Because of multibyte utf8 characters, a byte offset
|
|
|
|
/// is not equivalent to a character offset. The CodeMap will convert BytePos
|
|
|
|
/// values to CharPos values as necessary.
|
2016-04-20 18:52:31 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
2015-01-17 23:33:05 +00:00
|
|
|
pub struct CharPos(pub usize);
|
2012-11-13 03:32:48 +00:00
|
|
|
|
2014-01-26 08:43:42 +00:00
|
|
|
// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
|
2012-11-16 23:14:11 +00:00
|
|
|
// have been unsuccessful
|
|
|
|
|
2013-02-27 01:12:00 +00:00
|
|
|
impl Pos for BytePos {
|
2015-01-17 23:49:08 +00:00
|
|
|
fn from_usize(n: usize) -> BytePos { BytePos(n as u32) }
|
|
|
|
fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize }
|
2012-11-13 03:32:48 +00:00
|
|
|
}
|
|
|
|
|
2014-12-31 20:45:13 +00:00
|
|
|
impl Add for BytePos {
|
|
|
|
type Output = BytePos;
|
|
|
|
|
2014-12-01 19:59:55 +00:00
|
|
|
fn add(self, rhs: BytePos) -> BytePos {
|
2015-01-17 23:49:08 +00:00
|
|
|
BytePos((self.to_usize() + rhs.to_usize()) as u32)
|
2014-12-01 19:59:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-31 20:45:13 +00:00
|
|
|
impl Sub for BytePos {
|
|
|
|
type Output = BytePos;
|
|
|
|
|
2014-12-01 19:59:55 +00:00
|
|
|
fn sub(self, rhs: BytePos) -> BytePos {
|
2015-01-17 23:49:08 +00:00
|
|
|
BytePos((self.to_usize() - rhs.to_usize()) as u32)
|
2014-12-01 19:59:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
impl Encodable for BytePos {
|
|
|
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
|
|
|
s.emit_u32(self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Decodable for BytePos {
|
|
|
|
fn decode<D: Decoder>(d: &mut D) -> Result<BytePos, D::Error> {
|
2016-03-23 03:01:37 +00:00
|
|
|
Ok(BytePos(d.read_u32()?))
|
2015-02-11 17:29:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-27 01:12:00 +00:00
|
|
|
impl Pos for CharPos {
|
2015-01-17 23:49:08 +00:00
|
|
|
fn from_usize(n: usize) -> CharPos { CharPos(n) }
|
|
|
|
fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
|
2012-11-13 03:32:48 +00:00
|
|
|
}
|
|
|
|
|
2014-12-31 20:45:13 +00:00
|
|
|
impl Add for CharPos {
|
|
|
|
type Output = CharPos;
|
|
|
|
|
2014-12-01 19:59:55 +00:00
|
|
|
fn add(self, rhs: CharPos) -> CharPos {
|
2015-01-17 23:49:08 +00:00
|
|
|
CharPos(self.to_usize() + rhs.to_usize())
|
2014-12-01 19:59:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-31 20:45:13 +00:00
|
|
|
impl Sub for CharPos {
|
|
|
|
type Output = CharPos;
|
|
|
|
|
2014-12-01 19:59:55 +00:00
|
|
|
fn sub(self, rhs: CharPos) -> CharPos {
|
2015-01-17 23:49:08 +00:00
|
|
|
CharPos(self.to_usize() - rhs.to_usize())
|
2014-12-01 19:59:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
// _____________________________________________________________________________
|
2015-12-13 12:12:47 +00:00
|
|
|
// Span, MultiSpan, Spanned
|
2015-02-11 17:29:49 +00:00
|
|
|
//
|
|
|
|
|
2014-11-25 01:06:06 +00:00
|
|
|
/// Spans represent a region of code, used for error reporting. Positions in spans
|
|
|
|
/// are *absolute* positions from the beginning of the codemap, not positions
|
|
|
|
/// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
|
|
|
|
/// to the original source.
|
2015-07-02 03:37:52 +00:00
|
|
|
/// You must be careful if the span crosses more than one file - you will not be
|
|
|
|
/// able to use many of the functions on spans in codemap and you cannot assume
|
|
|
|
/// that the length of the span = hi - lo; there may be space in the BytePos
|
|
|
|
/// range between files.
|
2016-01-29 06:33:14 +00:00
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
2013-08-31 16:13:04 +00:00
|
|
|
pub struct Span {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub lo: BytePos,
|
|
|
|
pub hi: BytePos,
|
2014-03-26 13:40:51 +00:00
|
|
|
/// Information about where the macro came from, if this piece of
|
|
|
|
/// code was created by a macro expansion.
|
2014-09-17 16:01:33 +00:00
|
|
|
pub expn_id: ExpnId
|
2012-11-13 01:19:56 +00:00
|
|
|
}
|
2012-11-13 01:14:15 +00:00
|
|
|
|
2016-04-20 18:52:31 +00:00
|
|
|
/// A collection of spans. Spans have two orthogonal attributes:
|
|
|
|
///
|
|
|
|
/// - they can be *primary spans*. In this case they are the locus of
|
|
|
|
/// the error, and would be rendered with `^^^`.
|
|
|
|
/// - they can have a *label*. In this case, the label is written next
|
|
|
|
/// to the mark in the snippet when we render.
|
2015-12-13 12:12:47 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct MultiSpan {
|
2016-04-20 18:52:31 +00:00
|
|
|
primary_spans: Vec<Span>,
|
|
|
|
span_labels: Vec<(Span, String)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct SpanLabel {
|
2016-04-26 16:48:54 +00:00
|
|
|
/// The span we are going to include in the final snippet.
|
2016-04-20 18:52:31 +00:00
|
|
|
pub span: Span,
|
|
|
|
|
2016-04-26 16:48:54 +00:00
|
|
|
/// Is this a primary span? This is the "locus" of the message,
|
|
|
|
/// and is indicated with a `^^^^` underline, versus `----`.
|
2016-04-20 18:52:31 +00:00
|
|
|
pub is_primary: bool,
|
|
|
|
|
2016-04-26 16:48:54 +00:00
|
|
|
/// What label should we attach to this span (if any)?
|
2016-04-20 18:52:31 +00:00
|
|
|
pub label: Option<String>,
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
|
2014-10-06 23:33:44 +00:00
|
|
|
pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
|
2014-01-01 06:53:22 +00:00
|
|
|
|
2014-12-10 20:18:23 +00:00
|
|
|
// Generic span to be used for code originating from the command line
|
|
|
|
pub const COMMAND_LINE_SP: Span = Span { lo: BytePos(0),
|
|
|
|
hi: BytePos(0),
|
|
|
|
expn_id: COMMAND_LINE_EXPN };
|
|
|
|
|
2015-08-10 18:40:46 +00:00
|
|
|
impl Span {
|
2016-04-20 18:56:01 +00:00
|
|
|
/// Returns a new span representing just the end-point of this span
|
|
|
|
pub fn end_point(self) -> Span {
|
|
|
|
let lo = cmp::max(self.hi.0 - 1, self.lo.0);
|
|
|
|
Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id}
|
|
|
|
}
|
|
|
|
|
2015-08-10 18:40:46 +00:00
|
|
|
/// Returns `self` if `self` is not the dummy span, and `other` otherwise.
|
|
|
|
pub fn substitute_dummy(self, other: Span) -> Span {
|
2016-01-29 06:33:14 +00:00
|
|
|
if self.source_equal(&DUMMY_SP) { other } else { self }
|
2015-08-10 18:40:46 +00:00
|
|
|
}
|
2015-09-26 01:44:37 +00:00
|
|
|
|
|
|
|
pub fn contains(self, other: Span) -> bool {
|
|
|
|
self.lo <= other.lo && other.hi <= self.hi
|
|
|
|
}
|
2015-12-13 12:12:47 +00:00
|
|
|
|
2016-01-29 06:33:14 +00:00
|
|
|
/// Return true if the spans are equal with regards to the source text.
|
|
|
|
///
|
|
|
|
/// Use this instead of `==` when either span could be generated code,
|
|
|
|
/// and you only care that they point to the same bytes of source text.
|
|
|
|
pub fn source_equal(&self, other: &Span) -> bool {
|
|
|
|
self.lo == other.lo && self.hi == other.hi
|
|
|
|
}
|
|
|
|
|
2015-12-13 12:12:47 +00:00
|
|
|
/// Returns `Some(span)`, a union of `self` and `other`, on overlap.
|
|
|
|
pub fn merge(self, other: Span) -> Option<Span> {
|
|
|
|
if self.expn_id != other.expn_id {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self.lo <= other.lo && self.hi > other.lo) ||
|
|
|
|
(self.lo >= other.lo && self.lo < other.hi) {
|
|
|
|
Some(Span {
|
|
|
|
lo: cmp::min(self.lo, other.lo),
|
|
|
|
hi: cmp::max(self.hi, other.hi),
|
|
|
|
expn_id: self.expn_id,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `Some(span)`, where the start is trimmed by the end of `other`
|
|
|
|
pub fn trim_start(self, other: Span) -> Option<Span> {
|
|
|
|
if self.hi > other.hi {
|
|
|
|
Some(Span { lo: cmp::max(self.lo, other.hi), .. self })
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2015-08-10 18:40:46 +00:00
|
|
|
}
|
|
|
|
|
2015-01-28 13:34:18 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
2013-08-31 16:13:04 +00:00
|
|
|
pub struct Spanned<T> {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub node: T,
|
|
|
|
pub span: Span,
|
2013-07-02 19:47:32 +00:00
|
|
|
}
|
2013-01-30 17:56:33 +00:00
|
|
|
|
2015-01-04 06:24:50 +00:00
|
|
|
impl Encodable for Span {
|
|
|
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
2016-01-20 18:04:31 +00:00
|
|
|
s.emit_struct("Span", 2, |s| {
|
2016-03-23 03:01:37 +00:00
|
|
|
s.emit_struct_field("lo", 0, |s| {
|
2016-01-20 18:04:31 +00:00
|
|
|
self.lo.encode(s)
|
2016-03-23 03:01:37 +00:00
|
|
|
})?;
|
2016-01-20 18:04:31 +00:00
|
|
|
|
|
|
|
s.emit_struct_field("hi", 1, |s| {
|
|
|
|
self.hi.encode(s)
|
|
|
|
})
|
|
|
|
})
|
2015-01-04 06:24:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Decodable for Span {
|
2015-02-11 17:29:49 +00:00
|
|
|
fn decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> {
|
2016-01-20 18:04:31 +00:00
|
|
|
d.read_struct("Span", 2, |d| {
|
2016-03-23 03:01:37 +00:00
|
|
|
let lo = d.read_struct_field("lo", 0, |d| {
|
2016-01-20 18:04:31 +00:00
|
|
|
BytePos::decode(d)
|
2016-03-23 03:01:37 +00:00
|
|
|
})?;
|
2016-01-20 18:04:31 +00:00
|
|
|
|
2016-03-23 03:01:37 +00:00
|
|
|
let hi = d.read_struct_field("hi", 1, |d| {
|
2016-01-20 18:04:31 +00:00
|
|
|
BytePos::decode(d)
|
2016-03-23 03:01:37 +00:00
|
|
|
})?;
|
2016-01-20 18:04:31 +00:00
|
|
|
|
|
|
|
Ok(mk_sp(lo, hi))
|
|
|
|
})
|
2015-01-04 06:24:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-17 06:56:27 +00:00
|
|
|
fn default_span_debug(span: Span, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "Span {{ lo: {:?}, hi: {:?}, expn_id: {:?} }}",
|
|
|
|
span.lo, span.hi, span.expn_id)
|
|
|
|
}
|
|
|
|
|
|
|
|
thread_local!(pub static SPAN_DEBUG: Cell<fn(Span, &mut fmt::Formatter) -> fmt::Result> =
|
|
|
|
Cell::new(default_span_debug));
|
|
|
|
|
|
|
|
impl fmt::Debug for Span {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
SPAN_DEBUG.with(|span_debug| span_debug.get()(*self, f))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-31 16:13:04 +00:00
|
|
|
pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> {
|
2013-02-15 09:15:53 +00:00
|
|
|
respan(mk_sp(lo, hi), t)
|
2013-01-30 17:56:33 +00:00
|
|
|
}
|
|
|
|
|
2013-08-31 16:13:04 +00:00
|
|
|
pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
|
|
|
|
Spanned {node: t, span: sp}
|
2013-01-30 17:56:33 +00:00
|
|
|
}
|
|
|
|
|
2013-08-31 16:13:04 +00:00
|
|
|
pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
|
2014-01-01 06:53:22 +00:00
|
|
|
respan(DUMMY_SP, t)
|
2013-01-30 17:56:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* assuming that we're not in macro expansion */
|
2013-08-31 16:13:04 +00:00
|
|
|
pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
|
2014-09-17 16:01:33 +00:00
|
|
|
Span {lo: lo, hi: hi, expn_id: NO_EXPANSION}
|
2013-01-30 17:56:33 +00:00
|
|
|
}
|
|
|
|
|
2014-04-09 20:42:25 +00:00
|
|
|
/// Return the span itself if it doesn't come from a macro expansion,
|
|
|
|
/// otherwise return the call site span up to the `enclosing_sp` by
|
|
|
|
/// following the `expn_info` chain.
|
2014-09-17 16:01:33 +00:00
|
|
|
pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span {
|
|
|
|
let call_site1 = cm.with_expn_info(sp.expn_id, |ei| ei.map(|ei| ei.call_site));
|
|
|
|
let call_site2 = cm.with_expn_info(enclosing_sp.expn_id, |ei| ei.map(|ei| ei.call_site));
|
|
|
|
match (call_site1, call_site2) {
|
2014-04-09 20:42:25 +00:00
|
|
|
(None, _) => sp,
|
2014-09-17 16:01:33 +00:00
|
|
|
(Some(call_site1), Some(call_site2)) if call_site1 == call_site2 => sp,
|
|
|
|
(Some(call_site1), _) => original_sp(cm, call_site1, enclosing_sp),
|
2014-04-09 20:42:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-13 12:12:47 +00:00
|
|
|
impl MultiSpan {
|
|
|
|
pub fn new() -> MultiSpan {
|
2016-04-20 18:52:31 +00:00
|
|
|
MultiSpan {
|
|
|
|
primary_spans: vec![],
|
|
|
|
span_labels: vec![]
|
|
|
|
}
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 18:52:31 +00:00
|
|
|
pub fn from_span(primary_span: Span) -> MultiSpan {
|
|
|
|
MultiSpan {
|
|
|
|
primary_spans: vec![primary_span],
|
|
|
|
span_labels: vec![]
|
|
|
|
}
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 18:52:31 +00:00
|
|
|
pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
|
|
|
|
MultiSpan {
|
|
|
|
primary_spans: vec,
|
|
|
|
span_labels: vec![]
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-20 18:52:31 +00:00
|
|
|
pub fn push_span_label(&mut self, span: Span, label: String) {
|
|
|
|
self.span_labels.push((span, label));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Selects the first primary span (if any)
|
|
|
|
pub fn primary_span(&self) -> Option<Span> {
|
|
|
|
self.primary_spans.first().cloned()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns all primary spans.
|
|
|
|
pub fn primary_spans(&self) -> &[Span] {
|
|
|
|
&self.primary_spans
|
|
|
|
}
|
|
|
|
|
2016-04-27 01:06:53 +00:00
|
|
|
/// Returns the strings to highlight. We always ensure that there
|
|
|
|
/// is an entry for each of the primary spans -- for each primary
|
|
|
|
/// span P, if there is at least one label with span P, we return
|
|
|
|
/// those labels (marked as primary). But otherwise we return
|
|
|
|
/// `SpanLabel` instances with empty labels.
|
2016-04-20 18:52:31 +00:00
|
|
|
pub fn span_labels(&self) -> Vec<SpanLabel> {
|
|
|
|
let is_primary = |span| self.primary_spans.contains(&span);
|
|
|
|
let mut span_labels = vec![];
|
|
|
|
|
|
|
|
for &(span, ref label) in &self.span_labels {
|
|
|
|
span_labels.push(SpanLabel {
|
|
|
|
span: span,
|
|
|
|
is_primary: is_primary(span),
|
|
|
|
label: Some(label.clone())
|
|
|
|
});
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 18:52:31 +00:00
|
|
|
for &span in &self.primary_spans {
|
|
|
|
if !span_labels.iter().any(|sl| sl.span == span) {
|
|
|
|
span_labels.push(SpanLabel {
|
|
|
|
span: span,
|
|
|
|
is_primary: true,
|
|
|
|
label: None
|
|
|
|
});
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-20 18:52:31 +00:00
|
|
|
|
|
|
|
span_labels
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Span> for MultiSpan {
|
|
|
|
fn from(span: Span) -> MultiSpan {
|
2016-04-20 18:52:31 +00:00
|
|
|
MultiSpan::from_span(span)
|
2015-12-13 12:12:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
|
|
|
|
//
|
|
|
|
|
2012-11-16 23:14:11 +00:00
|
|
|
/// A source code location used for error reporting
|
2015-04-09 06:25:48 +00:00
|
|
|
#[derive(Debug)]
|
2012-11-16 03:37:29 +00:00
|
|
|
pub struct Loc {
|
2012-11-16 23:14:11 +00:00
|
|
|
/// Information about the original source
|
2014-03-27 22:39:48 +00:00
|
|
|
pub file: Rc<FileMap>,
|
2012-11-16 23:14:11 +00:00
|
|
|
/// The (1-based) line number
|
2015-01-17 23:33:05 +00:00
|
|
|
pub line: usize,
|
2012-11-16 23:14:11 +00:00
|
|
|
/// The (0-based) column offset
|
2014-03-27 22:39:48 +00:00
|
|
|
pub col: CharPos
|
2012-11-13 01:14:15 +00:00
|
|
|
}
|
|
|
|
|
2013-01-30 17:56:33 +00:00
|
|
|
/// A source code location used as the result of lookup_char_pos_adj
|
|
|
|
// Actually, *none* of the clients use the filename *or* file field;
|
|
|
|
// perhaps they should just be removed.
|
2015-04-09 06:25:48 +00:00
|
|
|
#[derive(Debug)]
|
2013-01-30 17:56:33 +00:00
|
|
|
pub struct LocWithOpt {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub filename: FileName,
|
2015-01-17 23:33:05 +00:00
|
|
|
pub line: usize,
|
2014-03-27 22:39:48 +00:00
|
|
|
pub col: CharPos,
|
|
|
|
pub file: Option<Rc<FileMap>>,
|
2013-01-30 17:56:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// used to be structural records. Better names, anyone?
|
2015-04-09 06:25:48 +00:00
|
|
|
#[derive(Debug)]
|
2015-01-17 23:33:05 +00:00
|
|
|
pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
|
2015-04-09 06:25:48 +00:00
|
|
|
#[derive(Debug)]
|
2014-03-27 22:39:48 +00:00
|
|
|
pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
|
2013-12-07 02:41:11 +00:00
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
2015-06-16 18:47:09 +00:00
|
|
|
// ExpnFormat, NameAndSpan, ExpnInfo, ExpnId
|
2015-02-11 17:29:49 +00:00
|
|
|
//
|
|
|
|
|
2015-06-16 18:47:09 +00:00
|
|
|
/// The source of expansion.
|
2015-08-26 23:46:05 +00:00
|
|
|
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
|
2015-06-16 18:47:09 +00:00
|
|
|
pub enum ExpnFormat {
|
2015-01-04 03:54:18 +00:00
|
|
|
/// e.g. #[derive(...)] <item>
|
2015-08-27 00:11:53 +00:00
|
|
|
MacroAttribute(Name),
|
2014-03-26 13:40:51 +00:00
|
|
|
/// e.g. `format!()`
|
2015-08-27 00:11:53 +00:00
|
|
|
MacroBang(Name),
|
2015-08-26 23:46:05 +00:00
|
|
|
}
|
|
|
|
|
2015-01-28 13:34:18 +00:00
|
|
|
#[derive(Clone, Hash, Debug)]
|
2013-12-07 02:41:11 +00:00
|
|
|
pub struct NameAndSpan {
|
2014-03-26 13:40:51 +00:00
|
|
|
/// The format with which the macro was invoked.
|
2015-06-16 18:47:09 +00:00
|
|
|
pub format: ExpnFormat,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
/// Whether the macro is allowed to use #[unstable]/feature-gated
|
|
|
|
/// features internally without forcing the whole crate to opt-in
|
|
|
|
/// to them.
|
|
|
|
pub allow_internal_unstable: bool,
|
2014-03-26 13:40:51 +00:00
|
|
|
/// The span of the macro definition itself. The macro may not
|
|
|
|
/// have a sensible definition span (e.g. something defined
|
|
|
|
/// completely inside libsyntax) in which case this is None.
|
2014-03-27 22:39:48 +00:00
|
|
|
pub span: Option<Span>
|
2013-12-07 02:41:11 +00:00
|
|
|
}
|
2013-02-21 08:16:31 +00:00
|
|
|
|
2015-08-26 23:46:05 +00:00
|
|
|
impl NameAndSpan {
|
2015-08-27 00:11:53 +00:00
|
|
|
pub fn name(&self) -> Name {
|
2015-08-26 23:46:05 +00:00
|
|
|
match self.format {
|
2015-08-27 00:11:53 +00:00
|
|
|
ExpnFormat::MacroAttribute(s) => s,
|
|
|
|
ExpnFormat::MacroBang(s) => s,
|
2015-08-26 23:46:05 +00:00
|
|
|
}
|
2015-08-27 00:11:53 +00:00
|
|
|
}
|
2015-08-26 23:46:05 +00:00
|
|
|
}
|
|
|
|
|
2015-06-16 18:47:09 +00:00
|
|
|
/// Extra information for tracking spans of macro and syntax sugar expansion
|
2015-01-28 13:34:18 +00:00
|
|
|
#[derive(Hash, Debug)]
|
2013-07-02 09:31:00 +00:00
|
|
|
pub struct ExpnInfo {
|
2015-06-16 18:47:09 +00:00
|
|
|
/// The location of the actual macro invocation or syntax sugar , e.g.
|
|
|
|
/// `let x = foo!();` or `if let Some(y) = x {}`
|
2014-03-26 13:40:51 +00:00
|
|
|
///
|
|
|
|
/// This may recursively refer to other macro invocations, e.g. if
|
|
|
|
/// `foo!()` invoked `bar!()` internally, and there was an
|
|
|
|
/// expression inside `bar!`; the call_site of the expression in
|
|
|
|
/// the expansion would point to the `bar!` invocation; that
|
|
|
|
/// call_site span would have its own ExpnInfo, with the call_site
|
|
|
|
/// pointing to the `foo!` invocation.
|
2014-03-27 22:39:48 +00:00
|
|
|
pub call_site: Span,
|
2015-06-16 18:47:09 +00:00
|
|
|
/// Information about the expansion.
|
2014-03-27 22:39:48 +00:00
|
|
|
pub callee: NameAndSpan
|
2013-02-21 08:16:31 +00:00
|
|
|
}
|
2013-01-30 17:56:33 +00:00
|
|
|
|
2015-01-28 13:34:18 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)]
|
2014-09-28 16:25:48 +00:00
|
|
|
pub struct ExpnId(u32);
|
2014-09-17 16:01:33 +00:00
|
|
|
|
2015-04-01 17:53:32 +00:00
|
|
|
pub const NO_EXPANSION: ExpnId = ExpnId(!0);
|
2014-12-10 20:18:23 +00:00
|
|
|
// For code appearing from the command line
|
2015-04-01 17:53:32 +00:00
|
|
|
pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(!1);
|
2014-09-17 16:01:33 +00:00
|
|
|
|
2014-09-28 16:25:48 +00:00
|
|
|
impl ExpnId {
|
2015-04-21 00:51:10 +00:00
|
|
|
pub fn from_u32(id: u32) -> ExpnId {
|
|
|
|
ExpnId(id)
|
2014-09-28 16:25:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-21 00:51:10 +00:00
|
|
|
pub fn into_u32(self) -> u32 {
|
|
|
|
self.0
|
2014-09-28 16:25:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// FileMap, MultiByteChar, FileName, FileLines
|
|
|
|
//
|
|
|
|
|
2014-05-22 23:57:53 +00:00
|
|
|
pub type FileName = String;
|
2012-11-13 02:35:17 +00:00
|
|
|
|
2015-04-09 18:46:03 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct LineInfo {
|
|
|
|
/// Index of line, starting from 0.
|
|
|
|
pub line_index: usize,
|
|
|
|
|
|
|
|
/// Column in line where span begins, starting from 0.
|
|
|
|
pub start_col: CharPos,
|
|
|
|
|
|
|
|
/// Column in line where span ends, starting from 0, exclusive.
|
|
|
|
pub end_col: CharPos,
|
|
|
|
}
|
|
|
|
|
2014-03-16 18:56:24 +00:00
|
|
|
pub struct FileLines {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub file: Rc<FileMap>,
|
2015-04-09 18:46:03 +00:00
|
|
|
pub lines: Vec<LineInfo>
|
2014-03-16 18:56:24 +00:00
|
|
|
}
|
2012-11-13 02:24:56 +00:00
|
|
|
|
2012-11-16 03:37:29 +00:00
|
|
|
/// Identifies an offset of a multi-byte character in a FileMap
|
2015-03-30 13:38:59 +00:00
|
|
|
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
|
2012-11-16 03:37:29 +00:00
|
|
|
pub struct MultiByteChar {
|
|
|
|
/// The absolute offset of the character in the CodeMap
|
2014-03-27 22:39:48 +00:00
|
|
|
pub pos: BytePos,
|
2012-11-16 03:37:29 +00:00
|
|
|
/// The number of bytes, >=2
|
2015-01-17 23:33:05 +00:00
|
|
|
pub bytes: usize,
|
2012-11-16 03:37:29 +00:00
|
|
|
}
|
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
/// A single source in the CodeMap.
|
2012-11-13 02:59:37 +00:00
|
|
|
pub struct FileMap {
|
2012-11-16 23:14:11 +00:00
|
|
|
/// The name of the file that the source came from, source that doesn't
|
|
|
|
/// originate from files has names between angle brackets by convention,
|
|
|
|
/// e.g. `<anon>`
|
2014-03-27 22:39:48 +00:00
|
|
|
pub name: FileName,
|
2012-11-16 23:14:11 +00:00
|
|
|
/// The complete source code
|
2015-02-11 17:29:49 +00:00
|
|
|
pub src: Option<Rc<String>>,
|
2012-11-16 23:14:11 +00:00
|
|
|
/// The start position of this source in the CodeMap
|
2014-03-27 22:39:48 +00:00
|
|
|
pub start_pos: BytePos,
|
2015-02-11 17:29:49 +00:00
|
|
|
/// The end position of this source in the CodeMap
|
|
|
|
pub end_pos: BytePos,
|
2012-11-16 23:14:11 +00:00
|
|
|
/// Locations of lines beginnings in the source code
|
2015-02-11 17:29:49 +00:00
|
|
|
pub lines: RefCell<Vec<BytePos>>,
|
2012-11-16 23:14:11 +00:00
|
|
|
/// Locations of multi-byte characters in the source code
|
2015-02-11 17:29:49 +00:00
|
|
|
pub multibyte_chars: RefCell<Vec<MultiByteChar>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Encodable for FileMap {
|
|
|
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
|
|
|
s.emit_struct("FileMap", 5, |s| {
|
2016-03-23 03:01:37 +00:00
|
|
|
s.emit_struct_field("name", 0, |s| self.name.encode(s))?;
|
|
|
|
s.emit_struct_field("start_pos", 1, |s| self.start_pos.encode(s))?;
|
|
|
|
s.emit_struct_field("end_pos", 2, |s| self.end_pos.encode(s))?;
|
|
|
|
s.emit_struct_field("lines", 3, |s| {
|
2016-03-22 22:58:45 +00:00
|
|
|
let lines = self.lines.borrow();
|
|
|
|
// store the length
|
|
|
|
s.emit_u32(lines.len() as u32)?;
|
|
|
|
|
|
|
|
if !lines.is_empty() {
|
|
|
|
// In order to preserve some space, we exploit the fact that
|
|
|
|
// the lines list is sorted and individual lines are
|
|
|
|
// probably not that long. Because of that we can store lines
|
|
|
|
// as a difference list, using as little space as possible
|
|
|
|
// for the differences.
|
|
|
|
let max_line_length = if lines.len() == 1 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
lines.windows(2)
|
|
|
|
.map(|w| w[1] - w[0])
|
|
|
|
.map(|bp| bp.to_usize())
|
|
|
|
.max()
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
let bytes_per_diff: u8 = match max_line_length {
|
|
|
|
0 ... 0xFF => 1,
|
|
|
|
0x100 ... 0xFFFF => 2,
|
|
|
|
_ => 4
|
|
|
|
};
|
|
|
|
|
|
|
|
// Encode the number of bytes used per diff.
|
|
|
|
bytes_per_diff.encode(s)?;
|
|
|
|
|
|
|
|
// Encode the first element.
|
|
|
|
lines[0].encode(s)?;
|
|
|
|
|
|
|
|
let diff_iter = (&lines[..]).windows(2)
|
|
|
|
.map(|w| (w[1] - w[0]));
|
|
|
|
|
|
|
|
match bytes_per_diff {
|
|
|
|
1 => for diff in diff_iter { (diff.0 as u8).encode(s)? },
|
|
|
|
2 => for diff in diff_iter { (diff.0 as u16).encode(s)? },
|
|
|
|
4 => for diff in diff_iter { diff.0.encode(s)? },
|
|
|
|
_ => unreachable!()
|
2015-02-11 17:29:49 +00:00
|
|
|
}
|
2016-03-22 22:58:45 +00:00
|
|
|
}
|
2015-02-11 17:29:49 +00:00
|
|
|
|
2016-03-22 22:58:45 +00:00
|
|
|
Ok(())
|
|
|
|
})?;
|
2015-02-11 17:29:49 +00:00
|
|
|
s.emit_struct_field("multibyte_chars", 4, |s| {
|
|
|
|
(*self.multibyte_chars.borrow()).encode(s)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Decodable for FileMap {
|
|
|
|
fn decode<D: Decoder>(d: &mut D) -> Result<FileMap, D::Error> {
|
|
|
|
|
|
|
|
d.read_struct("FileMap", 5, |d| {
|
2016-03-23 03:01:37 +00:00
|
|
|
let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?;
|
|
|
|
let start_pos: BytePos = d.read_struct_field("start_pos", 1, |d| Decodable::decode(d))?;
|
|
|
|
let end_pos: BytePos = d.read_struct_field("end_pos", 2, |d| Decodable::decode(d))?;
|
|
|
|
let lines: Vec<BytePos> = d.read_struct_field("lines", 3, |d| {
|
2016-03-22 22:58:45 +00:00
|
|
|
let num_lines: u32 = Decodable::decode(d)?;
|
|
|
|
let mut lines = Vec::with_capacity(num_lines as usize);
|
|
|
|
|
|
|
|
if num_lines > 0 {
|
|
|
|
// Read the number of bytes used per diff.
|
|
|
|
let bytes_per_diff: u8 = Decodable::decode(d)?;
|
|
|
|
|
|
|
|
// Read the first element.
|
|
|
|
let mut line_start: BytePos = Decodable::decode(d)?;
|
|
|
|
lines.push(line_start);
|
|
|
|
|
|
|
|
for _ in 1..num_lines {
|
|
|
|
let diff = match bytes_per_diff {
|
|
|
|
1 => d.read_u8()? as u32,
|
|
|
|
2 => d.read_u16()? as u32,
|
|
|
|
4 => d.read_u32()?,
|
|
|
|
_ => unreachable!()
|
|
|
|
};
|
2015-02-11 17:29:49 +00:00
|
|
|
|
2016-03-22 22:58:45 +00:00
|
|
|
line_start = line_start + BytePos(diff);
|
2015-02-11 17:29:49 +00:00
|
|
|
|
|
|
|
lines.push(line_start);
|
|
|
|
}
|
2016-03-22 22:58:45 +00:00
|
|
|
}
|
2015-02-11 17:29:49 +00:00
|
|
|
|
2016-03-22 22:58:45 +00:00
|
|
|
Ok(lines)
|
|
|
|
})?;
|
2016-03-21 08:38:25 +00:00
|
|
|
let multibyte_chars: Vec<MultiByteChar> =
|
|
|
|
d.read_struct_field("multibyte_chars", 4, |d| Decodable::decode(d))?;
|
2015-02-11 17:29:49 +00:00
|
|
|
Ok(FileMap {
|
|
|
|
name: name,
|
|
|
|
start_pos: start_pos,
|
|
|
|
end_pos: end_pos,
|
|
|
|
src: None,
|
|
|
|
lines: RefCell::new(lines),
|
|
|
|
multibyte_chars: RefCell::new(multibyte_chars)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2012-11-12 23:12:20 +00:00
|
|
|
}
|
|
|
|
|
2015-04-09 06:25:48 +00:00
|
|
|
impl fmt::Debug for FileMap {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(fmt, "FileMap({})", self.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-31 22:17:22 +00:00
|
|
|
impl FileMap {
|
2014-06-09 20:12:30 +00:00
|
|
|
/// EFFECT: register a start-of-line offset in the
|
|
|
|
/// table of line-beginnings.
|
|
|
|
/// UNCHECKED INVARIANT: these offsets must be added in the right
|
|
|
|
/// order and must be in the right places; there is shared knowledge
|
|
|
|
/// about what ends a line between this file and parse.rs
|
|
|
|
/// WARNING: pos param here is the offset relative to start of CodeMap,
|
|
|
|
/// and CodeMap will append a newline when adding a filemap without a newline at the end,
|
|
|
|
/// so the safe way to call this is with value calculated as
|
|
|
|
/// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap.
|
2013-05-31 22:17:22 +00:00
|
|
|
pub fn next_line(&self, pos: BytePos) {
|
2013-01-30 17:56:33 +00:00
|
|
|
// the new charpos must be > the last one (or it's the first one).
|
2014-10-15 06:05:01 +00:00
|
|
|
let mut lines = self.lines.borrow_mut();
|
2014-03-20 22:05:37 +00:00
|
|
|
let line_len = lines.len();
|
2014-11-14 17:18:10 +00:00
|
|
|
assert!(line_len == 0 || ((*lines)[line_len - 1] < pos));
|
2014-03-20 22:05:37 +00:00
|
|
|
lines.push(pos);
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2011-07-05 09:48:19 +00:00
|
|
|
|
2015-04-09 18:46:03 +00:00
|
|
|
/// get a line from the list of pre-computed line-beginnings.
|
|
|
|
/// line-number here is 0-based.
|
|
|
|
pub fn get_line(&self, line_number: usize) -> Option<&str> {
|
2015-02-11 17:29:49 +00:00
|
|
|
match self.src {
|
|
|
|
Some(ref src) => {
|
|
|
|
let lines = self.lines.borrow();
|
|
|
|
lines.get(line_number).map(|&line| {
|
|
|
|
let begin: BytePos = line - self.start_pos;
|
|
|
|
let begin = begin.to_usize();
|
2015-07-02 03:37:52 +00:00
|
|
|
// We can't use `lines.get(line_number+1)` because we might
|
|
|
|
// be parsing when we call this function and thus the current
|
|
|
|
// line is the last one we have line info for.
|
2015-02-11 17:29:49 +00:00
|
|
|
let slice = &src[begin..];
|
|
|
|
match slice.find('\n') {
|
|
|
|
Some(e) => &slice[..e],
|
|
|
|
None => slice
|
2015-04-09 18:46:03 +00:00
|
|
|
}
|
2015-02-11 17:29:49 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
None => None
|
|
|
|
}
|
2012-11-13 00:56:39 +00:00
|
|
|
}
|
2011-07-16 06:01:10 +00:00
|
|
|
|
2015-01-17 23:33:05 +00:00
|
|
|
pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) {
|
2013-03-29 01:39:09 +00:00
|
|
|
assert!(bytes >=2 && bytes <= 4);
|
2012-11-16 03:37:29 +00:00
|
|
|
let mbc = MultiByteChar {
|
|
|
|
pos: pos,
|
|
|
|
bytes: bytes,
|
|
|
|
};
|
2014-03-20 22:05:37 +00:00
|
|
|
self.multibyte_chars.borrow_mut().push(mbc);
|
2012-11-16 03:37:29 +00:00
|
|
|
}
|
2013-11-28 03:06:35 +00:00
|
|
|
|
|
|
|
pub fn is_real_file(&self) -> bool {
|
2014-11-27 20:00:50 +00:00
|
|
|
!(self.name.starts_with("<") &&
|
|
|
|
self.name.ends_with(">"))
|
2013-11-28 03:06:35 +00:00
|
|
|
}
|
2015-02-11 17:29:49 +00:00
|
|
|
|
|
|
|
pub fn is_imported(&self) -> bool {
|
|
|
|
self.src.is_none()
|
|
|
|
}
|
2015-11-11 05:26:14 +00:00
|
|
|
|
|
|
|
fn count_lines(&self) -> usize {
|
|
|
|
self.lines.borrow().len()
|
|
|
|
}
|
2012-02-01 10:37:53 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 22:44:57 +00:00
|
|
|
/// An abstraction over the fs operations used by the Parser.
|
|
|
|
pub trait FileLoader {
|
|
|
|
/// Query the existence of a file.
|
|
|
|
fn file_exists(&self, path: &Path) -> bool;
|
|
|
|
|
|
|
|
/// Read the contents of an UTF-8 file into memory.
|
|
|
|
fn read_file(&self, path: &Path) -> io::Result<String>;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A FileLoader that uses std::fs to load real files.
|
|
|
|
pub struct RealFileLoader;
|
|
|
|
|
|
|
|
impl FileLoader for RealFileLoader {
|
|
|
|
fn file_exists(&self, path: &Path) -> bool {
|
|
|
|
fs::metadata(path).is_ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_file(&self, path: &Path) -> io::Result<String> {
|
|
|
|
let mut src = String::new();
|
2016-03-23 03:01:37 +00:00
|
|
|
fs::File::open(path)?.read_to_string(&mut src)?;
|
2015-05-13 22:44:57 +00:00
|
|
|
Ok(src)
|
|
|
|
}
|
|
|
|
}
|
2015-02-11 17:29:49 +00:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
|
|
|
// CodeMap
|
|
|
|
//
|
|
|
|
|
2012-11-13 02:24:56 +00:00
|
|
|
pub struct CodeMap {
|
2014-09-17 16:01:33 +00:00
|
|
|
pub files: RefCell<Vec<Rc<FileMap>>>,
|
2015-05-13 22:44:57 +00:00
|
|
|
expansions: RefCell<Vec<ExpnInfo>>,
|
|
|
|
file_loader: Box<FileLoader>
|
2011-07-05 09:48:19 +00:00
|
|
|
}
|
|
|
|
|
2013-05-31 22:17:22 +00:00
|
|
|
impl CodeMap {
|
2013-03-22 02:07:54 +00:00
|
|
|
pub fn new() -> CodeMap {
|
2012-11-13 02:24:56 +00:00
|
|
|
CodeMap {
|
2014-02-28 21:09:09 +00:00
|
|
|
files: RefCell::new(Vec::new()),
|
2014-09-17 16:01:33 +00:00
|
|
|
expansions: RefCell::new(Vec::new()),
|
2015-05-13 22:44:57 +00:00
|
|
|
file_loader: Box::new(RealFileLoader)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_file_loader(file_loader: Box<FileLoader>) -> CodeMap {
|
|
|
|
CodeMap {
|
|
|
|
files: RefCell::new(Vec::new()),
|
|
|
|
expansions: RefCell::new(Vec::new()),
|
|
|
|
file_loader: file_loader
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2012-01-21 09:00:06 +00:00
|
|
|
}
|
2012-11-13 02:24:56 +00:00
|
|
|
|
2015-05-13 22:44:57 +00:00
|
|
|
pub fn file_exists(&self, path: &Path) -> bool {
|
|
|
|
self.file_loader.file_exists(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_file(&self, path: &Path) -> io::Result<Rc<FileMap>> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let src = self.file_loader.read_file(path)?;
|
2015-05-13 22:44:57 +00:00
|
|
|
Ok(self.new_filemap(path.to_str().unwrap().to_string(), src))
|
|
|
|
}
|
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
fn next_start_pos(&self) -> usize {
|
|
|
|
let files = self.files.borrow();
|
|
|
|
match files.last() {
|
|
|
|
None => 0,
|
|
|
|
// Add one so there is some space between files. This lets us distinguish
|
|
|
|
// positions in the codemap, even in the presence of zero-length files.
|
|
|
|
Some(last) => last.end_pos.to_usize() + 1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new filemap without setting its line information. If you don't
|
|
|
|
/// intend to set the line information yourself, you should use new_filemap_and_lines.
|
2015-05-01 15:08:39 +00:00
|
|
|
pub fn new_filemap(&self, filename: FileName, mut src: String) -> Rc<FileMap> {
|
2015-07-02 03:37:52 +00:00
|
|
|
let start_pos = self.next_start_pos();
|
2013-12-31 00:30:33 +00:00
|
|
|
let mut files = self.files.borrow_mut();
|
2012-11-16 22:22:09 +00:00
|
|
|
|
2014-03-18 00:59:44 +00:00
|
|
|
// Remove utf-8 BOM if any.
|
2015-05-01 15:08:39 +00:00
|
|
|
if src.starts_with("\u{feff}") {
|
|
|
|
src.drain(..3);
|
|
|
|
}
|
2014-03-18 00:59:44 +00:00
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
let end_pos = start_pos + src.len();
|
|
|
|
|
2014-03-16 18:56:24 +00:00
|
|
|
let filemap = Rc::new(FileMap {
|
2014-01-15 18:58:29 +00:00
|
|
|
name: filename,
|
2015-02-11 17:29:49 +00:00
|
|
|
src: Some(Rc::new(src)),
|
2015-01-17 23:49:08 +00:00
|
|
|
start_pos: Pos::from_usize(start_pos),
|
2015-02-11 17:29:49 +00:00
|
|
|
end_pos: Pos::from_usize(end_pos),
|
2014-02-28 21:09:09 +00:00
|
|
|
lines: RefCell::new(Vec::new()),
|
|
|
|
multibyte_chars: RefCell::new(Vec::new()),
|
2014-03-16 18:56:24 +00:00
|
|
|
});
|
2012-11-16 22:22:09 +00:00
|
|
|
|
2014-03-20 22:05:37 +00:00
|
|
|
files.push(filemap.clone());
|
2012-11-16 22:22:09 +00:00
|
|
|
|
2014-03-16 18:56:24 +00:00
|
|
|
filemap
|
2012-11-16 22:22:09 +00:00
|
|
|
}
|
|
|
|
|
2015-07-02 05:14:14 +00:00
|
|
|
/// Creates a new filemap and sets its line information.
|
|
|
|
pub fn new_filemap_and_lines(&self, filename: &str, src: &str) -> Rc<FileMap> {
|
|
|
|
let fm = self.new_filemap(filename.to_string(), src.to_owned());
|
2016-04-20 18:56:01 +00:00
|
|
|
let mut byte_pos: u32 = fm.start_pos.0;
|
2015-07-02 05:14:14 +00:00
|
|
|
for line in src.lines() {
|
|
|
|
// register the start of this line
|
|
|
|
fm.next_line(BytePos(byte_pos));
|
|
|
|
|
|
|
|
// update byte_pos to include this line and the \n at the end
|
|
|
|
byte_pos += line.len() as u32 + 1;
|
|
|
|
}
|
|
|
|
fm
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
/// Allocates a new FileMap representing a source file from an external
|
|
|
|
/// crate. The source code of such an "imported filemap" is not available,
|
|
|
|
/// but we still know enough to generate accurate debuginfo location
|
|
|
|
/// information for things inlined from other crates.
|
|
|
|
pub fn new_imported_filemap(&self,
|
|
|
|
filename: FileName,
|
|
|
|
source_len: usize,
|
2015-04-17 04:38:24 +00:00
|
|
|
mut file_local_lines: Vec<BytePos>,
|
|
|
|
mut file_local_multibyte_chars: Vec<MultiByteChar>)
|
2015-02-11 17:29:49 +00:00
|
|
|
-> Rc<FileMap> {
|
2015-07-02 03:37:52 +00:00
|
|
|
let start_pos = self.next_start_pos();
|
2015-02-11 17:29:49 +00:00
|
|
|
let mut files = self.files.borrow_mut();
|
|
|
|
|
|
|
|
let end_pos = Pos::from_usize(start_pos + source_len);
|
|
|
|
let start_pos = Pos::from_usize(start_pos);
|
|
|
|
|
2015-04-17 04:38:24 +00:00
|
|
|
for pos in &mut file_local_lines {
|
|
|
|
*pos = *pos + start_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
for mbc in &mut file_local_multibyte_chars {
|
|
|
|
mbc.pos = mbc.pos + start_pos;
|
|
|
|
}
|
2015-02-11 17:29:49 +00:00
|
|
|
|
|
|
|
let filemap = Rc::new(FileMap {
|
|
|
|
name: filename,
|
|
|
|
src: None,
|
|
|
|
start_pos: start_pos,
|
|
|
|
end_pos: end_pos,
|
2015-04-17 04:38:24 +00:00
|
|
|
lines: RefCell::new(file_local_lines),
|
|
|
|
multibyte_chars: RefCell::new(file_local_multibyte_chars),
|
2015-02-11 17:29:49 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
files.push(filemap.clone());
|
|
|
|
|
|
|
|
filemap
|
|
|
|
}
|
|
|
|
|
2014-05-22 23:57:53 +00:00
|
|
|
pub fn mk_substr_filename(&self, sp: Span) -> String {
|
2012-11-13 02:24:56 +00:00
|
|
|
let pos = self.lookup_char_pos(sp.lo);
|
2014-05-07 23:33:43 +00:00
|
|
|
(format!("<{}:{}:{}>",
|
|
|
|
pos.file.name,
|
|
|
|
pos.line,
|
2015-01-17 23:49:08 +00:00
|
|
|
pos.col.to_usize() + 1)).to_string()
|
2011-07-05 09:48:19 +00:00
|
|
|
}
|
2012-01-25 21:22:10 +00:00
|
|
|
|
2012-11-16 23:14:11 +00:00
|
|
|
/// Lookup source information about a BytePos
|
2013-04-17 16:15:08 +00:00
|
|
|
pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
|
2015-06-16 18:47:09 +00:00
|
|
|
let chpos = self.bytepos_to_file_charpos(pos);
|
2015-07-02 03:37:52 +00:00
|
|
|
match self.lookup_line(pos) {
|
|
|
|
Ok(FileMapAndLine { fm: f, line: a }) => {
|
|
|
|
let line = a + 1; // Line numbers start at 1
|
|
|
|
let linebpos = (*f.lines.borrow())[a];
|
|
|
|
let linechpos = self.bytepos_to_file_charpos(linebpos);
|
|
|
|
debug!("byte pos {:?} is on the line at byte pos {:?}",
|
|
|
|
pos, linebpos);
|
|
|
|
debug!("char pos {:?} is on the line at char pos {:?}",
|
|
|
|
chpos, linechpos);
|
|
|
|
debug!("byte is on line: {}", line);
|
|
|
|
assert!(chpos >= linechpos);
|
|
|
|
Loc {
|
|
|
|
file: f,
|
|
|
|
line: line,
|
|
|
|
col: chpos - linechpos,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(f) => {
|
|
|
|
Loc {
|
|
|
|
file: f,
|
|
|
|
line: 0,
|
|
|
|
col: chpos,
|
|
|
|
}
|
|
|
|
}
|
2015-06-16 18:47:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
// If the relevant filemap is empty, we don't return a line number.
|
|
|
|
fn lookup_line(&self, pos: BytePos) -> Result<FileMapAndLine, Rc<FileMap>> {
|
2015-06-16 18:47:09 +00:00
|
|
|
let idx = self.lookup_filemap_idx(pos);
|
|
|
|
|
|
|
|
let files = self.files.borrow();
|
|
|
|
let f = (*files)[idx].clone();
|
2015-07-02 03:37:52 +00:00
|
|
|
|
|
|
|
let len = f.lines.borrow().len();
|
|
|
|
if len == 0 {
|
|
|
|
return Err(f);
|
|
|
|
}
|
|
|
|
|
2015-06-16 18:47:09 +00:00
|
|
|
let mut a = 0;
|
|
|
|
{
|
|
|
|
let lines = f.lines.borrow();
|
|
|
|
let mut b = lines.len();
|
|
|
|
while b - a > 1 {
|
|
|
|
let m = (a + b) / 2;
|
2015-07-02 03:37:52 +00:00
|
|
|
if (*lines)[m] > pos {
|
|
|
|
b = m;
|
|
|
|
} else {
|
|
|
|
a = m;
|
|
|
|
}
|
2015-06-16 18:47:09 +00:00
|
|
|
}
|
2015-07-02 03:37:52 +00:00
|
|
|
assert!(a <= lines.len());
|
2015-06-16 18:47:09 +00:00
|
|
|
}
|
2015-07-02 03:37:52 +00:00
|
|
|
Ok(FileMapAndLine { fm: f, line: a })
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2011-07-16 06:01:10 +00:00
|
|
|
|
2013-05-31 22:17:22 +00:00
|
|
|
pub fn lookup_char_pos_adj(&self, pos: BytePos) -> LocWithOpt {
|
2012-11-13 02:24:56 +00:00
|
|
|
let loc = self.lookup_char_pos(pos);
|
2014-01-15 18:58:29 +00:00
|
|
|
LocWithOpt {
|
2014-05-25 10:17:19 +00:00
|
|
|
filename: loc.file.name.to_string(),
|
2014-01-15 18:58:29 +00:00
|
|
|
line: loc.line,
|
|
|
|
col: loc.col,
|
|
|
|
file: Some(loc.file)
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
|
|
|
}
|
2011-07-05 09:48:19 +00:00
|
|
|
|
2014-06-21 10:39:03 +00:00
|
|
|
pub fn span_to_string(&self, sp: Span) -> String {
|
2016-04-20 18:52:31 +00:00
|
|
|
if sp == COMMAND_LINE_SP {
|
|
|
|
return "<command line option>".to_string();
|
|
|
|
}
|
|
|
|
|
2016-01-29 06:33:14 +00:00
|
|
|
if self.files.borrow().is_empty() && sp.source_equal(&DUMMY_SP) {
|
2014-05-25 10:17:19 +00:00
|
|
|
return "no-location".to_string();
|
2012-12-05 23:13:24 +00:00
|
|
|
}
|
|
|
|
|
2012-11-13 02:24:56 +00:00
|
|
|
let lo = self.lookup_char_pos_adj(sp.lo);
|
|
|
|
let hi = self.lookup_char_pos_adj(sp.hi);
|
2014-05-07 23:33:43 +00:00
|
|
|
return (format!("{}:{}:{}: {}:{}",
|
|
|
|
lo.filename,
|
|
|
|
lo.line,
|
2015-01-17 23:49:08 +00:00
|
|
|
lo.col.to_usize() + 1,
|
2014-05-07 23:33:43 +00:00
|
|
|
hi.line,
|
2015-01-17 23:49:08 +00:00
|
|
|
hi.col.to_usize() + 1)).to_string()
|
2012-02-10 18:28:43 +00:00
|
|
|
}
|
|
|
|
|
2015-11-24 22:05:27 +00:00
|
|
|
// Returns true if two spans have the same callee
|
|
|
|
// (Assumes the same ExpnFormat implies same callee)
|
|
|
|
fn match_callees(&self, sp_a: &Span, sp_b: &Span) -> bool {
|
|
|
|
let fmt_a = self
|
|
|
|
.with_expn_info(sp_a.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.callee.format.clone()));
|
|
|
|
|
|
|
|
let fmt_b = self
|
|
|
|
.with_expn_info(sp_b.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.callee.format.clone()));
|
|
|
|
fmt_a == fmt_b
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a formatted string showing the expansion chain of a span
|
|
|
|
///
|
|
|
|
/// Spans are printed in the following format:
|
|
|
|
///
|
|
|
|
/// filename:start_line:col: end_line:col
|
|
|
|
/// snippet
|
|
|
|
/// Callee:
|
|
|
|
/// Callee span
|
|
|
|
/// Callsite:
|
|
|
|
/// Callsite span
|
|
|
|
///
|
|
|
|
/// Callees and callsites are printed recursively (if available, otherwise header
|
|
|
|
/// and span is omitted), expanding into their own callee/callsite spans.
|
|
|
|
/// Each layer of recursion has an increased indent, and snippets are truncated
|
|
|
|
/// to at most 50 characters. Finally, recursive calls to the same macro are squashed,
|
|
|
|
/// with '...' used to represent any number of recursive calls.
|
|
|
|
pub fn span_to_expanded_string(&self, sp: Span) -> String {
|
|
|
|
self.span_to_expanded_string_internal(sp, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn span_to_expanded_string_internal(&self, sp:Span, indent: &str) -> String {
|
|
|
|
let mut indent = indent.to_owned();
|
|
|
|
let mut output = "".to_owned();
|
|
|
|
let span_str = self.span_to_string(sp);
|
|
|
|
let mut span_snip = self.span_to_snippet(sp)
|
|
|
|
.unwrap_or("Snippet unavailable".to_owned());
|
2016-01-21 22:58:09 +00:00
|
|
|
|
|
|
|
// Truncate by code points - in worst case this will be more than 50 characters,
|
|
|
|
// but ensures at least 50 characters and respects byte boundaries.
|
|
|
|
let char_vec: Vec<(usize, char)> = span_snip.char_indices().collect();
|
|
|
|
if char_vec.len() > 50 {
|
|
|
|
span_snip.truncate(char_vec[49].0);
|
2015-11-24 22:05:27 +00:00
|
|
|
span_snip.push_str("...");
|
|
|
|
}
|
2016-01-21 22:58:09 +00:00
|
|
|
|
2015-11-24 22:05:27 +00:00
|
|
|
output.push_str(&format!("{}{}\n{}`{}`\n", indent, span_str, indent, span_snip));
|
|
|
|
|
|
|
|
if sp.expn_id == NO_EXPANSION || sp.expn_id == COMMAND_LINE_EXPN {
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut callee = self.with_expn_info(sp.expn_id,
|
|
|
|
|ei| ei.and_then(|ei| ei.callee.span.clone()));
|
|
|
|
let mut callsite = self.with_expn_info(sp.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.call_site.clone()));
|
|
|
|
|
|
|
|
indent.push_str(" ");
|
|
|
|
let mut is_recursive = false;
|
|
|
|
|
|
|
|
while callee.is_some() && self.match_callees(&sp, &callee.unwrap()) {
|
|
|
|
callee = self.with_expn_info(callee.unwrap().expn_id,
|
|
|
|
|ei| ei.and_then(|ei| ei.callee.span.clone()));
|
|
|
|
is_recursive = true;
|
|
|
|
}
|
|
|
|
if let Some(span) = callee {
|
|
|
|
output.push_str(&indent);
|
|
|
|
output.push_str("Callee:\n");
|
|
|
|
if is_recursive {
|
|
|
|
output.push_str(&indent);
|
|
|
|
output.push_str("...\n");
|
|
|
|
}
|
|
|
|
output.push_str(&(self.span_to_expanded_string_internal(span, &indent)));
|
|
|
|
}
|
|
|
|
|
|
|
|
is_recursive = false;
|
|
|
|
while callsite.is_some() && self.match_callees(&sp, &callsite.unwrap()) {
|
|
|
|
callsite = self.with_expn_info(callsite.unwrap().expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.call_site.clone()));
|
|
|
|
is_recursive = true;
|
|
|
|
}
|
|
|
|
if let Some(span) = callsite {
|
|
|
|
output.push_str(&indent);
|
|
|
|
output.push_str("Callsite:\n");
|
|
|
|
if is_recursive {
|
|
|
|
output.push_str(&indent);
|
|
|
|
output.push_str("...\n");
|
|
|
|
}
|
|
|
|
output.push_str(&(self.span_to_expanded_string_internal(span, &indent)));
|
|
|
|
}
|
|
|
|
output
|
|
|
|
}
|
|
|
|
|
2016-01-21 22:58:09 +00:00
|
|
|
/// Return the source span - this is either the supplied span, or the span for
|
|
|
|
/// the macro callsite that expanded to it.
|
|
|
|
pub fn source_callsite(&self, sp: Span) -> Span {
|
|
|
|
let mut span = sp;
|
2016-02-03 07:44:53 +00:00
|
|
|
// Special case - if a macro is parsed as an argument to another macro, the source
|
|
|
|
// callsite is the first callsite, which is also source-equivalent to the span.
|
|
|
|
let mut first = true;
|
2016-01-21 22:58:09 +00:00
|
|
|
while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN {
|
|
|
|
if let Some(callsite) = self.with_expn_info(span.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.call_site.clone())) {
|
2016-02-03 07:44:53 +00:00
|
|
|
if first && span.source_equal(&callsite) {
|
|
|
|
if self.lookup_char_pos(span.lo).file.is_real_file() {
|
|
|
|
return Span { expn_id: NO_EXPANSION, .. span };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
first = false;
|
2016-01-21 22:58:09 +00:00
|
|
|
span = callsite;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
span
|
|
|
|
}
|
|
|
|
|
2016-01-29 07:22:55 +00:00
|
|
|
/// Return the source callee.
|
|
|
|
///
|
|
|
|
/// Returns None if the supplied span has no expansion trace,
|
|
|
|
/// else returns the NameAndSpan for the macro definition
|
|
|
|
/// corresponding to the source callsite.
|
|
|
|
pub fn source_callee(&self, sp: Span) -> Option<NameAndSpan> {
|
|
|
|
let mut span = sp;
|
2016-02-03 07:44:53 +00:00
|
|
|
// Special case - if a macro is parsed as an argument to another macro, the source
|
|
|
|
// callsite is source-equivalent to the span, and the source callee is the first callee.
|
|
|
|
let mut first = true;
|
2016-01-29 07:22:55 +00:00
|
|
|
while let Some(callsite) = self.with_expn_info(span.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.call_site.clone())) {
|
2016-02-03 07:44:53 +00:00
|
|
|
if first && span.source_equal(&callsite) {
|
|
|
|
if self.lookup_char_pos(span.lo).file.is_real_file() {
|
|
|
|
return self.with_expn_info(span.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.callee.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
first = false;
|
2016-01-29 07:22:55 +00:00
|
|
|
if let Some(_) = self.with_expn_info(callsite.expn_id,
|
2016-02-03 07:44:53 +00:00
|
|
|
|ei| ei.map(|ei| ei.call_site.clone())) {
|
2016-01-29 07:22:55 +00:00
|
|
|
span = callsite;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return self.with_expn_info(span.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.callee.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2013-08-31 16:13:04 +00:00
|
|
|
pub fn span_to_filename(&self, sp: Span) -> FileName {
|
2014-05-25 10:17:19 +00:00
|
|
|
self.lookup_char_pos(sp.lo).file.name.to_string()
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2011-07-05 09:48:19 +00:00
|
|
|
|
2015-04-30 08:23:50 +00:00
|
|
|
pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
|
2016-04-20 18:52:31 +00:00
|
|
|
debug!("span_to_lines(sp={:?})", sp);
|
|
|
|
|
2015-04-30 08:23:50 +00:00
|
|
|
if sp.lo > sp.hi {
|
|
|
|
return Err(SpanLinesError::IllFormedSpan(sp));
|
|
|
|
}
|
|
|
|
|
2012-11-13 02:24:56 +00:00
|
|
|
let lo = self.lookup_char_pos(sp.lo);
|
2016-04-20 18:52:31 +00:00
|
|
|
debug!("span_to_lines: lo={:?}", lo);
|
2012-11-13 02:24:56 +00:00
|
|
|
let hi = self.lookup_char_pos(sp.hi);
|
2016-04-20 18:52:31 +00:00
|
|
|
debug!("span_to_lines: hi={:?}", hi);
|
2015-04-30 08:23:50 +00:00
|
|
|
|
|
|
|
if lo.file.start_pos != hi.file.start_pos {
|
|
|
|
return Err(SpanLinesError::DistinctSources(DistinctSources {
|
|
|
|
begin: (lo.file.name.clone(), lo.file.start_pos),
|
|
|
|
end: (hi.file.name.clone(), hi.file.start_pos),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
assert!(hi.line >= lo.line);
|
|
|
|
|
2015-04-09 18:46:03 +00:00
|
|
|
let mut lines = Vec::with_capacity(hi.line - lo.line + 1);
|
|
|
|
|
|
|
|
// The span starts partway through the first line,
|
|
|
|
// but after that it starts from offset 0.
|
|
|
|
let mut start_col = lo.col;
|
|
|
|
|
|
|
|
// For every line but the last, it extends from `start_col`
|
|
|
|
// and to the end of the line. Be careful because the line
|
|
|
|
// numbers in Loc are 1-based, so we subtract 1 to get 0-based
|
|
|
|
// lines.
|
|
|
|
for line_index in lo.line-1 .. hi.line-1 {
|
2016-04-20 18:56:01 +00:00
|
|
|
let line_len = lo.file.get_line(line_index)
|
|
|
|
.map(|s| s.chars().count())
|
|
|
|
.unwrap_or(0);
|
2015-04-09 18:46:03 +00:00
|
|
|
lines.push(LineInfo { line_index: line_index,
|
|
|
|
start_col: start_col,
|
|
|
|
end_col: CharPos::from_usize(line_len) });
|
|
|
|
start_col = CharPos::from_usize(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// For the last line, it extends from `start_col` to `hi.col`:
|
|
|
|
lines.push(LineInfo { line_index: hi.line - 1,
|
|
|
|
start_col: start_col,
|
|
|
|
end_col: hi.col });
|
|
|
|
|
2015-04-30 08:23:50 +00:00
|
|
|
Ok(FileLines {file: lo.file, lines: lines})
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2012-02-10 18:28:43 +00:00
|
|
|
|
2015-02-05 15:02:22 +00:00
|
|
|
pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
|
|
|
|
if sp.lo > sp.hi {
|
|
|
|
return Err(SpanSnippetError::IllFormedSpan(sp));
|
|
|
|
}
|
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
let local_begin = self.lookup_byte_offset(sp.lo);
|
|
|
|
let local_end = self.lookup_byte_offset(sp.hi);
|
2013-08-04 02:14:01 +00:00
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
if local_begin.fm.start_pos != local_end.fm.start_pos {
|
2015-02-05 15:02:22 +00:00
|
|
|
return Err(SpanSnippetError::DistinctSources(DistinctSources {
|
2015-02-11 17:29:49 +00:00
|
|
|
begin: (local_begin.fm.name.clone(),
|
|
|
|
local_begin.fm.start_pos),
|
|
|
|
end: (local_end.fm.name.clone(),
|
|
|
|
local_end.fm.start_pos)
|
2015-02-05 15:02:22 +00:00
|
|
|
}));
|
2013-08-04 02:14:01 +00:00
|
|
|
} else {
|
2015-02-11 17:29:49 +00:00
|
|
|
match local_begin.fm.src {
|
|
|
|
Some(ref src) => {
|
|
|
|
let start_index = local_begin.pos.to_usize();
|
|
|
|
let end_index = local_end.pos.to_usize();
|
|
|
|
let source_len = (local_begin.fm.end_pos -
|
|
|
|
local_begin.fm.start_pos).to_usize();
|
|
|
|
|
|
|
|
if start_index > end_index || end_index > source_len {
|
|
|
|
return Err(SpanSnippetError::MalformedForCodemap(
|
|
|
|
MalformedCodemapPositions {
|
|
|
|
name: local_begin.fm.name.clone(),
|
|
|
|
source_len: source_len,
|
|
|
|
begin_pos: local_begin.pos,
|
|
|
|
end_pos: local_end.pos,
|
|
|
|
}));
|
|
|
|
}
|
2015-02-05 15:02:22 +00:00
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
return Ok((&src[start_index..end_index]).to_string())
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
return Err(SpanSnippetError::SourceNotAvailable {
|
|
|
|
filename: local_begin.fm.name.clone()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2013-08-04 02:14:01 +00:00
|
|
|
}
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2012-05-11 00:18:04 +00:00
|
|
|
|
2014-03-16 18:56:24 +00:00
|
|
|
pub fn get_filemap(&self, filename: &str) -> Rc<FileMap> {
|
2015-06-11 12:56:07 +00:00
|
|
|
for fm in self.files.borrow().iter() {
|
2014-11-27 20:00:50 +00:00
|
|
|
if filename == fm.name {
|
2014-03-16 18:56:24 +00:00
|
|
|
return fm.clone();
|
2013-12-31 00:30:33 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-09 19:17:22 +00:00
|
|
|
panic!("asking for {} which we don't know about", filename);
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2012-01-25 21:22:10 +00:00
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
/// For a global BytePos compute the local offset within the containing FileMap
|
2014-02-05 04:31:33 +00:00
|
|
|
pub fn lookup_byte_offset(&self, bpos: BytePos) -> FileMapAndBytePos {
|
|
|
|
let idx = self.lookup_filemap_idx(bpos);
|
2014-10-15 06:05:01 +00:00
|
|
|
let fm = (*self.files.borrow())[idx].clone();
|
2014-02-05 04:31:33 +00:00
|
|
|
let offset = bpos - fm.start_pos;
|
|
|
|
FileMapAndBytePos {fm: fm, pos: offset}
|
|
|
|
}
|
|
|
|
|
2015-07-06 02:13:19 +00:00
|
|
|
/// Converts an absolute BytePos to a CharPos relative to the filemap.
|
2014-02-05 04:31:33 +00:00
|
|
|
pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos {
|
|
|
|
let idx = self.lookup_filemap_idx(bpos);
|
|
|
|
let files = self.files.borrow();
|
2014-10-15 06:05:01 +00:00
|
|
|
let map = &(*files)[idx];
|
2014-02-05 04:31:33 +00:00
|
|
|
|
|
|
|
// The number of extra bytes due to multibyte chars in the FileMap
|
|
|
|
let mut total_extra_bytes = 0;
|
|
|
|
|
2015-06-11 12:56:07 +00:00
|
|
|
for mbc in map.multibyte_chars.borrow().iter() {
|
2014-12-20 08:09:35 +00:00
|
|
|
debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos);
|
2014-02-05 04:31:33 +00:00
|
|
|
if mbc.pos < bpos {
|
|
|
|
// every character is at least one byte, so we only
|
|
|
|
// count the actual extra bytes.
|
|
|
|
total_extra_bytes += mbc.bytes - 1;
|
|
|
|
// We should never see a byte position in the middle of a
|
|
|
|
// character
|
2015-01-17 23:49:08 +00:00
|
|
|
assert!(bpos.to_usize() >= mbc.pos.to_usize() + mbc.bytes);
|
2014-02-05 04:31:33 +00:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-17 23:49:08 +00:00
|
|
|
assert!(map.start_pos.to_usize() + total_extra_bytes <= bpos.to_usize());
|
|
|
|
CharPos(bpos.to_usize() - map.start_pos.to_usize() - total_extra_bytes)
|
2014-02-05 04:31:33 +00:00
|
|
|
}
|
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
// Return the index of the filemap (in self.files) which contains pos.
|
2015-01-17 23:33:05 +00:00
|
|
|
fn lookup_filemap_idx(&self, pos: BytePos) -> usize {
|
2013-12-31 00:30:33 +00:00
|
|
|
let files = self.files.borrow();
|
2014-10-15 06:05:01 +00:00
|
|
|
let files = &*files;
|
2015-07-02 03:37:52 +00:00
|
|
|
let count = files.len();
|
|
|
|
|
|
|
|
// Binary search for the filemap.
|
2015-01-28 01:01:48 +00:00
|
|
|
let mut a = 0;
|
2015-07-02 03:37:52 +00:00
|
|
|
let mut b = count;
|
2015-01-28 01:01:48 +00:00
|
|
|
while b - a > 1 {
|
|
|
|
let m = (a + b) / 2;
|
2014-10-15 06:05:01 +00:00
|
|
|
if files[m].start_pos > pos {
|
2012-11-13 03:32:48 +00:00
|
|
|
b = m;
|
|
|
|
} else {
|
|
|
|
a = m;
|
|
|
|
}
|
2012-11-13 02:24:56 +00:00
|
|
|
}
|
2015-07-02 03:37:52 +00:00
|
|
|
|
|
|
|
assert!(a < count, "position {} does not resolve to a source location", pos.to_usize());
|
2012-11-16 03:37:29 +00:00
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2016-03-11 18:29:06 +00:00
|
|
|
/// Check if the backtrace `subtrace` contains `suptrace` as a prefix.
|
|
|
|
pub fn more_specific_trace(&self,
|
|
|
|
mut subtrace: ExpnId,
|
|
|
|
suptrace: ExpnId)
|
|
|
|
-> bool {
|
|
|
|
loop {
|
|
|
|
if subtrace == suptrace {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
let stop = self.with_expn_info(subtrace, |opt_expn_info| {
|
|
|
|
if let Some(expn_info) = opt_expn_info {
|
|
|
|
subtrace = expn_info.call_site.expn_id;
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if stop {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-17 16:01:33 +00:00
|
|
|
pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
|
|
|
|
let mut expansions = self.expansions.borrow_mut();
|
|
|
|
expansions.push(expn_info);
|
2015-04-17 22:32:42 +00:00
|
|
|
let len = expansions.len();
|
|
|
|
if len > u32::max_value() as usize {
|
|
|
|
panic!("too many ExpnInfo's!");
|
|
|
|
}
|
|
|
|
ExpnId(len as u32 - 1)
|
2014-09-17 16:01:33 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 18:28:32 +00:00
|
|
|
pub fn with_expn_info<T, F>(&self, id: ExpnId, f: F) -> T where
|
|
|
|
F: FnOnce(Option<&ExpnInfo>) -> T,
|
|
|
|
{
|
2014-09-17 16:01:33 +00:00
|
|
|
match id {
|
2015-04-06 01:07:11 +00:00
|
|
|
NO_EXPANSION | COMMAND_LINE_EXPN => f(None),
|
2015-01-17 23:33:05 +00:00
|
|
|
ExpnId(i) => f(Some(&(*self.expansions.borrow())[i as usize]))
|
2014-09-17 16:01:33 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-24 05:44:13 +00:00
|
|
|
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
/// Check if a span is "internal" to a macro in which #[unstable]
|
|
|
|
/// items can be used (that is, a macro marked with
|
|
|
|
/// `#[allow_internal_unstable]`).
|
|
|
|
pub fn span_allows_unstable(&self, span: Span) -> bool {
|
|
|
|
debug!("span_allows_unstable(span = {:?})", span);
|
|
|
|
let mut allows_unstable = false;
|
|
|
|
let mut expn_id = span.expn_id;
|
|
|
|
loop {
|
|
|
|
let quit = self.with_expn_info(expn_id, |expninfo| {
|
|
|
|
debug!("span_allows_unstable: expninfo = {:?}", expninfo);
|
|
|
|
expninfo.map_or(/* hit the top level */ true, |info| {
|
|
|
|
|
|
|
|
let span_comes_from_this_expansion =
|
2016-01-29 06:33:14 +00:00
|
|
|
info.callee.span.map_or(span.source_equal(&info.call_site), |mac_span| {
|
2015-09-26 01:44:37 +00:00
|
|
|
mac_span.contains(span)
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
});
|
|
|
|
|
2015-06-05 16:53:17 +00:00
|
|
|
debug!("span_allows_unstable: span: {:?} call_site: {:?} callee: {:?}",
|
|
|
|
(span.lo, span.hi),
|
|
|
|
(info.call_site.lo, info.call_site.hi),
|
|
|
|
info.callee.span.map(|x| (x.lo, x.hi)));
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
debug!("span_allows_unstable: from this expansion? {}, allows unstable? {}",
|
|
|
|
span_comes_from_this_expansion,
|
|
|
|
info.callee.allow_internal_unstable);
|
|
|
|
if span_comes_from_this_expansion {
|
|
|
|
allows_unstable = info.callee.allow_internal_unstable;
|
|
|
|
// we've found the right place, stop looking
|
|
|
|
true
|
2014-12-24 05:44:13 +00:00
|
|
|
} else {
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
// not the right place, keep looking
|
|
|
|
expn_id = info.call_site.expn_id;
|
|
|
|
false
|
2014-12-24 05:44:13 +00:00
|
|
|
}
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
})
|
|
|
|
});
|
|
|
|
if quit {
|
|
|
|
break
|
2014-12-24 05:44:13 +00:00
|
|
|
}
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
}
|
|
|
|
debug!("span_allows_unstable? {}", allows_unstable);
|
|
|
|
allows_unstable
|
2014-12-24 05:44:13 +00:00
|
|
|
}
|
2015-11-11 05:26:14 +00:00
|
|
|
|
|
|
|
pub fn count_lines(&self) -> usize {
|
|
|
|
self.files.borrow().iter().fold(0, |a, f| a + f.count_lines())
|
|
|
|
}
|
2016-04-16 01:23:50 +00:00
|
|
|
|
|
|
|
pub fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
|
|
|
|
let mut last_span = DUMMY_SP;
|
|
|
|
let mut span = span;
|
|
|
|
let mut result = vec![];
|
|
|
|
loop {
|
|
|
|
let span_name_span = self.with_expn_info(span.expn_id, |expn_info| {
|
|
|
|
expn_info.map(|ei| {
|
|
|
|
let (pre, post) = match ei.callee.format {
|
|
|
|
MacroAttribute(..) => ("#[", "]"),
|
|
|
|
MacroBang(..) => ("", "!"),
|
|
|
|
};
|
|
|
|
let macro_decl_name = format!("{}{}{}",
|
|
|
|
pre,
|
|
|
|
ei.callee.name(),
|
|
|
|
post);
|
|
|
|
let def_site_span = ei.callee.span;
|
|
|
|
(ei.call_site, macro_decl_name, def_site_span)
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
match span_name_span {
|
|
|
|
None => break,
|
|
|
|
Some((call_site, macro_decl_name, def_site_span)) => {
|
|
|
|
// Don't print recursive invocations
|
|
|
|
if !call_site.source_equal(&last_span) {
|
|
|
|
result.push(MacroBacktrace {
|
|
|
|
call_site: call_site,
|
|
|
|
macro_decl_name: macro_decl_name,
|
|
|
|
def_site_span: def_site_span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
last_span = span;
|
|
|
|
span = call_site;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct MacroBacktrace {
|
|
|
|
/// span where macro was applied to generate this code
|
|
|
|
pub call_site: Span,
|
|
|
|
|
|
|
|
/// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
|
|
|
|
pub macro_decl_name: String,
|
|
|
|
|
|
|
|
/// span where macro was defined (if known)
|
|
|
|
pub def_site_span: Option<Span>,
|
2011-07-11 06:27:03 +00:00
|
|
|
}
|
2012-01-26 09:52:08 +00:00
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
// _____________________________________________________________________________
|
2015-04-30 08:23:50 +00:00
|
|
|
// SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
|
2015-02-11 17:29:49 +00:00
|
|
|
//
|
|
|
|
|
2015-04-30 08:23:50 +00:00
|
|
|
pub type FileLinesResult = Result<FileLines, SpanLinesError>;
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub enum SpanLinesError {
|
|
|
|
IllFormedSpan(Span),
|
|
|
|
DistinctSources(DistinctSources),
|
|
|
|
}
|
|
|
|
|
2015-02-05 15:02:22 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub enum SpanSnippetError {
|
|
|
|
IllFormedSpan(Span),
|
|
|
|
DistinctSources(DistinctSources),
|
|
|
|
MalformedForCodemap(MalformedCodemapPositions),
|
2015-02-11 17:29:49 +00:00
|
|
|
SourceNotAvailable { filename: String }
|
2015-02-05 15:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct DistinctSources {
|
|
|
|
begin: (String, BytePos),
|
|
|
|
end: (String, BytePos)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct MalformedCodemapPositions {
|
|
|
|
name: String,
|
|
|
|
source_len: usize,
|
|
|
|
begin_pos: BytePos,
|
|
|
|
end_pos: BytePos
|
|
|
|
}
|
|
|
|
|
2015-02-11 17:29:49 +00:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Tests
|
|
|
|
//
|
|
|
|
|
2013-01-30 17:56:33 +00:00
|
|
|
#[cfg(test)]
|
2015-04-24 15:30:41 +00:00
|
|
|
mod tests {
|
2013-01-30 17:56:33 +00:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t1 () {
|
|
|
|
let cm = CodeMap::new();
|
2014-05-25 10:17:19 +00:00
|
|
|
let fm = cm.new_filemap("blork.rs".to_string(),
|
|
|
|
"first line.\nsecond line".to_string());
|
2013-01-30 17:56:33 +00:00
|
|
|
fm.next_line(BytePos(0));
|
2015-07-02 03:37:52 +00:00
|
|
|
// Test we can get lines with partial line info.
|
2015-04-09 18:46:03 +00:00
|
|
|
assert_eq!(fm.get_line(0), Some("first line."));
|
2015-07-02 03:37:52 +00:00
|
|
|
// TESTING BROKEN BEHAVIOR: line break declared before actual line break.
|
2013-01-30 17:56:33 +00:00
|
|
|
fm.next_line(BytePos(10));
|
2015-04-09 18:46:03 +00:00
|
|
|
assert_eq!(fm.get_line(1), Some("."));
|
2015-07-02 03:37:52 +00:00
|
|
|
fm.next_line(BytePos(12));
|
|
|
|
assert_eq!(fm.get_line(2), Some("second line"));
|
2013-01-30 17:56:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2015-01-31 23:08:25 +00:00
|
|
|
#[should_panic]
|
2013-01-30 17:56:33 +00:00
|
|
|
fn t2 () {
|
|
|
|
let cm = CodeMap::new();
|
2014-05-25 10:17:19 +00:00
|
|
|
let fm = cm.new_filemap("blork.rs".to_string(),
|
|
|
|
"first line.\nsecond line".to_string());
|
2013-01-30 17:56:33 +00:00
|
|
|
// TESTING *REALLY* BROKEN BEHAVIOR:
|
|
|
|
fm.next_line(BytePos(0));
|
|
|
|
fm.next_line(BytePos(10));
|
|
|
|
fm.next_line(BytePos(2));
|
|
|
|
}
|
2014-02-19 01:24:07 +00:00
|
|
|
|
2014-02-27 23:53:36 +00:00
|
|
|
fn init_code_map() -> CodeMap {
|
2014-02-19 01:24:07 +00:00
|
|
|
let cm = CodeMap::new();
|
2014-05-25 10:17:19 +00:00
|
|
|
let fm1 = cm.new_filemap("blork.rs".to_string(),
|
|
|
|
"first line.\nsecond line".to_string());
|
|
|
|
let fm2 = cm.new_filemap("empty.rs".to_string(),
|
|
|
|
"".to_string());
|
|
|
|
let fm3 = cm.new_filemap("blork2.rs".to_string(),
|
|
|
|
"first line.\nsecond line".to_string());
|
2014-02-19 01:24:07 +00:00
|
|
|
|
|
|
|
fm1.next_line(BytePos(0));
|
|
|
|
fm1.next_line(BytePos(12));
|
2015-07-02 03:37:52 +00:00
|
|
|
fm2.next_line(fm2.start_pos);
|
|
|
|
fm3.next_line(fm3.start_pos);
|
|
|
|
fm3.next_line(fm3.start_pos + BytePos(12));
|
2014-02-19 01:24:07 +00:00
|
|
|
|
|
|
|
cm
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t3() {
|
|
|
|
// Test lookup_byte_offset
|
|
|
|
let cm = init_code_map();
|
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
let fmabp1 = cm.lookup_byte_offset(BytePos(23));
|
2014-11-28 00:52:53 +00:00
|
|
|
assert_eq!(fmabp1.fm.name, "blork.rs");
|
2015-07-02 03:37:52 +00:00
|
|
|
assert_eq!(fmabp1.pos, BytePos(23));
|
|
|
|
|
|
|
|
let fmabp1 = cm.lookup_byte_offset(BytePos(24));
|
|
|
|
assert_eq!(fmabp1.fm.name, "empty.rs");
|
|
|
|
assert_eq!(fmabp1.pos, BytePos(0));
|
2014-02-19 01:24:07 +00:00
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
let fmabp2 = cm.lookup_byte_offset(BytePos(25));
|
2014-11-28 00:52:53 +00:00
|
|
|
assert_eq!(fmabp2.fm.name, "blork2.rs");
|
2014-02-19 01:24:07 +00:00
|
|
|
assert_eq!(fmabp2.pos, BytePos(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t4() {
|
2014-02-27 23:53:36 +00:00
|
|
|
// Test bytepos_to_file_charpos
|
2014-02-19 01:24:07 +00:00
|
|
|
let cm = init_code_map();
|
|
|
|
|
2014-02-27 23:53:36 +00:00
|
|
|
let cp1 = cm.bytepos_to_file_charpos(BytePos(22));
|
2014-02-19 01:24:07 +00:00
|
|
|
assert_eq!(cp1, CharPos(22));
|
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
let cp2 = cm.bytepos_to_file_charpos(BytePos(25));
|
2014-02-27 23:53:36 +00:00
|
|
|
assert_eq!(cp2, CharPos(0));
|
2014-02-19 01:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t5() {
|
|
|
|
// Test zero-length filemaps.
|
|
|
|
let cm = init_code_map();
|
|
|
|
|
|
|
|
let loc1 = cm.lookup_char_pos(BytePos(22));
|
2014-11-28 00:52:53 +00:00
|
|
|
assert_eq!(loc1.file.name, "blork.rs");
|
2014-02-19 01:24:07 +00:00
|
|
|
assert_eq!(loc1.line, 2);
|
|
|
|
assert_eq!(loc1.col, CharPos(10));
|
|
|
|
|
2015-07-02 03:37:52 +00:00
|
|
|
let loc2 = cm.lookup_char_pos(BytePos(25));
|
2014-11-28 00:52:53 +00:00
|
|
|
assert_eq!(loc2.file.name, "blork2.rs");
|
2014-02-19 01:24:07 +00:00
|
|
|
assert_eq!(loc2.line, 1);
|
|
|
|
assert_eq!(loc2.col, CharPos(0));
|
|
|
|
}
|
2014-02-27 23:53:36 +00:00
|
|
|
|
|
|
|
fn init_code_map_mbc() -> CodeMap {
|
|
|
|
let cm = CodeMap::new();
|
|
|
|
// € is a three byte utf8 char.
|
2014-05-07 23:33:43 +00:00
|
|
|
let fm1 =
|
2014-05-25 10:17:19 +00:00
|
|
|
cm.new_filemap("blork.rs".to_string(),
|
|
|
|
"fir€st €€€€ line.\nsecond line".to_string());
|
|
|
|
let fm2 = cm.new_filemap("blork2.rs".to_string(),
|
|
|
|
"first line€€.\n€ second line".to_string());
|
2014-02-27 23:53:36 +00:00
|
|
|
|
|
|
|
fm1.next_line(BytePos(0));
|
2015-07-02 03:37:52 +00:00
|
|
|
fm1.next_line(BytePos(28));
|
|
|
|
fm2.next_line(fm2.start_pos);
|
|
|
|
fm2.next_line(fm2.start_pos + BytePos(20));
|
2014-02-27 23:53:36 +00:00
|
|
|
|
|
|
|
fm1.record_multibyte_char(BytePos(3), 3);
|
|
|
|
fm1.record_multibyte_char(BytePos(9), 3);
|
|
|
|
fm1.record_multibyte_char(BytePos(12), 3);
|
|
|
|
fm1.record_multibyte_char(BytePos(15), 3);
|
|
|
|
fm1.record_multibyte_char(BytePos(18), 3);
|
2015-07-02 03:37:52 +00:00
|
|
|
fm2.record_multibyte_char(fm2.start_pos + BytePos(10), 3);
|
|
|
|
fm2.record_multibyte_char(fm2.start_pos + BytePos(13), 3);
|
|
|
|
fm2.record_multibyte_char(fm2.start_pos + BytePos(18), 3);
|
2014-02-27 23:53:36 +00:00
|
|
|
|
|
|
|
cm
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t6() {
|
|
|
|
// Test bytepos_to_file_charpos in the presence of multi-byte chars
|
|
|
|
let cm = init_code_map_mbc();
|
|
|
|
|
|
|
|
let cp1 = cm.bytepos_to_file_charpos(BytePos(3));
|
|
|
|
assert_eq!(cp1, CharPos(3));
|
|
|
|
|
|
|
|
let cp2 = cm.bytepos_to_file_charpos(BytePos(6));
|
|
|
|
assert_eq!(cp2, CharPos(4));
|
|
|
|
|
2014-03-03 10:44:43 +00:00
|
|
|
let cp3 = cm.bytepos_to_file_charpos(BytePos(56));
|
2014-02-27 23:53:36 +00:00
|
|
|
assert_eq!(cp3, CharPos(12));
|
|
|
|
|
2014-03-03 10:44:43 +00:00
|
|
|
let cp4 = cm.bytepos_to_file_charpos(BytePos(61));
|
2014-02-27 23:53:36 +00:00
|
|
|
assert_eq!(cp4, CharPos(15));
|
|
|
|
}
|
2014-03-03 10:44:43 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t7() {
|
|
|
|
// Test span_to_lines for a span ending at the end of filemap
|
|
|
|
let cm = init_code_map();
|
2014-09-18 11:36:01 +00:00
|
|
|
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
|
2015-04-30 08:23:50 +00:00
|
|
|
let file_lines = cm.span_to_lines(span).unwrap();
|
2014-03-03 10:44:43 +00:00
|
|
|
|
2014-11-28 00:52:53 +00:00
|
|
|
assert_eq!(file_lines.file.name, "blork.rs");
|
2014-03-03 10:44:43 +00:00
|
|
|
assert_eq!(file_lines.lines.len(), 1);
|
2015-04-09 18:46:03 +00:00
|
|
|
assert_eq!(file_lines.lines[0].line_index, 1);
|
|
|
|
}
|
|
|
|
|
2016-04-20 18:56:01 +00:00
|
|
|
/// Given a string like " ~~~~~~~~~~~~ ", produces a span
|
2015-04-09 18:46:03 +00:00
|
|
|
/// coverting that range. The idea is that the string has the same
|
|
|
|
/// length as the input, and we uncover the byte positions. Note
|
|
|
|
/// that this can span lines and so on.
|
|
|
|
fn span_from_selection(input: &str, selection: &str) -> Span {
|
|
|
|
assert_eq!(input.len(), selection.len());
|
2016-04-20 18:56:01 +00:00
|
|
|
let left_index = selection.find('~').unwrap() as u32;
|
2015-12-13 12:12:47 +00:00
|
|
|
let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
|
2015-04-09 18:46:03 +00:00
|
|
|
Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test span_to_snippet and span_to_lines for a span coverting 3
|
|
|
|
/// lines in the middle of a file.
|
|
|
|
#[test]
|
|
|
|
fn span_to_snippet_and_lines_spanning_multiple_lines() {
|
|
|
|
let cm = CodeMap::new();
|
|
|
|
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
|
2016-04-20 18:56:01 +00:00
|
|
|
let selection = " \n ~~\n~~~\n~~~~~ \n \n";
|
2015-07-02 05:14:14 +00:00
|
|
|
cm.new_filemap_and_lines("blork.rs", inputtext);
|
2015-04-09 18:46:03 +00:00
|
|
|
let span = span_from_selection(inputtext, selection);
|
|
|
|
|
|
|
|
// check that we are extracting the text we thought we were extracting
|
|
|
|
assert_eq!(&cm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD");
|
|
|
|
|
|
|
|
// check that span_to_lines gives us the complete result with the lines/cols we expected
|
2015-04-30 08:23:50 +00:00
|
|
|
let lines = cm.span_to_lines(span).unwrap();
|
2015-04-09 18:46:03 +00:00
|
|
|
let expected = vec![
|
|
|
|
LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) },
|
|
|
|
LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) },
|
|
|
|
LineInfo { line_index: 3, start_col: CharPos(0), end_col: CharPos(5) }
|
|
|
|
];
|
|
|
|
assert_eq!(lines.lines, expected);
|
2014-03-03 10:44:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t8() {
|
|
|
|
// Test span_to_snippet for a span ending at the end of filemap
|
|
|
|
let cm = init_code_map();
|
2014-09-18 11:36:01 +00:00
|
|
|
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
|
2014-03-03 10:44:43 +00:00
|
|
|
let snippet = cm.span_to_snippet(span);
|
|
|
|
|
2015-02-05 15:02:22 +00:00
|
|
|
assert_eq!(snippet, Ok("second line".to_string()));
|
2014-03-03 10:44:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t9() {
|
|
|
|
// Test span_to_str for a span ending at the end of filemap
|
|
|
|
let cm = init_code_map();
|
2014-09-18 11:36:01 +00:00
|
|
|
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
|
2014-06-21 10:39:03 +00:00
|
|
|
let sstr = cm.span_to_string(span);
|
2014-03-03 10:44:43 +00:00
|
|
|
|
2014-11-28 00:52:53 +00:00
|
|
|
assert_eq!(sstr, "blork.rs:2:1: 2:12");
|
2014-03-03 10:44:43 +00:00
|
|
|
}
|
2015-11-24 22:05:27 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t10() {
|
|
|
|
// Test span_to_expanded_string works in base case (no expansion)
|
|
|
|
let cm = init_code_map();
|
|
|
|
let span = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
|
|
|
|
let sstr = cm.span_to_expanded_string(span);
|
|
|
|
assert_eq!(sstr, "blork.rs:1:1: 1:12\n`first line.`\n");
|
|
|
|
|
|
|
|
let span = Span { lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION };
|
|
|
|
let sstr = cm.span_to_expanded_string(span);
|
|
|
|
assert_eq!(sstr, "blork.rs:2:1: 2:12\n`second line`\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t11() {
|
|
|
|
// Test span_to_expanded_string works with expansion
|
|
|
|
use ast::Name;
|
|
|
|
let cm = init_code_map();
|
|
|
|
let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
|
|
|
|
let format = ExpnFormat::MacroBang(Name(0u32));
|
|
|
|
let callee = NameAndSpan { format: format,
|
|
|
|
allow_internal_unstable: false,
|
|
|
|
span: None };
|
|
|
|
|
|
|
|
let info = ExpnInfo { call_site: root, callee: callee };
|
|
|
|
let id = cm.record_expansion(info);
|
|
|
|
let sp = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id };
|
|
|
|
|
|
|
|
let sstr = cm.span_to_expanded_string(sp);
|
|
|
|
assert_eq!(sstr,
|
|
|
|
"blork.rs:2:1: 2:12\n`second line`\n Callsite:\n \
|
|
|
|
blork.rs:1:1: 1:12\n `first line.`\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init_expansion_chain(cm: &CodeMap) -> Span {
|
|
|
|
// Creates an expansion chain containing two recursive calls
|
|
|
|
// root -> expA -> expA -> expB -> expB -> end
|
|
|
|
use ast::Name;
|
|
|
|
|
|
|
|
let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
|
|
|
|
|
|
|
|
let format_root = ExpnFormat::MacroBang(Name(0u32));
|
|
|
|
let callee_root = NameAndSpan { format: format_root,
|
|
|
|
allow_internal_unstable: false,
|
|
|
|
span: Some(root) };
|
|
|
|
|
|
|
|
let info_a1 = ExpnInfo { call_site: root, callee: callee_root };
|
|
|
|
let id_a1 = cm.record_expansion(info_a1);
|
|
|
|
let span_a1 = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a1 };
|
|
|
|
|
|
|
|
let format_a = ExpnFormat::MacroBang(Name(1u32));
|
|
|
|
let callee_a = NameAndSpan { format: format_a,
|
|
|
|
allow_internal_unstable: false,
|
|
|
|
span: Some(span_a1) };
|
|
|
|
|
|
|
|
let info_a2 = ExpnInfo { call_site: span_a1, callee: callee_a.clone() };
|
|
|
|
let id_a2 = cm.record_expansion(info_a2);
|
|
|
|
let span_a2 = Span { lo: BytePos(12), hi: BytePos(23), expn_id: id_a2 };
|
|
|
|
|
|
|
|
let info_b1 = ExpnInfo { call_site: span_a2, callee: callee_a };
|
|
|
|
let id_b1 = cm.record_expansion(info_b1);
|
|
|
|
let span_b1 = Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b1 };
|
|
|
|
|
|
|
|
let format_b = ExpnFormat::MacroBang(Name(2u32));
|
|
|
|
let callee_b = NameAndSpan { format: format_b,
|
|
|
|
allow_internal_unstable: false,
|
|
|
|
span: None };
|
|
|
|
|
|
|
|
let info_b2 = ExpnInfo { call_site: span_b1, callee: callee_b.clone() };
|
|
|
|
let id_b2 = cm.record_expansion(info_b2);
|
|
|
|
let span_b2 = Span { lo: BytePos(25), hi: BytePos(36), expn_id: id_b2 };
|
|
|
|
|
|
|
|
let info_end = ExpnInfo { call_site: span_b2, callee: callee_b };
|
|
|
|
let id_end = cm.record_expansion(info_end);
|
|
|
|
Span { lo: BytePos(37), hi: BytePos(48), expn_id: id_end }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t12() {
|
|
|
|
// Test span_to_expanded_string collapses recursive macros and handles
|
|
|
|
// recursive callsite and callee expansions
|
|
|
|
let cm = init_code_map();
|
|
|
|
let end = init_expansion_chain(&cm);
|
|
|
|
let sstr = cm.span_to_expanded_string(end);
|
|
|
|
let res_str =
|
|
|
|
r"blork2.rs:2:1: 2:12
|
|
|
|
`second line`
|
|
|
|
Callsite:
|
|
|
|
...
|
|
|
|
blork2.rs:1:1: 1:12
|
|
|
|
`first line.`
|
|
|
|
Callee:
|
|
|
|
blork.rs:2:1: 2:12
|
|
|
|
`second line`
|
|
|
|
Callee:
|
|
|
|
blork.rs:1:1: 1:12
|
|
|
|
`first line.`
|
|
|
|
Callsite:
|
|
|
|
blork.rs:1:1: 1:12
|
|
|
|
`first line.`
|
|
|
|
Callsite:
|
|
|
|
...
|
|
|
|
blork.rs:2:1: 2:12
|
|
|
|
`second line`
|
|
|
|
Callee:
|
|
|
|
blork.rs:1:1: 1:12
|
|
|
|
`first line.`
|
|
|
|
Callsite:
|
|
|
|
blork.rs:1:1: 1:12
|
|
|
|
`first line.`
|
|
|
|
";
|
|
|
|
assert_eq!(sstr, res_str);
|
|
|
|
}
|
2013-01-30 17:56:33 +00:00
|
|
|
}
|