2012-12-03 18:48:01 -06: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-07-14 22:46:04 -05:00
|
|
|
//
|
|
|
|
// ignore-lexer-test FIXME #15679
|
2012-12-03 18:48:01 -06:00
|
|
|
|
2014-12-22 11:04:23 -06: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-12 18:45:24 -06:00
|
|
|
|
2014-11-06 02:05:53 -06:00
|
|
|
pub use self::MacroFormat::*;
|
|
|
|
|
2013-12-30 18:24:48 -06:00
|
|
|
use std::cell::RefCell;
|
2014-12-22 11:04:23 -06:00
|
|
|
use std::num::ToPrimitive;
|
|
|
|
use std::ops::{Add, Sub};
|
2014-03-16 13:56:24 -05:00
|
|
|
use std::rc::Rc;
|
2014-12-22 11:04:23 -06:00
|
|
|
|
2014-09-28 11:25:48 -05:00
|
|
|
use libc::c_uint;
|
2014-12-22 11:04:23 -06:00
|
|
|
use serialize::{Encodable, Decodable, Encoder, Decoder};
|
2012-05-15 15:40:18 -05:00
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Pos, BytePos, CharPos
|
|
|
|
//
|
|
|
|
|
2013-01-25 18:57:39 -06:00
|
|
|
pub trait Pos {
|
2015-01-17 17:49:08 -06:00
|
|
|
fn from_usize(n: usize) -> Self;
|
|
|
|
fn to_usize(&self) -> usize;
|
2012-11-12 21:32:48 -06:00
|
|
|
}
|
|
|
|
|
2013-11-19 11:15:49 -06:00
|
|
|
/// A byte offset. Keep this small (currently 32-bits), as AST contains
|
|
|
|
/// a lot of them.
|
2015-01-28 07:34:18 -06:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Debug)]
|
2014-03-31 21:01:01 -05:00
|
|
|
pub struct BytePos(pub u32);
|
2013-11-19 11:15:49 -06:00
|
|
|
|
2012-11-16 17:14:11 -06: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.
|
2015-01-28 07:34:18 -06:00
|
|
|
#[derive(Copy, PartialEq, Hash, PartialOrd, Debug)]
|
2015-01-17 17:33:05 -06:00
|
|
|
pub struct CharPos(pub usize);
|
2012-11-12 21:32:48 -06:00
|
|
|
|
2014-01-26 02:43:42 -06:00
|
|
|
// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
|
2012-11-16 17:14:11 -06:00
|
|
|
// have been unsuccessful
|
|
|
|
|
2013-02-26 19:12:00 -06:00
|
|
|
impl Pos for BytePos {
|
2015-01-17 17:49:08 -06: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-12 21:32:48 -06:00
|
|
|
}
|
|
|
|
|
2014-12-31 14:45:13 -06:00
|
|
|
impl Add for BytePos {
|
|
|
|
type Output = BytePos;
|
|
|
|
|
2014-12-01 13:59:55 -06:00
|
|
|
fn add(self, rhs: BytePos) -> BytePos {
|
2015-01-17 17:49:08 -06:00
|
|
|
BytePos((self.to_usize() + rhs.to_usize()) as u32)
|
2014-12-01 13:59:55 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-31 14:45:13 -06:00
|
|
|
impl Sub for BytePos {
|
|
|
|
type Output = BytePos;
|
|
|
|
|
2014-12-01 13:59:55 -06:00
|
|
|
fn sub(self, rhs: BytePos) -> BytePos {
|
2015-01-17 17:49:08 -06:00
|
|
|
BytePos((self.to_usize() - rhs.to_usize()) as u32)
|
2014-12-01 13:59:55 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06: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> {
|
|
|
|
Ok(BytePos(try!{ d.read_u32() }))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-26 19:12:00 -06:00
|
|
|
impl Pos for CharPos {
|
2015-01-17 17:49:08 -06:00
|
|
|
fn from_usize(n: usize) -> CharPos { CharPos(n) }
|
|
|
|
fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
|
2012-11-12 21:32:48 -06:00
|
|
|
}
|
|
|
|
|
2014-12-31 14:45:13 -06:00
|
|
|
impl Add for CharPos {
|
|
|
|
type Output = CharPos;
|
|
|
|
|
2014-12-01 13:59:55 -06:00
|
|
|
fn add(self, rhs: CharPos) -> CharPos {
|
2015-01-17 17:49:08 -06:00
|
|
|
CharPos(self.to_usize() + rhs.to_usize())
|
2014-12-01 13:59:55 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-31 14:45:13 -06:00
|
|
|
impl Sub for CharPos {
|
|
|
|
type Output = CharPos;
|
|
|
|
|
2014-12-01 13:59:55 -06:00
|
|
|
fn sub(self, rhs: CharPos) -> CharPos {
|
2015-01-17 17:49:08 -06:00
|
|
|
CharPos(self.to_usize() - rhs.to_usize())
|
2014-12-01 13:59:55 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Span, Spanned
|
|
|
|
//
|
|
|
|
|
2014-11-24 19:06:06 -06: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-01-28 07:34:18 -06:00
|
|
|
#[derive(Clone, Copy, Debug, Hash)]
|
2013-08-31 11:13:04 -05:00
|
|
|
pub struct Span {
|
2014-03-27 17:39:48 -05:00
|
|
|
pub lo: BytePos,
|
|
|
|
pub hi: BytePos,
|
2014-03-26 08:40:51 -05:00
|
|
|
/// Information about where the macro came from, if this piece of
|
|
|
|
/// code was created by a macro expansion.
|
2014-09-17 11:01:33 -05:00
|
|
|
pub expn_id: ExpnId
|
2012-11-12 19:19:56 -06:00
|
|
|
}
|
2012-11-12 19:14:15 -06:00
|
|
|
|
2014-10-06 18:33:44 -05:00
|
|
|
pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
|
2014-01-01 00:53:22 -06:00
|
|
|
|
2014-12-10 14:18:23 -06: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-01-28 07:34:18 -06:00
|
|
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
2013-08-31 11:13:04 -05:00
|
|
|
pub struct Spanned<T> {
|
2014-03-27 17:39:48 -05:00
|
|
|
pub node: T,
|
|
|
|
pub span: Span,
|
2013-07-02 14:47:32 -05:00
|
|
|
}
|
2013-01-30 11:56:33 -06:00
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
impl PartialEq for Span {
|
2013-08-31 11:13:04 -05:00
|
|
|
fn eq(&self, other: &Span) -> bool {
|
2012-11-14 20:59:30 -06:00
|
|
|
return (*self).lo == (*other).lo && (*self).hi == (*other).hi;
|
|
|
|
}
|
2013-08-31 11:13:04 -05:00
|
|
|
fn ne(&self, other: &Span) -> bool { !(*self).eq(other) }
|
2012-11-12 19:14:15 -06:00
|
|
|
}
|
|
|
|
|
2014-05-31 12:43:52 -05:00
|
|
|
impl Eq for Span {}
|
2014-03-22 15:30:45 -05:00
|
|
|
|
2015-01-04 00:24:50 -06:00
|
|
|
impl Encodable for Span {
|
|
|
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
2015-02-11 11:29:49 -06:00
|
|
|
// Encode spans as a single u64 in order to cut down on tagging overhead
|
|
|
|
// added by the RBML metadata encoding. The should be solved differently
|
|
|
|
// altogether some time (FIXME #21482)
|
|
|
|
s.emit_u64( (self.lo.0 as u64) | ((self.hi.0 as u64) << 32) )
|
2015-01-04 00:24:50 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Decodable for Span {
|
2015-02-11 11:29:49 -06:00
|
|
|
fn decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> {
|
|
|
|
let lo_hi: u64 = try! { d.read_u64() };
|
|
|
|
let lo = BytePos(lo_hi as u32);
|
|
|
|
let hi = BytePos((lo_hi >> 32) as u32);
|
|
|
|
Ok(mk_sp(lo, hi))
|
2015-01-04 00:24:50 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-31 11:13:04 -05:00
|
|
|
pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> {
|
2013-02-15 03:15:53 -06:00
|
|
|
respan(mk_sp(lo, hi), t)
|
2013-01-30 11:56:33 -06:00
|
|
|
}
|
|
|
|
|
2013-08-31 11:13:04 -05:00
|
|
|
pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
|
|
|
|
Spanned {node: t, span: sp}
|
2013-01-30 11:56:33 -06:00
|
|
|
}
|
|
|
|
|
2013-08-31 11:13:04 -05:00
|
|
|
pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
|
2014-01-01 00:53:22 -06:00
|
|
|
respan(DUMMY_SP, t)
|
2013-01-30 11:56:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* assuming that we're not in macro expansion */
|
2013-08-31 11:13:04 -05:00
|
|
|
pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
|
2014-09-17 11:01:33 -05:00
|
|
|
Span {lo: lo, hi: hi, expn_id: NO_EXPANSION}
|
2013-01-30 11:56:33 -06:00
|
|
|
}
|
|
|
|
|
2014-04-09 15:42:25 -05: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 11:01:33 -05: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 15:42:25 -05:00
|
|
|
(None, _) => sp,
|
2014-09-17 11:01:33 -05: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 15:42:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
|
|
|
|
//
|
|
|
|
|
2012-11-16 17:14:11 -06:00
|
|
|
/// A source code location used for error reporting
|
2012-11-15 21:37:29 -06:00
|
|
|
pub struct Loc {
|
2012-11-16 17:14:11 -06:00
|
|
|
/// Information about the original source
|
2014-03-27 17:39:48 -05:00
|
|
|
pub file: Rc<FileMap>,
|
2012-11-16 17:14:11 -06:00
|
|
|
/// The (1-based) line number
|
2015-01-17 17:33:05 -06:00
|
|
|
pub line: usize,
|
2012-11-16 17:14:11 -06:00
|
|
|
/// The (0-based) column offset
|
2014-03-27 17:39:48 -05:00
|
|
|
pub col: CharPos
|
2012-11-12 19:14:15 -06:00
|
|
|
}
|
|
|
|
|
2013-01-30 11:56:33 -06: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.
|
|
|
|
pub struct LocWithOpt {
|
2014-03-27 17:39:48 -05:00
|
|
|
pub filename: FileName,
|
2015-01-17 17:33:05 -06:00
|
|
|
pub line: usize,
|
2014-03-27 17:39:48 -05:00
|
|
|
pub col: CharPos,
|
|
|
|
pub file: Option<Rc<FileMap>>,
|
2013-01-30 11:56:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// used to be structural records. Better names, anyone?
|
2015-01-17 17:33:05 -06:00
|
|
|
pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
|
2014-03-27 17:39:48 -05:00
|
|
|
pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
|
2013-12-06 20:41:11 -06:00
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
|
|
|
// MacroFormat, NameAndSpan, ExpnInfo, ExpnId
|
|
|
|
//
|
|
|
|
|
2014-03-26 08:40:51 -05:00
|
|
|
/// The syntax with which a macro was invoked.
|
2015-01-28 07:34:18 -06:00
|
|
|
#[derive(Clone, Copy, Hash, Debug)]
|
2013-12-06 20:41:11 -06:00
|
|
|
pub enum MacroFormat {
|
2015-01-03 21:54:18 -06:00
|
|
|
/// e.g. #[derive(...)] <item>
|
2013-12-06 20:41:11 -06:00
|
|
|
MacroAttribute,
|
2014-03-26 08:40:51 -05:00
|
|
|
/// e.g. `format!()`
|
2013-12-06 20:41:11 -06:00
|
|
|
MacroBang
|
|
|
|
}
|
|
|
|
|
2015-01-28 07:34:18 -06:00
|
|
|
#[derive(Clone, Hash, Debug)]
|
2013-12-06 20:41:11 -06:00
|
|
|
pub struct NameAndSpan {
|
2014-03-26 08:40:51 -05:00
|
|
|
/// The name of the macro that was invoked to create the thing
|
|
|
|
/// with this Span.
|
2014-05-22 18:57:53 -05:00
|
|
|
pub name: String,
|
2014-03-26 08:40:51 -05:00
|
|
|
/// The format with which the macro was invoked.
|
2014-03-27 17:39:48 -05:00
|
|
|
pub format: MacroFormat,
|
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-02-28 21:09:28 -06: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 08:40:51 -05: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 17:39:48 -05:00
|
|
|
pub span: Option<Span>
|
2013-12-06 20:41:11 -06:00
|
|
|
}
|
2013-02-21 02:16:31 -06:00
|
|
|
|
2013-07-02 04:31:00 -05:00
|
|
|
/// Extra information for tracking macro expansion of spans
|
2015-01-28 07:34:18 -06:00
|
|
|
#[derive(Hash, Debug)]
|
2013-07-02 04:31:00 -05:00
|
|
|
pub struct ExpnInfo {
|
2014-03-26 08:40:51 -05:00
|
|
|
/// The location of the actual macro invocation, e.g. `let x =
|
|
|
|
/// foo!();`
|
|
|
|
///
|
|
|
|
/// 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 17:39:48 -05:00
|
|
|
pub call_site: Span,
|
2014-03-26 08:40:51 -05:00
|
|
|
/// Information about the macro and its definition.
|
|
|
|
///
|
|
|
|
/// The `callee` of the inner expression in the `call_site`
|
|
|
|
/// example would point to the `macro_rules! bar { ... }` and that
|
|
|
|
/// of the `bar!()` invocation would point to the `macro_rules!
|
|
|
|
/// foo { ... }`.
|
2014-03-27 17:39:48 -05:00
|
|
|
pub callee: NameAndSpan
|
2013-02-21 02:16:31 -06:00
|
|
|
}
|
2013-01-30 11:56:33 -06:00
|
|
|
|
2015-01-28 07:34:18 -06:00
|
|
|
#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)]
|
2014-09-28 11:25:48 -05:00
|
|
|
pub struct ExpnId(u32);
|
2014-09-17 11:01:33 -05:00
|
|
|
|
2014-10-06 18:33:44 -05:00
|
|
|
pub const NO_EXPANSION: ExpnId = ExpnId(-1);
|
2014-12-10 14:18:23 -06:00
|
|
|
// For code appearing from the command line
|
|
|
|
pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(-2);
|
2014-09-17 11:01:33 -05:00
|
|
|
|
2014-09-28 11:25:48 -05:00
|
|
|
impl ExpnId {
|
|
|
|
pub fn from_llvm_cookie(cookie: c_uint) -> ExpnId {
|
|
|
|
ExpnId(cookie as u32)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_llvm_cookie(self) -> i32 {
|
|
|
|
let ExpnId(cookie) = self;
|
|
|
|
cookie as i32
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// FileMap, MultiByteChar, FileName, FileLines
|
|
|
|
//
|
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
pub type FileName = String;
|
2012-11-12 20:35:17 -06:00
|
|
|
|
2014-03-16 13:56:24 -05:00
|
|
|
pub struct FileLines {
|
2014-03-27 17:39:48 -05:00
|
|
|
pub file: Rc<FileMap>,
|
2015-01-17 17:33:05 -06:00
|
|
|
pub lines: Vec<usize>
|
2014-03-16 13:56:24 -05:00
|
|
|
}
|
2012-11-12 20:24:56 -06:00
|
|
|
|
2012-11-15 21:37:29 -06:00
|
|
|
/// Identifies an offset of a multi-byte character in a FileMap
|
2015-02-11 11:29:49 -06:00
|
|
|
#[derive(Copy, RustcEncodable, RustcDecodable, Eq, PartialEq)]
|
2012-11-15 21:37:29 -06:00
|
|
|
pub struct MultiByteChar {
|
|
|
|
/// The absolute offset of the character in the CodeMap
|
2014-03-27 17:39:48 -05:00
|
|
|
pub pos: BytePos,
|
2012-11-15 21:37:29 -06:00
|
|
|
/// The number of bytes, >=2
|
2015-01-17 17:33:05 -06:00
|
|
|
pub bytes: usize,
|
2012-11-15 21:37:29 -06:00
|
|
|
}
|
|
|
|
|
2012-11-16 17:14:11 -06:00
|
|
|
/// A single source in the CodeMap
|
2012-11-12 20:59:37 -06:00
|
|
|
pub struct FileMap {
|
2012-11-16 17:14:11 -06: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 17:39:48 -05:00
|
|
|
pub name: FileName,
|
2012-11-16 17:14:11 -06:00
|
|
|
/// The complete source code
|
2015-02-11 11:29:49 -06:00
|
|
|
pub src: Option<Rc<String>>,
|
2012-11-16 17:14:11 -06:00
|
|
|
/// The start position of this source in the CodeMap
|
2014-03-27 17:39:48 -05:00
|
|
|
pub start_pos: BytePos,
|
2015-02-11 11:29:49 -06:00
|
|
|
/// The end position of this source in the CodeMap
|
|
|
|
pub end_pos: BytePos,
|
2012-11-16 17:14:11 -06:00
|
|
|
/// Locations of lines beginnings in the source code
|
2015-02-11 11:29:49 -06:00
|
|
|
pub lines: RefCell<Vec<BytePos>>,
|
2012-11-16 17:14:11 -06:00
|
|
|
/// Locations of multi-byte characters in the source code
|
2015-02-11 11:29:49 -06: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| {
|
|
|
|
try! { s.emit_struct_field("name", 0, |s| self.name.encode(s)) };
|
|
|
|
try! { s.emit_struct_field("start_pos", 1, |s| self.start_pos.encode(s)) };
|
|
|
|
try! { s.emit_struct_field("end_pos", 2, |s| self.end_pos.encode(s)) };
|
|
|
|
try! { s.emit_struct_field("lines", 3, |s| {
|
|
|
|
let lines = self.lines.borrow();
|
|
|
|
// store the length
|
|
|
|
try! { s.emit_u32(lines.len() as u32) };
|
|
|
|
|
|
|
|
if lines.len() > 0 {
|
|
|
|
// 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 {
|
2015-03-18 11:14:54 -05:00
|
|
|
lines.windows(2)
|
2015-02-11 11:29:49 -06:00
|
|
|
.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.
|
|
|
|
try! { bytes_per_diff.encode(s) };
|
|
|
|
|
|
|
|
// Encode the first element.
|
|
|
|
try! { 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 { try! { (diff.0 as u8).encode(s) } },
|
|
|
|
2 => for diff in diff_iter { try! { (diff.0 as u16).encode(s) } },
|
|
|
|
4 => for diff in diff_iter { try! { (diff.0 as u32).encode(s) } },
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
};
|
|
|
|
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| {
|
|
|
|
let name: String = try! {
|
|
|
|
d.read_struct_field("name", 0, |d| Decodable::decode(d))
|
|
|
|
};
|
|
|
|
let start_pos: BytePos = try! {
|
|
|
|
d.read_struct_field("start_pos", 1, |d| Decodable::decode(d))
|
|
|
|
};
|
|
|
|
let end_pos: BytePos = try! {
|
|
|
|
d.read_struct_field("end_pos", 2, |d| Decodable::decode(d))
|
|
|
|
};
|
|
|
|
let lines: Vec<BytePos> = try! {
|
|
|
|
d.read_struct_field("lines", 3, |d| {
|
|
|
|
let num_lines: u32 = try! { 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 = try! { Decodable::decode(d) };
|
|
|
|
|
|
|
|
// Read the first element.
|
|
|
|
let mut line_start: BytePos = try! { Decodable::decode(d) };
|
|
|
|
lines.push(line_start);
|
|
|
|
|
|
|
|
for _ in 1..num_lines {
|
|
|
|
let diff = match bytes_per_diff {
|
|
|
|
1 => try! { d.read_u8() } as u32,
|
|
|
|
2 => try! { d.read_u16() } as u32,
|
|
|
|
4 => try! { d.read_u32() },
|
|
|
|
_ => unreachable!()
|
|
|
|
};
|
|
|
|
|
|
|
|
line_start = line_start + BytePos(diff);
|
|
|
|
|
|
|
|
lines.push(line_start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(lines)
|
|
|
|
})
|
|
|
|
};
|
|
|
|
let multibyte_chars: Vec<MultiByteChar> = try! {
|
|
|
|
d.read_struct_field("multibyte_chars", 4, |d| Decodable::decode(d))
|
|
|
|
};
|
|
|
|
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 17:12:20 -06:00
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
impl FileMap {
|
2014-06-09 15:12:30 -05: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 17:17:22 -05:00
|
|
|
pub fn next_line(&self, pos: BytePos) {
|
2013-01-30 11:56:33 -06:00
|
|
|
// the new charpos must be > the last one (or it's the first one).
|
2014-10-15 01:05:01 -05:00
|
|
|
let mut lines = self.lines.borrow_mut();
|
2014-03-20 17:05:37 -05:00
|
|
|
let line_len = lines.len();
|
2014-11-14 11:18:10 -06:00
|
|
|
assert!(line_len == 0 || ((*lines)[line_len - 1] < pos));
|
2014-03-20 17:05:37 -05:00
|
|
|
lines.push(pos);
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2011-07-05 04:48:19 -05:00
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// get a line from the list of pre-computed line-beginnings
|
2014-09-23 18:07:21 -05:00
|
|
|
///
|
2015-01-17 17:33:05 -06:00
|
|
|
pub fn get_line(&self, line_number: usize) -> Option<String> {
|
2015-02-11 11:29:49 -06: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();
|
|
|
|
let slice = &src[begin..];
|
|
|
|
match slice.find('\n') {
|
|
|
|
Some(e) => &slice[..e],
|
|
|
|
None => slice
|
|
|
|
}.to_string()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
None => None
|
|
|
|
}
|
2012-11-12 18:56:39 -06:00
|
|
|
}
|
2011-07-16 01:01:10 -05:00
|
|
|
|
2015-01-17 17:33:05 -06:00
|
|
|
pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(bytes >=2 && bytes <= 4);
|
2012-11-15 21:37:29 -06:00
|
|
|
let mbc = MultiByteChar {
|
|
|
|
pos: pos,
|
|
|
|
bytes: bytes,
|
|
|
|
};
|
2014-03-20 17:05:37 -05:00
|
|
|
self.multibyte_chars.borrow_mut().push(mbc);
|
2012-11-15 21:37:29 -06:00
|
|
|
}
|
2013-11-27 21:06:35 -06:00
|
|
|
|
|
|
|
pub fn is_real_file(&self) -> bool {
|
2014-11-27 14:00:50 -06:00
|
|
|
!(self.name.starts_with("<") &&
|
|
|
|
self.name.ends_with(">"))
|
2013-11-27 21:06:35 -06:00
|
|
|
}
|
2015-02-11 11:29:49 -06:00
|
|
|
|
|
|
|
pub fn is_imported(&self) -> bool {
|
|
|
|
self.src.is_none()
|
|
|
|
}
|
2012-02-01 04:37:53 -06:00
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
|
|
|
// CodeMap
|
|
|
|
//
|
|
|
|
|
2012-11-12 20:24:56 -06:00
|
|
|
pub struct CodeMap {
|
2014-09-17 11:01:33 -05:00
|
|
|
pub files: RefCell<Vec<Rc<FileMap>>>,
|
|
|
|
expansions: RefCell<Vec<ExpnInfo>>
|
2011-07-05 04:48:19 -05:00
|
|
|
}
|
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
impl CodeMap {
|
2013-03-21 21:07:54 -05:00
|
|
|
pub fn new() -> CodeMap {
|
2012-11-12 20:24:56 -06:00
|
|
|
CodeMap {
|
2014-02-28 15:09:09 -06:00
|
|
|
files: RefCell::new(Vec::new()),
|
2014-09-17 11:01:33 -05:00
|
|
|
expansions: RefCell::new(Vec::new()),
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2012-01-21 03:00:06 -06:00
|
|
|
}
|
2012-11-12 20:24:56 -06:00
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
pub fn new_filemap(&self, filename: FileName, src: String) -> Rc<FileMap> {
|
2013-12-30 18:30:33 -06:00
|
|
|
let mut files = self.files.borrow_mut();
|
2014-03-20 17:05:37 -05:00
|
|
|
let start_pos = match files.last() {
|
2013-12-23 08:08:23 -06:00
|
|
|
None => 0,
|
2015-02-11 11:29:49 -06:00
|
|
|
Some(last) => last.end_pos.to_usize(),
|
2012-11-15 00:27:53 -06:00
|
|
|
};
|
2012-11-16 16:22:09 -06:00
|
|
|
|
2014-03-17 19:59:44 -05:00
|
|
|
// Remove utf-8 BOM if any.
|
|
|
|
// FIXME #12884: no efficient/safe way to remove from the start of a string
|
|
|
|
// and reuse the allocation.
|
2014-12-09 16:08:10 -06:00
|
|
|
let mut src = if src.starts_with("\u{feff}") {
|
2015-01-07 10:58:31 -06:00
|
|
|
String::from_str(&src[3..])
|
2014-03-17 19:59:44 -05:00
|
|
|
} else {
|
2015-02-18 13:48:57 -06:00
|
|
|
String::from_str(&src[..])
|
2014-03-17 19:59:44 -05:00
|
|
|
};
|
|
|
|
|
2014-03-03 04:44:43 -06:00
|
|
|
// Append '\n' in case it's not already there.
|
2015-01-25 00:43:11 -06:00
|
|
|
// This is a workaround to prevent CodeMap.lookup_filemap_idx from
|
|
|
|
// accidentally overflowing into the next filemap in case the last byte
|
|
|
|
// of span is also the last byte of filemap, which leads to incorrect
|
|
|
|
// results from CodeMap.span_to_*.
|
2014-11-27 14:00:50 -06:00
|
|
|
if src.len() > 0 && !src.ends_with("\n") {
|
2014-10-15 01:05:01 -05:00
|
|
|
src.push('\n');
|
2014-03-03 04:44:43 -06:00
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
let end_pos = start_pos + src.len();
|
|
|
|
|
2014-03-16 13:56:24 -05:00
|
|
|
let filemap = Rc::new(FileMap {
|
2014-01-15 12:58:29 -06:00
|
|
|
name: filename,
|
2015-02-11 11:29:49 -06:00
|
|
|
src: Some(Rc::new(src)),
|
2015-01-17 17:49:08 -06:00
|
|
|
start_pos: Pos::from_usize(start_pos),
|
2015-02-11 11:29:49 -06:00
|
|
|
end_pos: Pos::from_usize(end_pos),
|
2014-02-28 15:09:09 -06:00
|
|
|
lines: RefCell::new(Vec::new()),
|
|
|
|
multibyte_chars: RefCell::new(Vec::new()),
|
2014-03-16 13:56:24 -05:00
|
|
|
});
|
2012-11-16 16:22:09 -06:00
|
|
|
|
2014-03-20 17:05:37 -05:00
|
|
|
files.push(filemap.clone());
|
2012-11-16 16:22:09 -06:00
|
|
|
|
2014-03-16 13:56:24 -05:00
|
|
|
filemap
|
2012-11-16 16:22:09 -06:00
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06: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,
|
|
|
|
file_local_lines: Vec<BytePos>,
|
|
|
|
file_local_multibyte_chars: Vec<MultiByteChar>)
|
|
|
|
-> Rc<FileMap> {
|
|
|
|
let mut files = self.files.borrow_mut();
|
|
|
|
let start_pos = match files.last() {
|
|
|
|
None => 0,
|
|
|
|
Some(last) => last.end_pos.to_usize(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let end_pos = Pos::from_usize(start_pos + source_len);
|
|
|
|
let start_pos = Pos::from_usize(start_pos);
|
|
|
|
|
|
|
|
let lines = file_local_lines.map_in_place(|pos| pos + start_pos);
|
|
|
|
let multibyte_chars = file_local_multibyte_chars.map_in_place(|mbc| MultiByteChar {
|
|
|
|
pos: mbc.pos + start_pos,
|
|
|
|
bytes: mbc.bytes
|
|
|
|
});
|
|
|
|
|
|
|
|
let filemap = Rc::new(FileMap {
|
|
|
|
name: filename,
|
|
|
|
src: None,
|
|
|
|
start_pos: start_pos,
|
|
|
|
end_pos: end_pos,
|
|
|
|
lines: RefCell::new(lines),
|
|
|
|
multibyte_chars: RefCell::new(multibyte_chars),
|
|
|
|
});
|
|
|
|
|
|
|
|
files.push(filemap.clone());
|
|
|
|
|
|
|
|
filemap
|
|
|
|
}
|
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
pub fn mk_substr_filename(&self, sp: Span) -> String {
|
2012-11-12 20:24:56 -06:00
|
|
|
let pos = self.lookup_char_pos(sp.lo);
|
2014-05-07 18:33:43 -05:00
|
|
|
(format!("<{}:{}:{}>",
|
|
|
|
pos.file.name,
|
|
|
|
pos.line,
|
2015-01-17 17:49:08 -06:00
|
|
|
pos.col.to_usize() + 1)).to_string()
|
2011-07-05 04:48:19 -05:00
|
|
|
}
|
2012-01-25 15:22:10 -06:00
|
|
|
|
2012-11-16 17:14:11 -06:00
|
|
|
/// Lookup source information about a BytePos
|
2013-04-17 11:15:08 -05:00
|
|
|
pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
|
2014-03-16 13:56:24 -05:00
|
|
|
self.lookup_pos(pos)
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2011-07-16 01:01:10 -05:00
|
|
|
|
2013-05-31 17:17:22 -05:00
|
|
|
pub fn lookup_char_pos_adj(&self, pos: BytePos) -> LocWithOpt {
|
2012-11-12 20:24:56 -06:00
|
|
|
let loc = self.lookup_char_pos(pos);
|
2014-01-15 12:58:29 -06:00
|
|
|
LocWithOpt {
|
2014-05-25 05:17:19 -05:00
|
|
|
filename: loc.file.name.to_string(),
|
2014-01-15 12:58:29 -06:00
|
|
|
line: loc.line,
|
|
|
|
col: loc.col,
|
|
|
|
file: Some(loc.file)
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
|
|
|
}
|
2011-07-05 04:48:19 -05:00
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
pub fn span_to_string(&self, sp: Span) -> String {
|
2014-03-20 17:05:37 -05:00
|
|
|
if self.files.borrow().len() == 0 && sp == DUMMY_SP {
|
2014-05-25 05:17:19 -05:00
|
|
|
return "no-location".to_string();
|
2012-12-05 17:13:24 -06:00
|
|
|
}
|
|
|
|
|
2012-11-12 20:24:56 -06:00
|
|
|
let lo = self.lookup_char_pos_adj(sp.lo);
|
|
|
|
let hi = self.lookup_char_pos_adj(sp.hi);
|
2014-05-07 18:33:43 -05:00
|
|
|
return (format!("{}:{}:{}: {}:{}",
|
|
|
|
lo.filename,
|
|
|
|
lo.line,
|
2015-01-17 17:49:08 -06:00
|
|
|
lo.col.to_usize() + 1,
|
2014-05-07 18:33:43 -05:00
|
|
|
hi.line,
|
2015-01-17 17:49:08 -06:00
|
|
|
hi.col.to_usize() + 1)).to_string()
|
2012-02-10 12:28:43 -06:00
|
|
|
}
|
|
|
|
|
2013-08-31 11:13:04 -05:00
|
|
|
pub fn span_to_filename(&self, sp: Span) -> FileName {
|
2014-05-25 05:17:19 -05:00
|
|
|
self.lookup_char_pos(sp.lo).file.name.to_string()
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2011-07-05 04:48:19 -05:00
|
|
|
|
2014-03-16 13:56:24 -05:00
|
|
|
pub fn span_to_lines(&self, sp: Span) -> FileLines {
|
2012-11-12 20:24:56 -06:00
|
|
|
let lo = self.lookup_char_pos(sp.lo);
|
|
|
|
let hi = self.lookup_char_pos(sp.hi);
|
2014-02-28 15:09:09 -06:00
|
|
|
let mut lines = Vec::new();
|
2015-01-27 19:01:48 -06:00
|
|
|
for i in lo.line - 1..hi.line as usize {
|
2012-11-12 20:24:56 -06:00
|
|
|
lines.push(i);
|
|
|
|
};
|
2014-03-16 13:56:24 -05:00
|
|
|
FileLines {file: lo.file, lines: lines}
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2012-02-10 12:28:43 -06:00
|
|
|
|
2015-02-05 09:02:22 -06: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 11:29:49 -06:00
|
|
|
let local_begin = self.lookup_byte_offset(sp.lo);
|
|
|
|
let local_end = self.lookup_byte_offset(sp.hi);
|
2013-08-03 21:14:01 -05:00
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
if local_begin.fm.start_pos != local_end.fm.start_pos {
|
2015-02-05 09:02:22 -06:00
|
|
|
return Err(SpanSnippetError::DistinctSources(DistinctSources {
|
2015-02-11 11:29:49 -06: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 09:02:22 -06:00
|
|
|
}));
|
2013-08-03 21:14:01 -05:00
|
|
|
} else {
|
2015-02-11 11:29:49 -06: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 09:02:22 -06:00
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
return Ok((&src[start_index..end_index]).to_string())
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
return Err(SpanSnippetError::SourceNotAvailable {
|
|
|
|
filename: local_begin.fm.name.clone()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2013-08-03 21:14:01 -05:00
|
|
|
}
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2012-05-10 19:18:04 -05:00
|
|
|
|
2014-03-16 13:56:24 -05:00
|
|
|
pub fn get_filemap(&self, filename: &str) -> Rc<FileMap> {
|
2015-01-31 11:20:46 -06:00
|
|
|
for fm in &*self.files.borrow() {
|
2014-11-27 14:00:50 -06:00
|
|
|
if filename == fm.name {
|
2014-03-16 13:56:24 -05:00
|
|
|
return fm.clone();
|
2013-12-30 18:30:33 -06:00
|
|
|
}
|
|
|
|
}
|
2014-10-09 14:17:22 -05:00
|
|
|
panic!("asking for {} which we don't know about", filename);
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2012-01-25 15:22:10 -06:00
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
/// For a global BytePos compute the local offset within the containing FileMap
|
2014-02-04 22:31:33 -06:00
|
|
|
pub fn lookup_byte_offset(&self, bpos: BytePos) -> FileMapAndBytePos {
|
|
|
|
let idx = self.lookup_filemap_idx(bpos);
|
2014-10-15 01:05:01 -05:00
|
|
|
let fm = (*self.files.borrow())[idx].clone();
|
2014-02-04 22:31:33 -06:00
|
|
|
let offset = bpos - fm.start_pos;
|
|
|
|
FileMapAndBytePos {fm: fm, pos: offset}
|
|
|
|
}
|
|
|
|
|
2014-06-09 15:12:30 -05:00
|
|
|
/// Converts an absolute BytePos to a CharPos relative to the filemap and above.
|
2014-02-04 22:31:33 -06: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 01:05:01 -05:00
|
|
|
let map = &(*files)[idx];
|
2014-02-04 22:31:33 -06:00
|
|
|
|
|
|
|
// The number of extra bytes due to multibyte chars in the FileMap
|
|
|
|
let mut total_extra_bytes = 0;
|
|
|
|
|
2015-01-31 11:20:46 -06:00
|
|
|
for mbc in &*map.multibyte_chars.borrow() {
|
2014-12-20 02:09:35 -06:00
|
|
|
debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos);
|
2014-02-04 22:31:33 -06: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 17:49:08 -06:00
|
|
|
assert!(bpos.to_usize() >= mbc.pos.to_usize() + mbc.bytes);
|
2014-02-04 22:31:33 -06:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-17 17:49:08 -06: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-04 22:31:33 -06:00
|
|
|
}
|
|
|
|
|
2015-01-17 17:33:05 -06:00
|
|
|
fn lookup_filemap_idx(&self, pos: BytePos) -> usize {
|
2013-12-30 18:30:33 -06:00
|
|
|
let files = self.files.borrow();
|
2014-10-15 01:05:01 -05:00
|
|
|
let files = &*files;
|
2013-03-16 13:11:31 -05:00
|
|
|
let len = files.len();
|
2015-01-27 19:01:48 -06:00
|
|
|
let mut a = 0;
|
2012-11-12 20:24:56 -06:00
|
|
|
let mut b = len;
|
2015-01-27 19:01:48 -06:00
|
|
|
while b - a > 1 {
|
|
|
|
let m = (a + b) / 2;
|
2014-10-15 01:05:01 -05:00
|
|
|
if files[m].start_pos > pos {
|
2012-11-12 21:32:48 -06:00
|
|
|
b = m;
|
|
|
|
} else {
|
|
|
|
a = m;
|
|
|
|
}
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2014-10-15 01:05:01 -05:00
|
|
|
// There can be filemaps with length 0. These have the same start_pos as
|
|
|
|
// the previous filemap, but are not the filemaps we want (because they
|
|
|
|
// are length 0, they cannot contain what we are looking for). So,
|
|
|
|
// rewind until we find a useful filemap.
|
2014-02-18 19:24:07 -06:00
|
|
|
loop {
|
2014-10-15 01:05:01 -05:00
|
|
|
let lines = files[a].lines.borrow();
|
2014-03-20 17:05:37 -05:00
|
|
|
let lines = lines;
|
2014-02-18 19:24:07 -06:00
|
|
|
if lines.len() > 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if a == 0 {
|
2014-10-09 14:17:22 -05:00
|
|
|
panic!("position {} does not resolve to a source location",
|
2015-01-17 17:49:08 -06:00
|
|
|
pos.to_usize());
|
2014-02-18 19:24:07 -06:00
|
|
|
}
|
|
|
|
a -= 1;
|
|
|
|
}
|
2014-01-19 02:21:14 -06:00
|
|
|
if a >= len {
|
2014-10-09 14:17:22 -05:00
|
|
|
panic!("position {} does not resolve to a source location",
|
2015-01-17 17:49:08 -06:00
|
|
|
pos.to_usize())
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2012-11-15 21:37:29 -06:00
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2014-03-16 13:56:24 -05:00
|
|
|
fn lookup_line(&self, pos: BytePos) -> FileMapAndLine {
|
2012-11-15 21:37:29 -06:00
|
|
|
let idx = self.lookup_filemap_idx(pos);
|
2013-12-30 18:30:33 -06:00
|
|
|
|
|
|
|
let files = self.files.borrow();
|
2014-10-15 01:05:01 -05:00
|
|
|
let f = (*files)[idx].clone();
|
2015-01-27 19:01:48 -06:00
|
|
|
let mut a = 0;
|
2014-03-16 13:56:24 -05:00
|
|
|
{
|
2014-10-04 08:44:50 -05:00
|
|
|
let lines = f.lines.borrow();
|
2014-03-20 17:05:37 -05:00
|
|
|
let mut b = lines.len();
|
2015-01-27 19:01:48 -06:00
|
|
|
while b - a > 1 {
|
|
|
|
let m = (a + b) / 2;
|
2014-10-15 01:05:01 -05:00
|
|
|
if (*lines)[m] > pos { b = m; } else { a = m; }
|
2014-03-16 13:56:24 -05:00
|
|
|
}
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2014-03-16 13:56:24 -05:00
|
|
|
FileMapAndLine {fm: f, line: a}
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2012-01-25 15:22:10 -06:00
|
|
|
|
2013-04-17 11:15:08 -05:00
|
|
|
fn lookup_pos(&self, pos: BytePos) -> Loc {
|
2013-01-30 11:56:33 -06:00
|
|
|
let FileMapAndLine {fm: f, line: a} = self.lookup_line(pos);
|
2015-01-27 19:01:48 -06:00
|
|
|
let line = a + 1; // Line numbers start at 1
|
2014-02-27 17:53:36 -06:00
|
|
|
let chpos = self.bytepos_to_file_charpos(pos);
|
2014-10-15 01:05:01 -05:00
|
|
|
let linebpos = (*f.lines.borrow())[a];
|
2014-02-27 17:53:36 -06:00
|
|
|
let linechpos = self.bytepos_to_file_charpos(linebpos);
|
2014-12-20 02:09:35 -06:00
|
|
|
debug!("byte pos {:?} is on the line at byte pos {:?}",
|
2012-11-15 21:37:29 -06:00
|
|
|
pos, linebpos);
|
2014-12-20 02:09:35 -06:00
|
|
|
debug!("char pos {:?} is on the line at char pos {:?}",
|
2012-11-15 21:37:29 -06:00
|
|
|
chpos, linechpos);
|
2014-06-23 15:37:30 -05:00
|
|
|
debug!("byte is on line: {}", line);
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(chpos >= linechpos);
|
2014-03-16 13:56:24 -05:00
|
|
|
Loc {
|
2012-11-12 21:32:48 -06:00
|
|
|
file: f,
|
2012-11-15 21:37:29 -06:00
|
|
|
line: line,
|
|
|
|
col: chpos - linechpos
|
2014-03-16 13:56:24 -05:00
|
|
|
}
|
2012-11-12 20:24:56 -06:00
|
|
|
}
|
2014-09-17 11:01:33 -05:00
|
|
|
|
|
|
|
pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
|
|
|
|
let mut expansions = self.expansions.borrow_mut();
|
|
|
|
expansions.push(expn_info);
|
|
|
|
ExpnId(expansions.len().to_u32().expect("too many ExpnInfo's!") - 1)
|
|
|
|
}
|
|
|
|
|
2014-12-08 12:28:32 -06:00
|
|
|
pub fn with_expn_info<T, F>(&self, id: ExpnId, f: F) -> T where
|
|
|
|
F: FnOnce(Option<&ExpnInfo>) -> T,
|
|
|
|
{
|
2014-09-17 11:01:33 -05:00
|
|
|
match id {
|
|
|
|
NO_EXPANSION => f(None),
|
2015-01-17 17:33:05 -06:00
|
|
|
ExpnId(i) => f(Some(&(*self.expansions.borrow())[i as usize]))
|
2014-09-17 11:01:33 -05:00
|
|
|
}
|
|
|
|
}
|
2014-12-23 23:44:13 -06: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-02-28 21:09:28 -06: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 =
|
|
|
|
info.callee.span.map_or(span == info.call_site, |mac_span| {
|
2015-03-10 21:47:57 -05:00
|
|
|
mac_span.lo <= span.lo && span.hi <= mac_span.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-02-28 21:09:28 -06: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-23 23:44:13 -06: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-02-28 21:09:28 -06:00
|
|
|
// not the right place, keep looking
|
|
|
|
expn_id = info.call_site.expn_id;
|
|
|
|
false
|
2014-12-23 23:44:13 -06: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-02-28 21:09:28 -06:00
|
|
|
})
|
|
|
|
});
|
|
|
|
if quit {
|
|
|
|
break
|
2014-12-23 23:44:13 -06: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-02-28 21:09:28 -06:00
|
|
|
}
|
|
|
|
debug!("span_allows_unstable? {}", allows_unstable);
|
|
|
|
allows_unstable
|
2014-12-23 23:44:13 -06:00
|
|
|
}
|
2011-07-11 01:27:03 -05:00
|
|
|
}
|
2012-01-26 03:52:08 -06:00
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// SpanSnippetError, DistinctSources, MalformedCodemapPositions
|
|
|
|
//
|
|
|
|
|
2015-02-05 09:02:22 -06:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub enum SpanSnippetError {
|
|
|
|
IllFormedSpan(Span),
|
|
|
|
DistinctSources(DistinctSources),
|
|
|
|
MalformedForCodemap(MalformedCodemapPositions),
|
2015-02-11 11:29:49 -06:00
|
|
|
SourceNotAvailable { filename: String }
|
2015-02-05 09:02:22 -06: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 11:29:49 -06:00
|
|
|
|
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Tests
|
|
|
|
//
|
|
|
|
|
2013-01-30 11:56:33 -06:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t1 () {
|
|
|
|
let cm = CodeMap::new();
|
2014-05-25 05:17:19 -05:00
|
|
|
let fm = cm.new_filemap("blork.rs".to_string(),
|
|
|
|
"first line.\nsecond line".to_string());
|
2013-01-30 11:56:33 -06:00
|
|
|
fm.next_line(BytePos(0));
|
2014-11-20 14:24:39 -06:00
|
|
|
assert_eq!(fm.get_line(0), Some("first line.".to_string()));
|
2013-01-30 11:56:33 -06:00
|
|
|
// TESTING BROKEN BEHAVIOR:
|
|
|
|
fm.next_line(BytePos(10));
|
2014-11-20 14:24:39 -06:00
|
|
|
assert_eq!(fm.get_line(1), Some(".".to_string()));
|
2013-01-30 11:56:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2015-01-31 17:08:25 -06:00
|
|
|
#[should_panic]
|
2013-01-30 11:56:33 -06:00
|
|
|
fn t2 () {
|
|
|
|
let cm = CodeMap::new();
|
2014-05-25 05:17:19 -05:00
|
|
|
let fm = cm.new_filemap("blork.rs".to_string(),
|
|
|
|
"first line.\nsecond line".to_string());
|
2013-01-30 11:56:33 -06:00
|
|
|
// TESTING *REALLY* BROKEN BEHAVIOR:
|
|
|
|
fm.next_line(BytePos(0));
|
|
|
|
fm.next_line(BytePos(10));
|
|
|
|
fm.next_line(BytePos(2));
|
|
|
|
}
|
2014-02-18 19:24:07 -06:00
|
|
|
|
2014-02-27 17:53:36 -06:00
|
|
|
fn init_code_map() -> CodeMap {
|
2014-02-18 19:24:07 -06:00
|
|
|
let cm = CodeMap::new();
|
2014-05-25 05:17:19 -05: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-18 19:24:07 -06:00
|
|
|
|
|
|
|
fm1.next_line(BytePos(0));
|
|
|
|
fm1.next_line(BytePos(12));
|
2014-03-03 04:44:43 -06:00
|
|
|
fm2.next_line(BytePos(24));
|
|
|
|
fm3.next_line(BytePos(24));
|
|
|
|
fm3.next_line(BytePos(34));
|
2014-02-18 19:24:07 -06:00
|
|
|
|
|
|
|
cm
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t3() {
|
|
|
|
// Test lookup_byte_offset
|
|
|
|
let cm = init_code_map();
|
|
|
|
|
|
|
|
let fmabp1 = cm.lookup_byte_offset(BytePos(22));
|
2014-11-27 18:52:53 -06:00
|
|
|
assert_eq!(fmabp1.fm.name, "blork.rs");
|
2014-02-18 19:24:07 -06:00
|
|
|
assert_eq!(fmabp1.pos, BytePos(22));
|
|
|
|
|
2014-03-03 04:44:43 -06:00
|
|
|
let fmabp2 = cm.lookup_byte_offset(BytePos(24));
|
2014-11-27 18:52:53 -06:00
|
|
|
assert_eq!(fmabp2.fm.name, "blork2.rs");
|
2014-02-18 19:24:07 -06:00
|
|
|
assert_eq!(fmabp2.pos, BytePos(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t4() {
|
2014-02-27 17:53:36 -06:00
|
|
|
// Test bytepos_to_file_charpos
|
2014-02-18 19:24:07 -06:00
|
|
|
let cm = init_code_map();
|
|
|
|
|
2014-02-27 17:53:36 -06:00
|
|
|
let cp1 = cm.bytepos_to_file_charpos(BytePos(22));
|
2014-02-18 19:24:07 -06:00
|
|
|
assert_eq!(cp1, CharPos(22));
|
|
|
|
|
2014-03-03 04:44:43 -06:00
|
|
|
let cp2 = cm.bytepos_to_file_charpos(BytePos(24));
|
2014-02-27 17:53:36 -06:00
|
|
|
assert_eq!(cp2, CharPos(0));
|
2014-02-18 19:24:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t5() {
|
|
|
|
// Test zero-length filemaps.
|
|
|
|
let cm = init_code_map();
|
|
|
|
|
|
|
|
let loc1 = cm.lookup_char_pos(BytePos(22));
|
2014-11-27 18:52:53 -06:00
|
|
|
assert_eq!(loc1.file.name, "blork.rs");
|
2014-02-18 19:24:07 -06:00
|
|
|
assert_eq!(loc1.line, 2);
|
|
|
|
assert_eq!(loc1.col, CharPos(10));
|
|
|
|
|
2014-03-03 04:44:43 -06:00
|
|
|
let loc2 = cm.lookup_char_pos(BytePos(24));
|
2014-11-27 18:52:53 -06:00
|
|
|
assert_eq!(loc2.file.name, "blork2.rs");
|
2014-02-18 19:24:07 -06:00
|
|
|
assert_eq!(loc2.line, 1);
|
|
|
|
assert_eq!(loc2.col, CharPos(0));
|
|
|
|
}
|
2014-02-27 17:53:36 -06:00
|
|
|
|
|
|
|
fn init_code_map_mbc() -> CodeMap {
|
|
|
|
let cm = CodeMap::new();
|
|
|
|
// € is a three byte utf8 char.
|
2014-05-07 18:33:43 -05:00
|
|
|
let fm1 =
|
2014-05-25 05:17:19 -05: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 17:53:36 -06:00
|
|
|
|
|
|
|
fm1.next_line(BytePos(0));
|
|
|
|
fm1.next_line(BytePos(22));
|
2014-03-03 04:44:43 -06:00
|
|
|
fm2.next_line(BytePos(40));
|
|
|
|
fm2.next_line(BytePos(58));
|
2014-02-27 17:53:36 -06: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);
|
2014-03-03 04:44:43 -06:00
|
|
|
fm2.record_multibyte_char(BytePos(50), 3);
|
|
|
|
fm2.record_multibyte_char(BytePos(53), 3);
|
|
|
|
fm2.record_multibyte_char(BytePos(58), 3);
|
2014-02-27 17:53:36 -06: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 04:44:43 -06:00
|
|
|
let cp3 = cm.bytepos_to_file_charpos(BytePos(56));
|
2014-02-27 17:53:36 -06:00
|
|
|
assert_eq!(cp3, CharPos(12));
|
|
|
|
|
2014-03-03 04:44:43 -06:00
|
|
|
let cp4 = cm.bytepos_to_file_charpos(BytePos(61));
|
2014-02-27 17:53:36 -06:00
|
|
|
assert_eq!(cp4, CharPos(15));
|
|
|
|
}
|
2014-03-03 04:44:43 -06: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 06:36:01 -05:00
|
|
|
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
|
2014-03-03 04:44:43 -06:00
|
|
|
let file_lines = cm.span_to_lines(span);
|
|
|
|
|
2014-11-27 18:52:53 -06:00
|
|
|
assert_eq!(file_lines.file.name, "blork.rs");
|
2014-03-03 04:44:43 -06:00
|
|
|
assert_eq!(file_lines.lines.len(), 1);
|
2015-01-27 19:01:48 -06:00
|
|
|
assert_eq!(file_lines.lines[0], 1);
|
2014-03-03 04:44:43 -06: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 06:36:01 -05:00
|
|
|
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
|
2014-03-03 04:44:43 -06:00
|
|
|
let snippet = cm.span_to_snippet(span);
|
|
|
|
|
2015-02-05 09:02:22 -06:00
|
|
|
assert_eq!(snippet, Ok("second line".to_string()));
|
2014-03-03 04:44:43 -06: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 06:36:01 -05:00
|
|
|
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
|
2014-06-21 05:39:03 -05:00
|
|
|
let sstr = cm.span_to_string(span);
|
2014-03-03 04:44:43 -06:00
|
|
|
|
2014-11-27 18:52:53 -06:00
|
|
|
assert_eq!(sstr, "blork.rs:2:1: 2:12");
|
2014-03-03 04:44:43 -06:00
|
|
|
}
|
2013-01-30 11:56:33 -06:00
|
|
|
}
|