diff options
| author | bors <bors@rust-lang.org> | 2018-01-12 10:00:09 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-01-12 10:00:09 +0000 |
| commit | 0b90e4e8cd068910f604f3e1fb5d03cc01f1658f (patch) | |
| tree | 272837310543b7e12169bc8d74974400634dfd57 /src/libsyntax_pos | |
| parent | 73ac5d6a80f26c692f1e084b72d69637d7de2c8c (diff) | |
| parent | b766fa887dc0e4b923a38751fe4d570e35a75710 (diff) | |
| download | rust-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.rs | 39 |
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`: /// |
