Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix file!(), line!() and column!() macros #26347

Merged
merged 3 commits into from
Jun 18, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 44 additions & 53 deletions src/libsyntax/codemap.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -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,
}

@@ -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.
@@ -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
@@ -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
}

@@ -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 {
@@ -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);
39 changes: 23 additions & 16 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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;
@@ -672,26 +674,31 @@ 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;
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;
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); }
6 changes: 3 additions & 3 deletions src/libsyntax/ext/source_util.rs
Original file line number Diff line number Diff line change
@@ -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))
@@ -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))
@@ -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))
36 changes: 36 additions & 0 deletions src/test/run-pass/issue-26322.rs
Original file line number Diff line number Diff line change
@@ -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));
}