diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 4a6841aedca1299a33bce3f8ede40a90e5025850..22f788e34ecff143f74cf7e8a46f75c95931559c 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -95,7 +95,7 @@ fn from_str(src: &str) -> Result { // notify the expansion info that it is unhygienic let mark = Mark::fresh(mark); mark.set_expn_info(expn_info); - let span = call_site.with_ctxt(call_site.ctxt().apply_mark(mark)); + let span = call_site.with_ctxt(SyntaxContext::empty().apply_mark(mark)); let stream = parse::parse_stream_from_source_str(name, src, sess, Some(span)); Ok(__internal::token_stream_wrap(stream)) }) diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 8df6458b72e183d0e173f633948c6fa4fbf62605..21b5bb6a97180d3fadf5d91f5f006db3e754326d 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -156,7 +156,7 @@ fn build_reduced_graph_for_use_tree(&mut self, // Disallow `use $crate;` if source.name == keywords::DollarCrate.name() && path.segments.len() == 1 { - let crate_root = self.resolve_crate_root(source.ctxt); + let crate_root = self.resolve_crate_root(source.ctxt, true); let crate_name = match crate_root.kind { ModuleKind::Def(_, name) => name, ModuleKind::Block(..) => unreachable!(), diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 33e57b2d180d6e2472d3bc8bfe813842bdcf4c60..2a97df920926093dac4d3bddb67b68ea576e9b25 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -42,7 +42,7 @@ use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap}; use syntax::codemap::{dummy_spanned, respan}; -use syntax::ext::hygiene::{Mark, SyntaxContext}; +use syntax::ext::hygiene::{Mark, MarkKind, SyntaxContext}; use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy}; use syntax::ext::base::SyntaxExtension; use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; @@ -1775,8 +1775,17 @@ fn resolve_ident_in_module(&mut self, result } - fn resolve_crate_root(&mut self, mut ctxt: SyntaxContext) -> Module<'a> { - let module = match ctxt.adjust(Mark::root()) { + fn resolve_crate_root(&mut self, mut ctxt: SyntaxContext, legacy: bool) -> Module<'a> { + let mark = if legacy { + // When resolving `$crate` from a `macro_rules!` invoked in a `macro`, + // we don't want to pretend that the `macro_rules!` definition is in the `macro` + // as described in `SyntaxContext::apply_mark`, so we ignore prepended modern marks. + ctxt.marks().into_iter().find(|&mark| mark.kind() != MarkKind::Modern) + } else { + ctxt = ctxt.modern(); + ctxt.adjust(Mark::root()) + }; + let module = match mark { Some(def) => self.macro_def_scope(def), None => return self.graph_root, }; @@ -2961,11 +2970,11 @@ fn resolve_path(&mut self, (i == 1 && name == keywords::Crate.name() && path[0].node.name == keywords::CrateRoot.name()) { // `::a::b` or `::crate::a::b` - module = Some(self.resolve_crate_root(ident.node.ctxt.modern())); + module = Some(self.resolve_crate_root(ident.node.ctxt, false)); continue } else if i == 0 && name == keywords::DollarCrate.name() { // `$crate::a::b` - module = Some(self.resolve_crate_root(ident.node.ctxt)); + module = Some(self.resolve_crate_root(ident.node.ctxt, true)); continue } else if i == 1 && self.session.features.borrow().extern_absolute_paths && path[0].node.name == keywords::CrateRoot.name() && diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 260a0cd7cd7337d8616c926795e4d285dd45451e..fe6bbf45d9fd05f97b405519b1503f8fe5d9f263 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -140,7 +140,7 @@ fn fold_path(&mut self, mut path: ast::Path) -> ast::Path { let ident = path.segments[0].identifier; if ident.name == keywords::DollarCrate.name() { path.segments[0].identifier.name = keywords::CrateRoot.name(); - let module = self.0.resolve_crate_root(ident.ctxt); + let module = self.0.resolve_crate_root(ident.ctxt, true); if !module.is_local() { let span = path.segments[0].span; path.segments.insert(1, match module.kind { diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index e249ecfca588e750a34eda2d353a8aceb737b083..4fd801707853de8f1080dc1a384dc87c0d5a59f2 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -620,7 +620,7 @@ fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Spa "crate root imports need to be explicitly named: \ `use crate as name;`".to_string())); } else { - Some(self.resolve_crate_root(source.ctxt.modern())) + Some(self.resolve_crate_root(source.ctxt.modern(), false)) } } else if extern_absolute_paths && !token::Ident(source).is_path_segment_keyword() { diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index ab6c3f7d62d76c17b0ea0e224f865d6d3acf07df..23c29e6c6dd7b800a13882feab0d5647b946f3cb 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -181,6 +181,33 @@ pub fn allocate_directly(expansion_info: ExpnInfo) -> Self { /// Extend a syntax context with a given mark pub fn apply_mark(self, mark: Mark) -> SyntaxContext { + if mark.kind() == MarkKind::Modern { + return self.apply_mark_internal(mark); + } + + let call_site_ctxt = + mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern(); + if call_site_ctxt == SyntaxContext::empty() { + return self.apply_mark_internal(mark); + } + + // Otherwise, `mark` is a macros 1.0 definition and the call site is in a + // macros 2.0 expansion, i.e. a macros 1.0 invocation is in a macros 2.0 definition. + // + // In this case, the tokens from the macros 1.0 definition inherit the hygiene + // at their invocation. That is, we pretend that the macros 1.0 definition + // was defined at its invocation (i.e. inside the macros 2.0 definition) + // so that the macros 2.0 definition remains hygienic. + // + // See the example at `test/run-pass/hygiene/legacy_interaction.rs`. + let mut ctxt = call_site_ctxt; + for mark in self.marks() { + ctxt = ctxt.apply_mark_internal(mark); + } + ctxt.apply_mark_internal(mark) + } + + fn apply_mark_internal(self, mark: Mark) -> SyntaxContext { HygieneData::with(|data| { let syntax_contexts = &mut data.syntax_contexts; let mut modern = syntax_contexts[self.0 as usize].modern; @@ -215,6 +242,18 @@ pub fn remove_mark(&mut self) -> Mark { }) } + pub fn marks(mut self) -> Vec { + HygieneData::with(|data| { + let mut marks = Vec::new(); + while self != SyntaxContext::empty() { + marks.push(data.syntax_contexts[self.0 as usize].outer_mark); + self = data.syntax_contexts[self.0 as usize].prev_ctxt; + } + marks.reverse(); + marks + }) + } + /// Adjust this context for resolution in a scope created by the given expansion. /// For example, consider the following three resolutions of `f`: /// ```rust diff --git a/src/test/run-pass/hygiene/auxiliary/legacy_interaction.rs b/src/test/run-pass/hygiene/auxiliary/legacy_interaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..c614ee4d57501b3eb31b6165f74420962c22e463 --- /dev/null +++ b/src/test/run-pass/hygiene/auxiliary/legacy_interaction.rs @@ -0,0 +1,19 @@ +// Copyright 2017 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-pretty pretty-printing is unhygienic + +#[macro_export] +macro_rules! m { + () => { + fn f() {} // (2) + g(); // (1) + } +} diff --git a/src/test/run-pass/hygiene/legacy_interaction.rs b/src/test/run-pass/hygiene/legacy_interaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..683a15b99aebea3116acb55803ee22c192ff0b8d --- /dev/null +++ b/src/test/run-pass/hygiene/legacy_interaction.rs @@ -0,0 +1,50 @@ +// Copyright 2017 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-pretty pretty-printing is unhygienic + +// aux-build:legacy_interaction.rs + +#![feature(decl_macro)] +#[allow(unused)] + +extern crate legacy_interaction; +// ^ defines +// ```rust +// macro_rules! m { +// () => { +// fn f() // (1) +// g() // (2) +// } +// } +// ```rust + +mod def_site { + // Unless this macro opts out of hygiene, it should resolve the same wherever it is invoked. + pub macro m2() { + ::legacy_interaction::m!(); + f(); // This should resolve to (1) + fn g() {} // We want (2) resolve to this, not to (4) + } +} + +mod use_site { + fn test() { + fn f() -> bool { true } // (3) + fn g() -> bool { true } // (4) + + ::def_site::m2!(); + + let _: bool = f(); // This should resolve to (3) + let _: bool = g(); // This should resolve to (4) + } +} + +fn main() {}