Auto merge of #26347 - nagisa:macro-exp, r=nrc

r? @nrc, because breakage was caused by https://github.com/rust-lang/rust/pull/25318
This commit is contained in:
bors 2015-06-18 00:26:23 +00:00
commit 20d23d8e57
4 changed files with 106 additions and 72 deletions

View File

@ -17,7 +17,7 @@
//! within the CodeMap, which upon request can be converted to line and column //! within the CodeMap, which upon request can be converted to line and column
//! information, source code snippets, etc. //! information, source code snippets, etc.
pub use self::MacroFormat::*; pub use self::ExpnFormat::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
@ -228,17 +228,17 @@ pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
// _____________________________________________________________________________ // _____________________________________________________________________________
// MacroFormat, NameAndSpan, ExpnInfo, ExpnId // ExpnFormat, NameAndSpan, ExpnInfo, ExpnId
// //
/// The syntax with which a macro was invoked. /// The source of expansion.
#[derive(Clone, Copy, Hash, Debug)] #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
pub enum MacroFormat { pub enum ExpnFormat {
/// e.g. #[derive(...)] <item> /// e.g. #[derive(...)] <item>
MacroAttribute, MacroAttribute,
/// e.g. `format!()` /// e.g. `format!()`
MacroBang, MacroBang,
/// Expansion performed by the compiler (libsyntax::expand). /// Syntax sugar expansion performed by the compiler (libsyntax::expand).
CompilerExpansion, CompilerExpansion,
} }
@ -248,7 +248,7 @@ pub struct NameAndSpan {
/// with this Span. /// with this Span.
pub name: String, pub name: String,
/// The format with which the macro was invoked. /// The format with which the macro was invoked.
pub format: MacroFormat, pub format: ExpnFormat,
/// Whether the macro is allowed to use #[unstable]/feature-gated /// Whether the macro is allowed to use #[unstable]/feature-gated
/// features internally without forcing the whole crate to opt-in /// features internally without forcing the whole crate to opt-in
/// to them. /// to them.
@ -259,11 +259,11 @@ pub struct NameAndSpan {
pub span: Option<Span> pub span: Option<Span>
} }
/// Extra information for tracking macro expansion of spans /// Extra information for tracking spans of macro and syntax sugar expansion
#[derive(Hash, Debug)] #[derive(Hash, Debug)]
pub struct ExpnInfo { pub struct ExpnInfo {
/// The location of the actual macro invocation, e.g. `let x = /// The location of the actual macro invocation or syntax sugar , e.g.
/// foo!();` /// `let x = foo!();` or `if let Some(y) = x {}`
/// ///
/// This may recursively refer to other macro invocations, e.g. if /// This may recursively refer to other macro invocations, e.g. if
/// `foo!()` invoked `bar!()` internally, and there was an /// `foo!()` invoked `bar!()` internally, and there was an
@ -272,12 +272,7 @@ pub struct ExpnInfo {
/// call_site span would have its own ExpnInfo, with the call_site /// call_site span would have its own ExpnInfo, with the call_site
/// pointing to the `foo!` invocation. /// pointing to the `foo!` invocation.
pub call_site: Span, pub call_site: Span,
/// Information about the macro and its definition. /// Information about the expansion.
///
/// 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 { ... }`.
pub callee: NameAndSpan pub callee: NameAndSpan
} }
@ -677,7 +672,39 @@ impl CodeMap {
/// Lookup source information about a BytePos /// Lookup source information about a BytePos
pub fn lookup_char_pos(&self, pos: BytePos) -> Loc { pub fn lookup_char_pos(&self, pos: BytePos) -> Loc {
self.lookup_pos(pos) let FileMapAndLine {fm: f, line: a} = self.lookup_line(pos);
let line = a + 1; // Line numbers start at 1
let chpos = self.bytepos_to_file_charpos(pos);
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
}
}
fn lookup_line(&self, pos: BytePos) -> FileMapAndLine {
let idx = self.lookup_filemap_idx(pos);
let files = self.files.borrow();
let f = (*files)[idx].clone();
let mut a = 0;
{
let lines = f.lines.borrow();
let mut b = lines.len();
while b - a > 1 {
let m = (a + b) / 2;
if (*lines)[m] > pos { b = m; } else { a = m; }
}
}
FileMapAndLine {fm: f, line: a}
} }
pub fn lookup_char_pos_adj(&self, pos: BytePos) -> LocWithOpt { pub fn lookup_char_pos_adj(&self, pos: BytePos) -> LocWithOpt {
@ -877,42 +904,6 @@ impl CodeMap {
return a; return a;
} }
fn lookup_line(&self, pos: BytePos) -> FileMapAndLine {
let idx = self.lookup_filemap_idx(pos);
let files = self.files.borrow();
let f = (*files)[idx].clone();
let mut a = 0;
{
let lines = f.lines.borrow();
let mut b = lines.len();
while b - a > 1 {
let m = (a + b) / 2;
if (*lines)[m] > pos { b = m; } else { a = m; }
}
}
FileMapAndLine {fm: f, line: a}
}
fn lookup_pos(&self, pos: BytePos) -> Loc {
let FileMapAndLine {fm: f, line: a} = self.lookup_line(pos);
let line = a + 1; // Line numbers start at 1
let chpos = self.bytepos_to_file_charpos(pos);
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
}
}
pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId { pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
let mut expansions = self.expansions.borrow_mut(); let mut expansions = self.expansions.borrow_mut();
expansions.push(expn_info); expansions.push(expn_info);

View File

@ -13,7 +13,7 @@ pub use self::SyntaxExtension::*;
use ast; use ast;
use ast::Name; use ast::Name;
use codemap; use codemap;
use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION}; use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION, CompilerExpansion};
use ext; use ext;
use ext::expand; use ext::expand;
use ext::tt::macro_rules; use ext::tt::macro_rules;
@ -658,6 +658,8 @@ impl<'a> ExtCtxt<'a> {
}) })
} }
pub fn backtrace(&self) -> ExpnId { self.backtrace } pub fn backtrace(&self) -> ExpnId { self.backtrace }
/// Original span that caused the current exapnsion to happen.
pub fn original_span(&self) -> Span { pub fn original_span(&self) -> Span {
let mut expn_id = self.backtrace; let mut expn_id = self.backtrace;
let mut call_site = None; let mut call_site = None;
@ -672,26 +674,31 @@ impl<'a> ExtCtxt<'a> {
} }
call_site.expect("missing expansion backtrace") call_site.expect("missing expansion backtrace")
} }
pub fn original_span_in_file(&self) -> Span {
/// Returns span for the macro which originally caused the current expansion to happen.
///
/// Stops backtracing at include! boundary.
pub fn expansion_cause(&self) -> Span {
let mut expn_id = self.backtrace; let mut expn_id = self.backtrace;
let mut call_site = None; let mut last_macro = None;
loop { loop {
let expn_info = self.codemap().with_expn_info(expn_id, |ei| { if self.codemap().with_expn_info(expn_id, |info| {
ei.map(|ei| (ei.call_site, ei.callee.name == "include")) info.map_or(None, |i| {
}); if i.callee.name == "include" {
match expn_info { // Stop going up the backtrace once include! is encountered
None => break, return None;
Some((cs, is_include)) => {
if is_include {
// Don't recurse into file using "include!".
break;
} }
call_site = Some(cs); expn_id = i.call_site.expn_id;
expn_id = cs.expn_id; if i.callee.format != CompilerExpansion {
last_macro = Some(i.call_site)
}
return Some(());
})
}).is_none() {
break
} }
} }
} last_macro.expect("missing expansion backtrace")
call_site.expect("missing expansion backtrace")
} }
pub fn mod_push(&mut self, i: ast::Ident) { self.mod_path.push(i); } pub fn mod_push(&mut self, i: ast::Ident) { self.mod_path.push(i); }

View File

@ -34,7 +34,7 @@ pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'static> { -> Box<base::MacResult+'static> {
base::check_zero_tts(cx, sp, tts, "line!"); base::check_zero_tts(cx, sp, tts, "line!");
let topmost = cx.original_span_in_file(); let topmost = cx.expansion_cause();
let loc = cx.codemap().lookup_char_pos(topmost.lo); let loc = cx.codemap().lookup_char_pos(topmost.lo);
base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32)) base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32))
@ -45,7 +45,7 @@ pub fn expand_column(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'static> { -> Box<base::MacResult+'static> {
base::check_zero_tts(cx, sp, tts, "column!"); base::check_zero_tts(cx, sp, tts, "column!");
let topmost = cx.original_span_in_file(); let topmost = cx.expansion_cause();
let loc = cx.codemap().lookup_char_pos(topmost.lo); let loc = cx.codemap().lookup_char_pos(topmost.lo);
base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32)) base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32))
@ -58,7 +58,7 @@ pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'static> { -> Box<base::MacResult+'static> {
base::check_zero_tts(cx, sp, tts, "file!"); base::check_zero_tts(cx, sp, tts, "file!");
let topmost = cx.original_span_in_file(); let topmost = cx.expansion_cause();
let loc = cx.codemap().lookup_char_pos(topmost.lo); let loc = cx.codemap().lookup_char_pos(topmost.lo);
let filename = token::intern_and_get_ident(&loc.file.name); let filename = token::intern_and_get_ident(&loc.file.name);
base::MacEager::expr(cx.expr_str(topmost, filename)) base::MacEager::expr(cx.expr_str(topmost, filename))

View File

@ -0,0 +1,36 @@
// Copyright 2015 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.
macro_rules! columnline {
() => (
(column!(), line!())
)
}
macro_rules! indirectcolumnline {
() => (
(||{ columnline!() })()
)
}
fn main() {
let closure = || {
columnline!()
};
let iflet = if let Some(_) = Some(0) {
columnline!()
} else { (0, 0) };
let cl = columnline!();
assert_eq!(closure(), (8, 25));
assert_eq!(iflet, (8, 28));
assert_eq!(cl, (13, 30));
let indirect = indirectcolumnline!();
assert_eq!(indirect, (19, 34));
}