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-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
|
|
|
|
2015-06-16 13:47:09 -05:00
|
|
|
pub use self::ExpnFormat::*;
|
2014-11-06 02:05:53 -06:00
|
|
|
|
2016-06-21 17:08:13 -05:00
|
|
|
use std::cell::RefCell;
|
2016-06-09 15:36:20 -05:00
|
|
|
use std::path::{Path,PathBuf};
|
2014-03-16 13:56:24 -05:00
|
|
|
use std::rc::Rc;
|
2014-12-22 11:04:23 -06:00
|
|
|
|
2016-06-09 15:36:20 -05:00
|
|
|
use std::env;
|
2016-06-21 17:08:13 -05:00
|
|
|
use std::fs;
|
2015-05-13 17:44:57 -05:00
|
|
|
use std::io::{self, Read};
|
2016-06-24 08:05:28 -05:00
|
|
|
pub use syntax_pos::*;
|
2016-06-21 17:08:13 -05:00
|
|
|
use errors::CodeMapper;
|
2012-05-15 15:40:18 -05:00
|
|
|
|
2015-08-26 19:11:53 -05:00
|
|
|
use ast::Name;
|
2015-02-11 11:29:49 -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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-21 17:08:13 -05:00
|
|
|
/// The source of expansion.
|
|
|
|
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
|
|
|
|
pub enum ExpnFormat {
|
|
|
|
/// e.g. #[derive(...)] <item>
|
|
|
|
MacroAttribute(Name),
|
|
|
|
/// e.g. `format!()`
|
|
|
|
MacroBang(Name),
|
2016-10-29 19:42:12 -05:00
|
|
|
/// Desugaring done by the compiler during HIR lowering.
|
|
|
|
CompilerDesugaring(Name)
|
2015-12-13 06:12:47 -06:00
|
|
|
}
|
|
|
|
|
2016-06-21 17:08:13 -05:00
|
|
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
|
|
|
pub struct Spanned<T> {
|
|
|
|
pub node: T,
|
|
|
|
pub span: Span,
|
2015-12-13 06:12:47 -06:00
|
|
|
}
|
|
|
|
|
2016-06-21 17:08:13 -05:00
|
|
|
pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> {
|
|
|
|
respan(mk_sp(lo, hi), t)
|
2012-11-12 19:14:15 -06:00
|
|
|
}
|
|
|
|
|
2016-06-21 17:08:13 -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
|
|
|
}
|
|
|
|
|
2016-06-21 17:08:13 -05:00
|
|
|
pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
|
|
|
|
respan(DUMMY_SP, t)
|
2015-08-26 18:46:05 -05:00
|
|
|
}
|
|
|
|
|
2016-07-19 17:50:34 -05:00
|
|
|
/// Build a span that covers the two provided spans.
|
|
|
|
pub fn combine_spans(sp1: Span, sp2: Span) -> Span {
|
|
|
|
if sp1 == DUMMY_SP && sp2 == DUMMY_SP {
|
|
|
|
DUMMY_SP
|
|
|
|
} else if sp1 == DUMMY_SP {
|
|
|
|
sp2
|
|
|
|
} else if sp2 == DUMMY_SP {
|
|
|
|
sp1
|
|
|
|
} else {
|
|
|
|
Span {
|
|
|
|
lo: if sp1.lo < sp2.lo { sp1.lo } else { sp2.lo },
|
|
|
|
hi: if sp1.hi > sp2.hi { sp1.hi } else { sp2.hi },
|
|
|
|
expn_id: if sp1.expn_id == sp2.expn_id { sp1.expn_id } else { NO_EXPANSION },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 format with which the macro was invoked.
|
2015-06-16 13:47:09 -05: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-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
|
|
|
|
2015-08-26 18:46:05 -05:00
|
|
|
impl NameAndSpan {
|
2015-08-26 19:11:53 -05:00
|
|
|
pub fn name(&self) -> Name {
|
2015-08-26 18:46:05 -05:00
|
|
|
match self.format {
|
2016-10-29 19:42:12 -05:00
|
|
|
ExpnFormat::MacroAttribute(s) |
|
|
|
|
ExpnFormat::MacroBang(s) |
|
|
|
|
ExpnFormat::CompilerDesugaring(s) => s,
|
2015-08-26 18:46:05 -05:00
|
|
|
}
|
2015-08-26 19:11:53 -05:00
|
|
|
}
|
2015-08-26 18:46:05 -05:00
|
|
|
}
|
|
|
|
|
2015-06-16 13:47:09 -05:00
|
|
|
/// Extra information for tracking spans of macro and syntax sugar expansion
|
2015-01-28 07:34:18 -06:00
|
|
|
#[derive(Hash, Debug)]
|
2013-07-02 04:31:00 -05:00
|
|
|
pub struct ExpnInfo {
|
2015-06-16 13:47:09 -05: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 08:40:51 -05: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 17:39:48 -05:00
|
|
|
pub call_site: Span,
|
2015-06-16 13:47:09 -05:00
|
|
|
/// Information about the expansion.
|
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-02-11 11:29:49 -06:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// FileMap, MultiByteChar, FileName, FileLines
|
|
|
|
//
|
|
|
|
|
2015-05-13 17:44:57 -05: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;
|
|
|
|
|
2016-06-09 15:36:20 -05:00
|
|
|
/// Return an absolute path to a file, if possible.
|
|
|
|
fn abs_path(&self, path: &Path) -> Option<PathBuf>;
|
|
|
|
|
2015-05-13 17:44:57 -05:00
|
|
|
/// 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()
|
|
|
|
}
|
|
|
|
|
2016-06-09 15:36:20 -05:00
|
|
|
fn abs_path(&self, path: &Path) -> Option<PathBuf> {
|
|
|
|
if path.is_absolute() {
|
|
|
|
Some(path.to_path_buf())
|
|
|
|
} else {
|
|
|
|
env::current_dir()
|
|
|
|
.ok()
|
|
|
|
.map(|cwd| cwd.join(path))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-13 17:44:57 -05:00
|
|
|
fn read_file(&self, path: &Path) -> io::Result<String> {
|
|
|
|
let mut src = String::new();
|
2016-03-22 22:01:37 -05:00
|
|
|
fs::File::open(path)?.read_to_string(&mut src)?;
|
2015-05-13 17:44:57 -05:00
|
|
|
Ok(src)
|
|
|
|
}
|
|
|
|
}
|
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>>>,
|
2015-05-13 17:44:57 -05:00
|
|
|
expansions: RefCell<Vec<ExpnInfo>>,
|
|
|
|
file_loader: Box<FileLoader>
|
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()),
|
2015-05-13 17:44:57 -05: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-12 20:24:56 -06:00
|
|
|
}
|
2012-01-21 03:00:06 -06:00
|
|
|
}
|
2012-11-12 20:24:56 -06:00
|
|
|
|
2015-05-13 17:44:57 -05: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-22 22:01:37 -05:00
|
|
|
let src = self.file_loader.read_file(path)?;
|
2016-06-09 15:36:20 -05:00
|
|
|
let abs_path = self.file_loader.abs_path(path).map(|p| p.to_str().unwrap().to_string());
|
|
|
|
Ok(self.new_filemap(path.to_str().unwrap().to_string(), abs_path, src))
|
2015-05-13 17:44:57 -05:00
|
|
|
}
|
|
|
|
|
2015-07-01 22:37:52 -05: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.
|
2016-06-09 15:36:20 -05:00
|
|
|
pub fn new_filemap(&self, filename: FileName, abs_path: Option<FileName>,
|
|
|
|
mut src: String) -> Rc<FileMap> {
|
2015-07-01 22:37:52 -05:00
|
|
|
let start_pos = self.next_start_pos();
|
2013-12-30 18:30:33 -06:00
|
|
|
let mut files = self.files.borrow_mut();
|
2012-11-16 16:22:09 -06:00
|
|
|
|
2014-03-17 19:59:44 -05:00
|
|
|
// Remove utf-8 BOM if any.
|
2015-05-01 10:08:39 -05:00
|
|
|
if src.starts_with("\u{feff}") {
|
|
|
|
src.drain(..3);
|
|
|
|
}
|
2014-03-17 19:59:44 -05: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,
|
2016-06-09 15:36:20 -05:00
|
|
|
abs_path: abs_path,
|
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-07-02 00:14:14 -05:00
|
|
|
/// Creates a new filemap and sets its line information.
|
2016-06-09 15:36:20 -05:00
|
|
|
pub fn new_filemap_and_lines(&self, filename: &str, abs_path: Option<&str>,
|
|
|
|
src: &str) -> Rc<FileMap> {
|
|
|
|
let fm = self.new_filemap(filename.to_string(),
|
|
|
|
abs_path.map(|s| s.to_owned()),
|
|
|
|
src.to_owned());
|
2016-04-20 13:56:01 -05:00
|
|
|
let mut byte_pos: u32 = fm.start_pos.0;
|
2015-07-02 00:14:14 -05: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 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,
|
2016-06-09 15:36:20 -05:00
|
|
|
abs_path: Option<FileName>,
|
2015-02-11 11:29:49 -06:00
|
|
|
source_len: usize,
|
2015-04-16 23:38:24 -05:00
|
|
|
mut file_local_lines: Vec<BytePos>,
|
|
|
|
mut file_local_multibyte_chars: Vec<MultiByteChar>)
|
2015-02-11 11:29:49 -06:00
|
|
|
-> Rc<FileMap> {
|
2015-07-01 22:37:52 -05:00
|
|
|
let start_pos = self.next_start_pos();
|
2015-02-11 11:29:49 -06: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-16 23:38:24 -05: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 11:29:49 -06:00
|
|
|
|
|
|
|
let filemap = Rc::new(FileMap {
|
|
|
|
name: filename,
|
2016-06-09 15:36:20 -05:00
|
|
|
abs_path: abs_path,
|
2015-02-11 11:29:49 -06:00
|
|
|
src: None,
|
|
|
|
start_pos: start_pos,
|
|
|
|
end_pos: end_pos,
|
2015-04-16 23:38:24 -05:00
|
|
|
lines: RefCell::new(file_local_lines),
|
|
|
|
multibyte_chars: RefCell::new(file_local_multibyte_chars),
|
2015-02-11 11:29:49 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
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 {
|
2015-06-16 13:47:09 -05:00
|
|
|
let chpos = self.bytepos_to_file_charpos(pos);
|
2015-07-01 22:37:52 -05: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 13:47:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-01 22:37:52 -05: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 13:47:09 -05:00
|
|
|
let idx = self.lookup_filemap_idx(pos);
|
|
|
|
|
|
|
|
let files = self.files.borrow();
|
|
|
|
let f = (*files)[idx].clone();
|
2015-07-01 22:37:52 -05:00
|
|
|
|
2016-08-24 16:06:31 -05:00
|
|
|
match f.lookup_line(pos) {
|
|
|
|
Some(line) => Ok(FileMapAndLine { fm: f, line: line }),
|
|
|
|
None => Err(f)
|
2015-07-01 22:37:52 -05:00
|
|
|
}
|
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
|
|
|
|
2016-09-19 14:31:56 -05:00
|
|
|
/// Returns `Some(span)`, a union of the lhs and rhs span. The lhs must precede the rhs. If
|
|
|
|
/// there are gaps between lhs and rhs, the resulting union will cross these gaps.
|
|
|
|
/// For this to work, the spans have to be:
|
|
|
|
/// * the expn_id of both spans much match
|
|
|
|
/// * the lhs span needs to end on the same line the rhs span begins
|
|
|
|
/// * the lhs span must start at or before the rhs span
|
|
|
|
pub fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
|
|
|
|
use std::cmp;
|
|
|
|
|
|
|
|
// make sure we're at the same expansion id
|
|
|
|
if sp_lhs.expn_id != sp_rhs.expn_id {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let lhs_end = match self.lookup_line(sp_lhs.hi) {
|
|
|
|
Ok(x) => x,
|
|
|
|
Err(_) => return None
|
|
|
|
};
|
|
|
|
let rhs_begin = match self.lookup_line(sp_rhs.lo) {
|
|
|
|
Ok(x) => x,
|
|
|
|
Err(_) => return None
|
|
|
|
};
|
|
|
|
|
|
|
|
// if we must cross lines to merge, don't merge
|
|
|
|
if lhs_end.line != rhs_begin.line {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2016-09-20 17:39:05 -05:00
|
|
|
// ensure these follow the expected order and we don't overlap
|
|
|
|
if (sp_lhs.lo <= sp_rhs.lo) && (sp_lhs.hi <= sp_rhs.lo) {
|
2016-09-19 14:31:56 -05:00
|
|
|
Some(Span {
|
|
|
|
lo: cmp::min(sp_lhs.lo, sp_rhs.lo),
|
|
|
|
hi: cmp::max(sp_lhs.hi, sp_rhs.hi),
|
|
|
|
expn_id: sp_lhs.expn_id,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
pub fn span_to_string(&self, sp: Span) -> String {
|
2016-04-20 13:52:31 -05:00
|
|
|
if sp == COMMAND_LINE_SP {
|
|
|
|
return "<command line option>".to_string();
|
|
|
|
}
|
|
|
|
|
2016-01-29 00:33:14 -06:00
|
|
|
if self.files.borrow().is_empty() && sp.source_equal(&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
|
|
|
}
|
|
|
|
|
2015-11-24 16:05:27 -06: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 16:58:09 -06: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 16:05:27 -06:00
|
|
|
span_snip.push_str("...");
|
|
|
|
}
|
2016-01-21 16:58:09 -06:00
|
|
|
|
2015-11-24 16:05:27 -06: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 16:58:09 -06: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 01:44:53 -06: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 16:58:09 -06: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 01:44:53 -06: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 16:58:09 -06:00
|
|
|
span = callsite;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
span
|
|
|
|
}
|
|
|
|
|
2016-01-29 01:22:55 -06: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 01:44:53 -06: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 01:22:55 -06:00
|
|
|
while let Some(callsite) = self.with_expn_info(span.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.call_site.clone())) {
|
2016-02-03 01:44:53 -06: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 01:22:55 -06:00
|
|
|
if let Some(_) = self.with_expn_info(callsite.expn_id,
|
2016-02-03 01:44:53 -06:00
|
|
|
|ei| ei.map(|ei| ei.call_site.clone())) {
|
2016-01-29 01:22:55 -06:00
|
|
|
span = callsite;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return self.with_expn_info(span.expn_id,
|
|
|
|
|ei| ei.map(|ei| ei.callee.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2015-04-30 03:23:50 -05:00
|
|
|
pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
|
2016-04-20 13:52:31 -05:00
|
|
|
debug!("span_to_lines(sp={:?})", sp);
|
|
|
|
|
2015-04-30 03:23:50 -05:00
|
|
|
if sp.lo > sp.hi {
|
|
|
|
return Err(SpanLinesError::IllFormedSpan(sp));
|
|
|
|
}
|
|
|
|
|
2012-11-12 20:24:56 -06:00
|
|
|
let lo = self.lookup_char_pos(sp.lo);
|
2016-04-20 13:52:31 -05:00
|
|
|
debug!("span_to_lines: lo={:?}", lo);
|
2012-11-12 20:24:56 -06:00
|
|
|
let hi = self.lookup_char_pos(sp.hi);
|
2016-04-20 13:52:31 -05:00
|
|
|
debug!("span_to_lines: hi={:?}", hi);
|
2015-04-30 03:23:50 -05: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 13:46:03 -05: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 13:56:01 -05:00
|
|
|
let line_len = lo.file.get_line(line_index)
|
|
|
|
.map(|s| s.chars().count())
|
|
|
|
.unwrap_or(0);
|
2015-04-09 13:46:03 -05: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 03:23:50 -05:00
|
|
|
Ok(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
|
|
|
|
2016-05-24 09:08:01 -05:00
|
|
|
pub fn get_filemap(&self, filename: &str) -> Option<Rc<FileMap>> {
|
2015-06-11 07:56:07 -05:00
|
|
|
for fm in self.files.borrow().iter() {
|
2014-11-27 14:00:50 -06:00
|
|
|
if filename == fm.name {
|
2016-05-24 09:08:01 -05:00
|
|
|
return Some(fm.clone());
|
2013-12-30 18:30:33 -06:00
|
|
|
}
|
|
|
|
}
|
2016-05-24 09:08:01 -05:00
|
|
|
None
|
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}
|
|
|
|
}
|
|
|
|
|
2015-07-05 21:13:19 -05:00
|
|
|
/// Converts an absolute BytePos to a CharPos relative to the filemap.
|
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-06-11 07:56:07 -05:00
|
|
|
for mbc in map.multibyte_chars.borrow().iter() {
|
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-07-01 22:37:52 -05:00
|
|
|
// Return the index of the filemap (in self.files) which contains pos.
|
2016-08-24 16:06:31 -05:00
|
|
|
pub 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;
|
2015-07-01 22:37:52 -05:00
|
|
|
let count = files.len();
|
|
|
|
|
|
|
|
// Binary search for the filemap.
|
2015-01-27 19:01:48 -06:00
|
|
|
let mut a = 0;
|
2015-07-01 22:37:52 -05:00
|
|
|
let mut b = count;
|
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
|
|
|
}
|
2015-07-01 22:37:52 -05:00
|
|
|
|
|
|
|
assert!(a < count, "position {} does not resolve to a source location", pos.to_usize());
|
2012-11-15 21:37:29 -06:00
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
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);
|
2015-04-17 17:32:42 -05: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 11:01:33 -05:00
|
|
|
}
|
|
|
|
|
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 {
|
2015-04-05 20:07:11 -05:00
|
|
|
NO_EXPANSION | COMMAND_LINE_EXPN => 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 =
|
2016-01-29 00:33:14 -06:00
|
|
|
info.callee.span.map_or(span.source_equal(&info.call_site), |mac_span| {
|
2015-09-25 20:44:37 -05: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-02-28 21:09:28 -06:00
|
|
|
});
|
|
|
|
|
2015-06-05 11:53:17 -05: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-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
|
|
|
}
|
2015-11-10 23:26:14 -06:00
|
|
|
|
|
|
|
pub fn count_lines(&self) -> usize {
|
|
|
|
self.files.borrow().iter().fold(0, |a, f| a + f.count_lines())
|
|
|
|
}
|
2016-04-15 20:23:50 -05:00
|
|
|
|
|
|
|
pub fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
|
2016-09-20 21:09:22 -05:00
|
|
|
let mut prev_span = DUMMY_SP;
|
2016-04-15 20:23:50 -05:00
|
|
|
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(..) => ("", "!"),
|
2016-10-29 19:42:12 -05:00
|
|
|
CompilerDesugaring(..) => ("desugaring of `", "`"),
|
2016-04-15 20:23:50 -05:00
|
|
|
};
|
|
|
|
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
|
2016-09-20 21:09:22 -05:00
|
|
|
if !call_site.source_equal(&prev_span) {
|
2016-04-15 20:23:50 -05:00
|
|
|
result.push(MacroBacktrace {
|
|
|
|
call_site: call_site,
|
|
|
|
macro_decl_name: macro_decl_name,
|
|
|
|
def_site_span: def_site_span,
|
|
|
|
});
|
|
|
|
}
|
2016-09-20 21:09:22 -05:00
|
|
|
prev_span = span;
|
2016-04-15 20:23:50 -05:00
|
|
|
span = call_site;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-21 17:08:13 -05:00
|
|
|
impl CodeMapper for CodeMap {
|
|
|
|
fn lookup_char_pos(&self, pos: BytePos) -> Loc {
|
|
|
|
self.lookup_char_pos(pos)
|
|
|
|
}
|
|
|
|
fn span_to_lines(&self, sp: Span) -> FileLinesResult {
|
|
|
|
self.span_to_lines(sp)
|
|
|
|
}
|
|
|
|
fn span_to_string(&self, sp: Span) -> String {
|
|
|
|
self.span_to_string(sp)
|
|
|
|
}
|
|
|
|
fn span_to_filename(&self, sp: Span) -> FileName {
|
|
|
|
self.span_to_filename(sp)
|
|
|
|
}
|
|
|
|
fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
|
|
|
|
self.macro_backtrace(span)
|
|
|
|
}
|
2016-09-19 14:31:56 -05:00
|
|
|
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
|
|
|
|
self.merge_spans(sp_lhs, sp_rhs)
|
|
|
|
}
|
2015-02-05 09:02:22 -06:00
|
|
|
}
|
|
|
|
|
2015-02-11 11:29:49 -06:00
|
|
|
// _____________________________________________________________________________
|
|
|
|
// Tests
|
|
|
|
//
|
|
|
|
|
2013-01-30 11:56:33 -06:00
|
|
|
#[cfg(test)]
|
2015-04-24 10:30:41 -05:00
|
|
|
mod tests {
|
2013-01-30 11:56:33 -06:00
|
|
|
use super::*;
|
2016-11-17 08:04:36 -06:00
|
|
|
use symbol::keywords;
|
2016-06-22 17:39:43 -05:00
|
|
|
use std::rc::Rc;
|
2013-01-30 11:56:33 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t1 () {
|
|
|
|
let cm = CodeMap::new();
|
2014-05-25 05:17:19 -05:00
|
|
|
let fm = cm.new_filemap("blork.rs".to_string(),
|
2016-06-09 15:36:20 -05:00
|
|
|
None,
|
2014-05-25 05:17:19 -05:00
|
|
|
"first line.\nsecond line".to_string());
|
2013-01-30 11:56:33 -06:00
|
|
|
fm.next_line(BytePos(0));
|
2015-07-01 22:37:52 -05:00
|
|
|
// Test we can get lines with partial line info.
|
2015-04-09 13:46:03 -05:00
|
|
|
assert_eq!(fm.get_line(0), Some("first line."));
|
2015-07-01 22:37:52 -05:00
|
|
|
// TESTING BROKEN BEHAVIOR: line break declared before actual line break.
|
2013-01-30 11:56:33 -06:00
|
|
|
fm.next_line(BytePos(10));
|
2015-04-09 13:46:03 -05:00
|
|
|
assert_eq!(fm.get_line(1), Some("."));
|
2015-07-01 22:37:52 -05:00
|
|
|
fm.next_line(BytePos(12));
|
|
|
|
assert_eq!(fm.get_line(2), Some("second line"));
|
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(),
|
2016-06-09 15:36:20 -05:00
|
|
|
None,
|
2014-05-25 05:17:19 -05:00
|
|
|
"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(),
|
2016-06-09 15:36:20 -05:00
|
|
|
None,
|
2014-05-25 05:17:19 -05:00
|
|
|
"first line.\nsecond line".to_string());
|
|
|
|
let fm2 = cm.new_filemap("empty.rs".to_string(),
|
2016-06-09 15:36:20 -05:00
|
|
|
None,
|
2014-05-25 05:17:19 -05:00
|
|
|
"".to_string());
|
|
|
|
let fm3 = cm.new_filemap("blork2.rs".to_string(),
|
2016-06-09 15:36:20 -05:00
|
|
|
None,
|
2014-05-25 05:17:19 -05:00
|
|
|
"first line.\nsecond line".to_string());
|
2014-02-18 19:24:07 -06:00
|
|
|
|
|
|
|
fm1.next_line(BytePos(0));
|
|
|
|
fm1.next_line(BytePos(12));
|
2015-07-01 22:37:52 -05:00
|
|
|
fm2.next_line(fm2.start_pos);
|
|
|
|
fm3.next_line(fm3.start_pos);
|
|
|
|
fm3.next_line(fm3.start_pos + BytePos(12));
|
2014-02-18 19:24:07 -06:00
|
|
|
|
|
|
|
cm
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn t3() {
|
|
|
|
// Test lookup_byte_offset
|
|
|
|
let cm = init_code_map();
|
|
|
|
|
2015-07-01 22:37:52 -05:00
|
|
|
let fmabp1 = cm.lookup_byte_offset(BytePos(23));
|
2014-11-27 18:52:53 -06:00
|
|
|
assert_eq!(fmabp1.fm.name, "blork.rs");
|
2015-07-01 22:37:52 -05: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-18 19:24:07 -06:00
|
|
|
|
2015-07-01 22:37:52 -05:00
|
|
|
let fmabp2 = cm.lookup_byte_offset(BytePos(25));
|
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));
|
|
|
|
|
2015-07-01 22:37:52 -05:00
|
|
|
let cp2 = cm.bytepos_to_file_charpos(BytePos(25));
|
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));
|
|
|
|
|
2015-07-01 22:37:52 -05:00
|
|
|
let loc2 = cm.lookup_char_pos(BytePos(25));
|
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(),
|
2016-06-09 15:36:20 -05:00
|
|
|
None,
|
2014-05-25 05:17:19 -05:00
|
|
|
"fir€st €€€€ line.\nsecond line".to_string());
|
|
|
|
let fm2 = cm.new_filemap("blork2.rs".to_string(),
|
2016-06-09 15:36:20 -05:00
|
|
|
None,
|
2014-05-25 05:17:19 -05:00
|
|
|
"first line€€.\n€ second line".to_string());
|
2014-02-27 17:53:36 -06:00
|
|
|
|
|
|
|
fm1.next_line(BytePos(0));
|
2015-07-01 22:37:52 -05:00
|
|
|
fm1.next_line(BytePos(28));
|
|
|
|
fm2.next_line(fm2.start_pos);
|
|
|
|
fm2.next_line(fm2.start_pos + BytePos(20));
|
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);
|
2015-07-01 22:37:52 -05: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 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};
|
2015-04-30 03:23:50 -05:00
|
|
|
let file_lines = cm.span_to_lines(span).unwrap();
|
2014-03-03 04:44:43 -06:00
|
|
|
|
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-04-09 13:46:03 -05:00
|
|
|
assert_eq!(file_lines.lines[0].line_index, 1);
|
|
|
|
}
|
|
|
|
|
2016-04-20 13:56:01 -05:00
|
|
|
/// Given a string like " ~~~~~~~~~~~~ ", produces a span
|
2015-04-09 13:46:03 -05: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 13:56:01 -05:00
|
|
|
let left_index = selection.find('~').unwrap() as u32;
|
2015-12-13 06:12:47 -06:00
|
|
|
let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
|
2015-04-09 13:46:03 -05: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 13:56:01 -05:00
|
|
|
let selection = " \n ~~\n~~~\n~~~~~ \n \n";
|
2016-06-09 15:36:20 -05:00
|
|
|
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
2015-04-09 13:46:03 -05: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 03:23:50 -05:00
|
|
|
let lines = cm.span_to_lines(span).unwrap();
|
2015-04-09 13:46:03 -05: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 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
|
|
|
}
|
2015-11-24 16:05:27 -06: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
|
|
|
|
let cm = init_code_map();
|
|
|
|
let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
|
2016-11-17 08:04:36 -06:00
|
|
|
let format = ExpnFormat::MacroBang(keywords::Invalid.name());
|
2015-11-24 16:05:27 -06:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2016-09-19 14:31:56 -05:00
|
|
|
/// Test merging two spans on the same line
|
|
|
|
#[test]
|
|
|
|
fn span_merging() {
|
|
|
|
let cm = CodeMap::new();
|
|
|
|
let inputtext = "bbbb BB bb CCC\n";
|
|
|
|
let selection1 = " ~~ \n";
|
|
|
|
let selection2 = " ~~~\n";
|
|
|
|
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
|
|
|
let span1 = span_from_selection(inputtext, selection1);
|
|
|
|
let span2 = span_from_selection(inputtext, selection2);
|
|
|
|
|
|
|
|
if let Some(sp) = cm.merge_spans(span1, span2) {
|
|
|
|
let sstr = cm.span_to_expanded_string(sp);
|
|
|
|
assert_eq!(sstr, "blork.rs:1:6: 1:15\n`BB bb CCC`\n");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert!(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test failing to merge two spans on different lines
|
|
|
|
#[test]
|
|
|
|
fn span_merging_fail() {
|
|
|
|
let cm = CodeMap::new();
|
|
|
|
let inputtext = "bbbb BB\ncc CCC\n";
|
|
|
|
let selection1 = " ~~\n \n";
|
|
|
|
let selection2 = " \n ~~~\n";
|
|
|
|
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
|
|
|
let span1 = span_from_selection(inputtext, selection1);
|
|
|
|
let span2 = span_from_selection(inputtext, selection2);
|
|
|
|
|
2016-09-20 17:39:05 -05:00
|
|
|
assert!(cm.merge_spans(span1, span2).is_none());
|
2016-09-19 14:31:56 -05:00
|
|
|
}
|
|
|
|
|
2016-06-23 09:50:05 -05:00
|
|
|
/// Returns the span corresponding to the `n`th occurrence of
|
|
|
|
/// `substring` in `source_text`.
|
|
|
|
trait CodeMapExtension {
|
|
|
|
fn span_substr(&self,
|
|
|
|
file: &Rc<FileMap>,
|
|
|
|
source_text: &str,
|
|
|
|
substring: &str,
|
|
|
|
n: usize)
|
|
|
|
-> Span;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CodeMapExtension for CodeMap {
|
|
|
|
fn span_substr(&self,
|
|
|
|
file: &Rc<FileMap>,
|
|
|
|
source_text: &str,
|
|
|
|
substring: &str,
|
|
|
|
n: usize)
|
|
|
|
-> Span
|
|
|
|
{
|
|
|
|
println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
|
|
|
|
file.name, file.start_pos, substring, n);
|
|
|
|
let mut i = 0;
|
|
|
|
let mut hi = 0;
|
|
|
|
loop {
|
|
|
|
let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
|
|
|
|
panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
|
|
|
|
source_text, n, substring, i);
|
|
|
|
});
|
|
|
|
let lo = hi + offset;
|
|
|
|
hi = lo + substring.len();
|
|
|
|
if i == n {
|
|
|
|
let span = Span {
|
|
|
|
lo: BytePos(lo as u32 + file.start_pos.0),
|
|
|
|
hi: BytePos(hi as u32 + file.start_pos.0),
|
|
|
|
expn_id: NO_EXPANSION,
|
|
|
|
};
|
|
|
|
assert_eq!(&self.span_to_snippet(span).unwrap()[..],
|
|
|
|
substring);
|
|
|
|
return span;
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-24 16:05:27 -06:00
|
|
|
fn init_expansion_chain(cm: &CodeMap) -> Span {
|
|
|
|
// Creates an expansion chain containing two recursive calls
|
|
|
|
// root -> expA -> expA -> expB -> expB -> end
|
|
|
|
let root = Span { lo: BytePos(0), hi: BytePos(11), expn_id: NO_EXPANSION };
|
|
|
|
|
2016-11-17 08:04:36 -06:00
|
|
|
let format_root = ExpnFormat::MacroBang(keywords::Invalid.name());
|
2015-11-24 16:05:27 -06:00
|
|
|
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 };
|
|
|
|
|
2016-11-17 08:04:36 -06:00
|
|
|
let format_a = ExpnFormat::MacroBang(keywords::As.name());
|
2015-11-24 16:05:27 -06:00
|
|
|
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 };
|
|
|
|
|
2016-11-17 08:04:36 -06:00
|
|
|
let format_b = ExpnFormat::MacroBang(keywords::Box.name());
|
2015-11-24 16:05:27 -06:00
|
|
|
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 11:56:33 -06:00
|
|
|
}
|