From 979dd77c5d88cd21f350da5235d198b97264ec68 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 21 Jul 2020 19:51:07 -0400 Subject: [PATCH] Add test for serializing hygiene *into* a proc-macro crate This is a very obscure corner case, and should never be hit in practice. --- src/librustc_metadata/rmeta/encoder.rs | 31 +++++++++++++-- .../ui/proc-macro/auxiliary/make-macro.rs | 8 +++- .../ui/proc-macro/auxiliary/meta-macro.rs | 3 ++ src/test/ui/proc-macro/meta-macro-hygiene.rs | 19 +++++++-- .../ui/proc-macro/meta-macro-hygiene.stdout | 39 +++++++++++++++---- src/test/ui/proc-macro/meta-macro.stdout | 4 +- 6 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index 6539c13d812..076ed679834 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -259,10 +259,33 @@ impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { let len = hi - lo; len.encode(self)?; - // FIXME: Once #69976 is merged, treat proc-macros normally - // Currently, we can't encode `SyntaxContextData` for proc-macro crates, - // since the `SyntaxContextData`/`ExpnData` might reference `DefIds` from - // dependencies (which are not currently loaded during decoding). + // Don't serialize any `SyntaxContext`s from a proc-macro crate, + // since we don't load proc-macro dependencies during serialization. + // This means that any hygiene information from macros used *within* + // a proc-macro crate (e.g. invoking a macro that expands to a proc-macro + // definition) will be lost. + // + // This can show up in two ways: + // + // 1. Any hygiene information associated with identifier of + // a proc macro (e.g. `#[proc_macro] pub fn $name`) will be lost. + // Since proc-macros can only be invoked from a different crate, + // real code should never need to care about this. + // + // 2. Using `Span::def_site` or `Span::mixed_site` will not + // include any hygiene information associated with the defintion + // site. This means that a proc-macro cannot emit a `$crate` + // identifier which resolves to one of its dependencies, + // which also should never come up in practice. + // + // Additionally, this affects `Span::parent`, and any other + // span inspection APIs that would otherwise allow traversing + // the `SyntaxContexts` associated with a span. + // + // None of these user-visible effects should result in any + // cross-crate inconsistencies (getting one behavior in the same + // crate, and a different behavior in another crate) due to the + // limited surface that proc-macros can expose. if self.is_proc_macro { SyntaxContext::root().encode(self)?; } else { diff --git a/src/test/ui/proc-macro/auxiliary/make-macro.rs b/src/test/ui/proc-macro/auxiliary/make-macro.rs index 2c21221fbb0..3c851b6de2a 100644 --- a/src/test/ui/proc-macro/auxiliary/make-macro.rs +++ b/src/test/ui/proc-macro/auxiliary/make-macro.rs @@ -6,7 +6,13 @@ macro_rules! make_it { #[proc_macro] pub fn $name(input: TokenStream) -> TokenStream { println!("Def site: {:?}", Span::def_site()); - input + println!("Input: {:?}", input); + let new: TokenStream = input.into_iter().map(|mut t| { + t.set_span(Span::def_site()); + t + }).collect(); + println!("Respanned: {:?}", new); + new } }; } diff --git a/src/test/ui/proc-macro/auxiliary/meta-macro.rs b/src/test/ui/proc-macro/auxiliary/meta-macro.rs index 5265c6533b4..0a9b9887d95 100644 --- a/src/test/ui/proc-macro/auxiliary/meta-macro.rs +++ b/src/test/ui/proc-macro/auxiliary/meta-macro.rs @@ -10,3 +10,6 @@ extern crate make_macro; use proc_macro::{TokenStream, Span}; make_macro::make_it!(print_def_site); + +#[proc_macro] +pub fn dummy(input: TokenStream) -> TokenStream { input } diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.rs b/src/test/ui/proc-macro/meta-macro-hygiene.rs index 449377aed99..5e84be74da8 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.rs +++ b/src/test/ui/proc-macro/meta-macro-hygiene.rs @@ -1,13 +1,26 @@ // aux-build:make-macro.rs // aux-build:meta-macro.rs // edition:2018 -// compile-flags: -Z span-debug -Z unpretty=expanded,hygiene +// compile-flags: -Z span-debug -Z unpretty=expanded,hygiene -Z macro-backtrace // check-pass // normalize-stdout-test "\d+#" -> "0#" -// ^ We don't care about symbol ids, so set them all to 0 +// +// We don't care about symbol ids, so we set them all to 0 // in the stdout extern crate meta_macro; +extern crate make_macro; + +macro_rules! produce_it { + () => { + // `print_def_site!` will respan the `$crate` identifier + // with `Span::def_site()`. This should cause it to resolve + // relative to `meta_macro`, *not* `make_macro` (despite + // the fact that that `print_def_site` is produced by + // a `macro_rules!` macro in `make_macro`). + meta_macro::print_def_site!($crate::dummy!()); + } +} fn main() { - meta_macro::print_def_site!(); + produce_it!(); } diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.stdout b/src/test/ui/proc-macro/meta-macro-hygiene.stdout index 95f89b46d05..cb8ca614a23 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.stdout +++ b/src/test/ui/proc-macro/meta-macro-hygiene.stdout @@ -1,4 +1,6 @@ -Def site: $DIR/auxiliary/make-macro.rs:7:9: 10:10 (#6) +Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#7) +Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:20:37: 20:43 (#6) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:20:43: 20:45 (#6) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:20:43: 20:45 (#6) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:20:45: 20:50 (#6) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:20:50: 20:51 (#6) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:20:51: 20:53 (#6) }] +Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#7) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#7) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#7) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#7) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#7) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#7) }] #![feature /* 0#0 */(prelude_import)] #[prelude_import /* 0#1 */] use std /* 0#1 */::prelude /* 0#1 */::v1 /* 0#1 */::*; @@ -7,12 +9,29 @@ extern crate std /* 0#1 */; // aux-build:make-macro.rs // aux-build:meta-macro.rs // edition:2018 -// compile-flags: -Z span-debug -Z unpretty=expanded,hygiene +// compile-flags: -Z span-debug -Z unpretty=expanded,hygiene -Z macro-backtrace // check-pass // normalize-stdout-test "\d+#" -> "0#" -// ^ We don't care about symbol ids, so set them all to 0 +// +// We don't care about symbol ids, so we set them all to 0 // in the stdout extern crate meta_macro /* 0#0 */; +extern crate make_macro /* 0#0 */; + +macro_rules! produce_it + /* + 0#0 + */ { + () => + { + meta_macro :: print_def_site ! ($ crate :: dummy ! ()) ; + // `print_def_site!` will respan the `$crate` identifier + // with `Span::def_site()`. This should cause it to resolve + // relative to `meta_macro`, *not* `make_macro` (despite + // the fact that that `print_def_site` is produced by + // a `macro_rules!` macro in `make_macro`). + } +} fn main /* 0#0 */() { } @@ -20,11 +39,13 @@ fn main /* 0#0 */() { } Expansions: 0: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Root 1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) -2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site") +2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "produce_it") 3: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "cfg_if") 4: parent: ExpnId(3), call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "$crate::cfg_if") 5: parent: ExpnId(4), call_site_ctxt: #5, def_site_ctxt: #0, kind: Macro(Bang, "$crate::cfg_if") 6: parent: ExpnId(5), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "features") +7: parent: ExpnId(2), call_site_ctxt: #6, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site") +8: parent: ExpnId(7), call_site_ctxt: #7, def_site_ctxt: #0, kind: Macro(Bang, "$crate::dummy") SyntaxContexts: #0: parent: #0, outer_mark: (ExpnId(0), Opaque) @@ -33,7 +54,11 @@ SyntaxContexts: #3: parent: #0, outer_mark: (ExpnId(6), SemiTransparent) #4: parent: #0, outer_mark: (ExpnId(3), SemiTransparent) #5: parent: #0, outer_mark: (ExpnId(4), SemiTransparent) -#6: parent: #0, outer_mark: (ExpnId(2), Opaque) -#7: parent: #0, outer_mark: (ExpnId(2), Transparent) -#8: parent: #0, outer_mark: (ExpnId(2), SemiTransparent) +#6: parent: #0, outer_mark: (ExpnId(2), SemiTransparent) +#7: parent: #0, outer_mark: (ExpnId(7), Opaque) +#8: parent: #6, outer_mark: (ExpnId(7), Transparent) +#9: parent: #0, outer_mark: (ExpnId(7), SemiTransparent) +#10: parent: #0, outer_mark: (ExpnId(8), Opaque) +#11: parent: #7, outer_mark: (ExpnId(8), Transparent) +#12: parent: #7, outer_mark: (ExpnId(8), SemiTransparent) */ diff --git a/src/test/ui/proc-macro/meta-macro.stdout b/src/test/ui/proc-macro/meta-macro.stdout index 006c659df0b..e4374dbb3fa 100644 --- a/src/test/ui/proc-macro/meta-macro.stdout +++ b/src/test/ui/proc-macro/meta-macro.stdout @@ -1 +1,3 @@ -Def site: $DIR/auxiliary/make-macro.rs:7:9: 10:10 (#6) +Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#6) +Input: TokenStream [] +Respanned: TokenStream []