about summary refs log tree commit diff
path: root/src/libsyntax_pos
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-01-12 10:00:09 +0000
committerbors <bors@rust-lang.org>2018-01-12 10:00:09 +0000
commit0b90e4e8cd068910f604f3e1fb5d03cc01f1658f (patch)
tree272837310543b7e12169bc8d74974400634dfd57 /src/libsyntax_pos
parent73ac5d6a80f26c692f1e084b72d69637d7de2c8c (diff)
parentb766fa887dc0e4b923a38751fe4d570e35a75710 (diff)
downloadrust-0b90e4e8cd068910f604f3e1fb5d03cc01f1658f.tar.gz
rust-0b90e4e8cd068910f604f3e1fb5d03cc01f1658f.zip
Auto merge of #46551 - jseyfried:improve_legacy_modern_macro_interaction, r=nrc
macros: improve 1.0/2.0 interaction

This PR supports using unhygienic macros from hygienic macros without breaking the latter's hygiene.
```rust
// crate A:
#[macro_export]
macro_rules! m1 { () => {
    f(); // unhygienic: this macro needs `f` in its environment
    fn g() {} // (1) unhygienic: `g` is usable outside the macro definition
} }

// crate B:
#![feature(decl_macro)]
extern crate A;
use A::m1;

macro m2() {
    fn f() {} // (2)
    m1!(); // After this PR, `f()` in the expansion resolves to (2), not (3)
    g(); // After this PR, this resolves to `fn g() {}` from the above expansion.
         // Today, it is a resolution error.
}

fn test() {
    fn f() {} // (3)
    m2!(); // Today, `m2!()` can see (3) even though it should be hygienic.
    fn g() {} // Today, this conflicts with `fn g() {}` from the expansion, even though it should be hygienic.
}
```

Once this PR lands, you can make an existing unhygienic macro hygienic by wrapping it in a hygienic macro. There is an [example](https://github.com/rust-lang/rust/pull/46551/commits/b766fa887dc0e4b923a38751fe4d570e35a75710) of this in the tests.

r? @nrc
Diffstat (limited to 'src/libsyntax_pos')
-rw-r--r--src/libsyntax_pos/hygiene.rs39
1 files changed, 39 insertions, 0 deletions
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index 06a9306501c..b7fba9fe8df 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -188,6 +188,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;
@@ -222,6 +249,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`:
     ///