diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 2a9d96fa861b1..c0306b8494be2 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -552,6 +552,10 @@ pub struct ExtCtxt<'a> { pub syntax_env: SyntaxEnv, pub recursion_count: usize, + + pub filename: Option, + pub mod_path_stack: Vec, + pub in_block: bool, } impl<'a> ExtCtxt<'a> { @@ -570,6 +574,10 @@ impl<'a> ExtCtxt<'a> { exported_macros: Vec::new(), syntax_env: env, recursion_count: 0, + + filename: None, + mod_path_stack: Vec::new(), + in_block: false, } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index e8e042c13217a..e8098cfff457d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1183,6 +1183,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { + fn fold_crate(&mut self, c: Crate) -> Crate { + self.cx.filename = Some(self.cx.parse_sess.codemap().span_to_filename(c.span)); + noop_fold_crate(c, self) + } + fn fold_expr(&mut self, expr: P) -> P { expand_expr(expr, self) } @@ -1192,7 +1197,27 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { } fn fold_item(&mut self, item: P) -> SmallVector> { - expand_item(item, self) + use std::mem::replace; + let result; + if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node { + if item.span.contains(inner) { + self.push_mod_path(item.ident, &item.attrs); + result = expand_item(item, self); + self.pop_mod_path(); + } else { + let filename = if inner != codemap::DUMMY_SP { + Some(self.cx.parse_sess.codemap().span_to_filename(inner)) + } else { None }; + let orig_filename = replace(&mut self.cx.filename, filename); + let orig_mod_path_stack = replace(&mut self.cx.mod_path_stack, Vec::new()); + result = expand_item(item, self); + self.cx.filename = orig_filename; + self.cx.mod_path_stack = orig_mod_path_stack; + } + } else { + result = expand_item(item, self); + } + result } fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { @@ -1204,7 +1229,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { } fn fold_block(&mut self, block: P) -> P { - expand_block(block, self) + let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true); + let result = expand_block(block, self); + self.cx.in_block = was_in_block; + result } fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm { @@ -1230,6 +1258,21 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { } } +impl<'a, 'b> MacroExpander<'a, 'b> { + fn push_mod_path(&mut self, id: Ident, attrs: &[ast::Attribute]) { + let default_path = id.name.as_str(); + let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") { + Some(d) => d, + None => default_path, + }; + self.cx.mod_path_stack.push(file_path) + } + + fn pop_mod_path(&mut self) { + self.cx.mod_path_stack.pop().unwrap(); + } +} + fn new_span(cx: &ExtCtxt, sp: Span) -> Span { /* this discards information in the case of macro-defining macros */ Span { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index c641c478a6bba..77bae4cb3f6c4 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -16,7 +16,7 @@ use ext::tt::macro_parser::{Success, Error, Failure}; use ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal}; use ext::tt::macro_parser::parse; use parse::lexer::new_tt_reader; -use parse::parser::Parser; +use parse::parser::{Parser, Restrictions}; use parse::token::{self, special_idents, gensym_ident, NtTT, Token}; use parse::token::Token::*; use print; @@ -195,6 +195,12 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, imported_from, rhs); let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr)); + p.filename = cx.filename.clone(); + p.mod_path_stack = cx.mod_path_stack.clone(); + p.restrictions = match cx.in_block { + true => Restrictions::NO_NONINLINE_MOD, + false => Restrictions::empty(), + }; p.check_unknown_macro_variable(); // Let the context choose how to interpret the result. // Weird, but useful for X-macros. diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b5d29a0d6dbaf..e166a36721967 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -273,6 +273,7 @@ pub struct Parser<'a> { /// extra detail when the same error is seen twice pub obsolete_set: HashSet, /// Used to determine the path to externally loaded source files + pub filename: Option, pub mod_path_stack: Vec, /// Stack of spans of open delimiters. Used for error message. pub open_braces: Vec, @@ -354,6 +355,9 @@ impl<'a> Parser<'a> { { let tok0 = rdr.real_token(); let span = tok0.sp; + let filename = if span != codemap::DUMMY_SP { + Some(sess.codemap().span_to_filename(span)) + } else { None }; let placeholder = TokenAndSpan { tok: token::Underscore, sp: span, @@ -382,6 +386,7 @@ impl<'a> Parser<'a> { quote_depth: 0, obsolete_set: HashSet::new(), mod_path_stack: Vec::new(), + filename: filename, open_braces: Vec::new(), owns_directory: true, root_module_name: None, @@ -5325,7 +5330,7 @@ impl<'a> Parser<'a> { id: ast::Ident, outer_attrs: &[ast::Attribute], id_sp: Span) -> PResult<'a, ModulePathSuccess> { - let mut prefix = PathBuf::from(&self.sess.codemap().span_to_filename(self.span)); + let mut prefix = PathBuf::from(self.filename.as_ref().unwrap()); prefix.pop(); let mut dir_path = prefix; for part in &self.mod_path_stack { diff --git a/src/test/compile-fail/macro-expanded-mod.rs b/src/test/compile-fail/macro-expanded-mod.rs new file mode 100644 index 0000000000000..8e631a64f7a27 --- /dev/null +++ b/src/test/compile-fail/macro-expanded-mod.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that macro-expanded non-inline modules behave correctly + +macro_rules! mod_decl { + ($i:ident) => { mod $i; } +} + +mod macro_expanded_mod_helper { + mod_decl!(foo); // This should search in the folder `macro_expanded_mod_helper` +} + +fn main() { + mod_decl!(foo); //~ ERROR Cannot declare a non-inline module inside a block +} diff --git a/src/test/compile-fail/macro_expanded_mod_helper/foo/bar.rs b/src/test/compile-fail/macro_expanded_mod_helper/foo/bar.rs new file mode 100644 index 0000000000000..3ec34362da559 --- /dev/null +++ b/src/test/compile-fail/macro_expanded_mod_helper/foo/bar.rs @@ -0,0 +1,11 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-test diff --git a/src/test/compile-fail/macro_expanded_mod_helper/foo/mod.rs b/src/test/compile-fail/macro_expanded_mod_helper/foo/mod.rs new file mode 100644 index 0000000000000..25fcf11ce175b --- /dev/null +++ b/src/test/compile-fail/macro_expanded_mod_helper/foo/mod.rs @@ -0,0 +1,13 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-test + +mod_decl!(bar);