Skip to content

Commit

Permalink
Fix file!(), line!() and column!() macros
Browse files Browse the repository at this point in the history
These used to return wrong results in case they were expanded inside compiler’s
iternal syntax sugar (closures, if-let) expansions

Fixes #26322
  • Loading branch information
nagisa committed Jun 16, 2015
1 parent 73fb19c commit 7ff55ea
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 72 deletions.
97 changes: 44 additions & 53 deletions src/libsyntax/codemap.rs
Expand Up @@ -17,7 +17,7 @@
//! within the CodeMap, which upon request can be converted to line and column
//! information, source code snippets, etc.

pub use self::MacroFormat::*;
pub use self::ExpnFormat::*;

use std::cell::RefCell;
use std::ops::{Add, Sub};
Expand Down Expand Up @@ -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.
#[derive(Clone, Copy, Hash, Debug)]
pub enum MacroFormat {
/// The source of expansion.
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
pub enum ExpnFormat {
/// e.g. #[derive(...)] <item>
MacroAttribute,
/// e.g. `format!()`
MacroBang,
/// Expansion performed by the compiler (libsyntax::expand).
/// Syntax sugar expansion performed by the compiler (libsyntax::expand).
CompilerExpansion,
}

Expand All @@ -248,7 +248,7 @@ pub struct NameAndSpan {
/// with this Span.
pub name: String,
/// The format with which the macro was invoked.
pub format: MacroFormat,
pub format: ExpnFormat,
/// Whether the macro is allowed to use #[unstable]/feature-gated
/// features internally without forcing the whole crate to opt-in
/// to them.
Expand All @@ -259,11 +259,11 @@ pub struct NameAndSpan {
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)]
pub struct ExpnInfo {
/// The location of the actual macro invocation, e.g. `let x =
/// foo!();`
/// The location of the actual macro invocation or syntax sugar , e.g.
/// `let x = foo!();` or `if let Some(y) = x {}`
///
/// This may recursively refer to other macro invocations, e.g. if
/// `foo!()` invoked `bar!()` internally, and there was an
Expand All @@ -272,12 +272,7 @@ pub struct ExpnInfo {
/// call_site span would have its own ExpnInfo, with the call_site
/// pointing to the `foo!` invocation.
pub call_site: Span,
/// 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 { ... }`.
/// Information about the expansion.
pub callee: NameAndSpan
}

Expand Down Expand Up @@ -677,7 +672,39 @@ impl CodeMap {

/// Lookup source information about a BytePos
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 {
Expand Down Expand Up @@ -877,42 +904,6 @@ impl CodeMap {
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 {
let mut expansions = self.expansions.borrow_mut();
expansions.push(expn_info);
Expand Down
41 changes: 25 additions & 16 deletions src/libsyntax/ext/base.rs
Expand Up @@ -13,7 +13,7 @@ pub use self::SyntaxExtension::*;
use ast;
use ast::Name;
use codemap;
use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION, CompilerExpansion};
use ext;
use ext::expand;
use ext::tt::macro_rules;
Expand Down Expand Up @@ -658,6 +658,8 @@ impl<'a> ExtCtxt<'a> {
})
}
pub fn backtrace(&self) -> ExpnId { self.backtrace }

/// Original span that caused the current exapnsion to happen.
pub fn original_span(&self) -> Span {
let mut expn_id = self.backtrace;
let mut call_site = None;
Expand All @@ -672,26 +674,33 @@ impl<'a> ExtCtxt<'a> {
}
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 call_site = None;
let mut last_macro = None;
let mut current_expn = None;
loop {
let expn_info = self.codemap().with_expn_info(expn_id, |ei| {
ei.map(|ei| (ei.call_site, ei.callee.name == "include"))
});
match expn_info {
None => break,
Some((cs, is_include)) => {
if is_include {
// Don't recurse into file using "include!".
break;
if self.codemap().with_expn_info(expn_id, |info| {
info.map_or(None, |i| {
if i.callee.name == "include" {
// Stop going up the backtrace once include! is encountered
return None;
}
call_site = Some(cs);
expn_id = cs.expn_id;
}
expn_id = i.call_site.expn_id;
current_expn = Some(i.call_site);
if i.callee.format != CompilerExpansion {
last_macro = Some(i.call_site)
}
return Some(());
})
}).is_none() {
break
}
}
call_site.expect("missing expansion backtrace")
last_macro.expect("missing expansion backtrace")
}

pub fn mod_push(&mut self, i: ast::Ident) { self.mod_path.push(i); }
Expand Down
6 changes: 3 additions & 3 deletions src/libsyntax/ext/source_util.rs
Expand Up @@ -34,7 +34,7 @@ pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'static> {
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);

base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32))
Expand All @@ -45,7 +45,7 @@ pub fn expand_column(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'static> {
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);

base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32))
Expand All @@ -58,7 +58,7 @@ pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'static> {
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 filename = token::intern_and_get_ident(&loc.file.name);
base::MacEager::expr(cx.expr_str(topmost, filename))
Expand Down

0 comments on commit 7ff55ea

Please sign in to comment.