Add test for serializing hygiene *into* a proc-macro crate

This is a very obscure corner case, and should never be hit in practice.
This commit is contained in:
Aaron Hill 2020-07-21 19:51:07 -04:00
parent 5cd1b5dd79
commit 979dd77c5d
No known key found for this signature in database
GPG key ID: B4087E510E98B164
6 changed files with 88 additions and 16 deletions

View file

@ -259,10 +259,33 @@ impl<'tcx> SpecializedEncoder<Span> 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 {

View file

@ -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
}
};
}

View file

@ -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 }

View file

@ -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!();
}

View file

@ -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)
*/

View file

@ -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 []