about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJeffrey Seyfried <jeffrey.seyfried@gmail.com>2017-11-29 01:05:31 -0800
committerJeffrey Seyfried <jeffrey.seyfried@gmail.com>2017-12-13 13:33:03 -0800
commitd052d28d70b31fac942765da137f794835e6536e (patch)
treeb339c73c8f824f39bab9aa06dc785964f46e917d
parent3dfbc88a626625be01e112da11ec367e2fc71bb3 (diff)
downloadrust-d052d28d70b31fac942765da137f794835e6536e.tar.gz
rust-d052d28d70b31fac942765da137f794835e6536e.zip
Improve interaction between macros 2.0 and `macro_rules!`.
-rw-r--r--src/libproc_macro/lib.rs2
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs2
-rw-r--r--src/librustc_resolve/lib.rs19
-rw-r--r--src/librustc_resolve/macros.rs2
-rw-r--r--src/librustc_resolve/resolve_imports.rs2
-rw-r--r--src/libsyntax_pos/hygiene.rs39
-rw-r--r--src/test/run-pass/hygiene/auxiliary/legacy_interaction.rs19
-rw-r--r--src/test/run-pass/hygiene/legacy_interaction.rs50
8 files changed, 126 insertions, 9 deletions
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 4a6841aedca..22f788e34ec 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -95,7 +95,7 @@ impl FromStr for TokenStream {
             // 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 8df6458b72e..21b5bb6a971 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -156,7 +156,7 @@ impl<'a> Resolver<'a> {
 
                     // 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 33e57b2d180..2a97df92092 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -42,7 +42,7 @@ use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
 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 @@ impl<'a> Resolver<'a> {
         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 @@ impl<'a> Resolver<'a> {
                    (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 260a0cd7cd7..fe6bbf45d9f 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -140,7 +140,7 @@ impl<'a> base::Resolver for Resolver<'a> {
                 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 e249ecfca58..4fd80170785 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -620,7 +620,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                                          "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 ab6c3f7d62d..23c29e6c6dd 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -181,6 +181,33 @@ impl SyntaxContext {
 
     /// 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 @@ impl SyntaxContext {
         })
     }
 
+    pub fn marks(mut self) -> Vec<Mark> {
+        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 00000000000..c614ee4d575
--- /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 <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.
+
+// 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 00000000000..683a15b99ae
--- /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 <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.
+
+// 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() {}