about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2021-03-14 16:55:59 -0400
committerAaron Hill <aa1ronham@gmail.com>2021-03-14 21:31:46 -0400
commitf190bc4f4736ae0aed9580a2b4ed16bc2d678d11 (patch)
tree14456a9d09d0d9506cc11d24b8f6191a9af9acc8
parentd6eaea1c8860adb5302d2fbaad409e36585ab217 (diff)
downloadrust-f190bc4f4736ae0aed9580a2b4ed16bc2d678d11.tar.gz
rust-f190bc4f4736ae0aed9580a2b4ed16bc2d678d11.zip
Introduce `proc_macro_back_compat` lint, and emit for `time-macros-impl`
Now that future-incompat-report support has landed in nightly Cargo, we
can start to make progress towards removing the various proc-macro
back-compat hacks that have accumulated in the compiler.

This PR introduces a new lint `proc_macro_back_compat`, which results in
a future-incompat-report entry being generated. All proc-macro
back-compat warnings will be grouped under this lint. Note that this
lint will never actually become a hard error - instead, we will remove
the special cases for various macros, which will cause older versions of
those crates to emit some other error.

I've added code to fire this lint for the `time-macros-impl` case. This
is the easiest case out of all of our current back-compat hacks - the
crate was renamed to `time-macros`, so seeing a filename with
`time-macros-impl` guarantees that an older version of the parent `time`
crate is in use.

When Cargo's future-incompat-report feature gets stabilized, affected
users will start to see future-incompat warnings when they build their
crates.
-rw-r--r--compiler/rustc_ast/src/token.rs50
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs70
-rw-r--r--compiler/rustc_lint/src/context.rs3
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs53
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs1
-rw-r--r--src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs6
-rw-r--r--src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr70
-rw-r--r--src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout18
8 files changed, 206 insertions, 65 deletions
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 71792acb37d..093f7f2668c 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -11,11 +11,9 @@ use crate::tokenstream::TokenTree;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
 use rustc_macros::HashStable_Generic;
-use rustc_span::hygiene::ExpnKind;
-use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym};
 use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{self, edition::Edition, FileName, RealFileName, Span, DUMMY_SP};
+use rustc_span::{self, edition::Edition, Span, DUMMY_SP};
 use std::borrow::Cow;
 use std::{fmt, mem};
 
@@ -813,52 +811,6 @@ impl Nonterminal {
         }
         false
     }
-
-    // See issue #74616 for details
-    pub fn ident_name_compatibility_hack(
-        &self,
-        orig_span: Span,
-        source_map: &SourceMap,
-    ) -> Option<(Ident, bool)> {
-        if let NtIdent(ident, is_raw) = self {
-            if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
-                let filename = source_map.span_to_filename(orig_span);
-                if let FileName::Real(RealFileName::Named(path)) = filename {
-                    let matches_prefix = |prefix, filename| {
-                        // Check for a path that ends with 'prefix*/src/<filename>'
-                        let mut iter = path.components().rev();
-                        iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename)
-                            && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
-                            && iter
-                                .next()
-                                .and_then(|p| p.as_os_str().to_str())
-                                .map_or(false, |p| p.starts_with(prefix))
-                    };
-
-                    if (macro_name == sym::impl_macros
-                        && matches_prefix("time-macros-impl", "lib.rs"))
-                        || (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"))
-                    {
-                        let snippet = source_map.span_to_snippet(orig_span);
-                        if snippet.as_deref() == Ok("$name") {
-                            return Some((*ident, *is_raw));
-                        }
-                    }
-
-                    if macro_name == sym::tuple_from_req
-                        && (matches_prefix("actix-web", "extract.rs")
-                            || matches_prefix("actori-web", "extract.rs"))
-                    {
-                        let snippet = source_map.span_to_snippet(orig_span);
-                        if snippet.as_deref() == Ok("$T") {
-                            return Some((*ident, *is_raw));
-                        }
-                    }
-                }
-            }
-        }
-        None
-    }
 }
 
 impl PartialEq for Nonterminal {
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index b6195d3bbc4..837fad90580 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -2,16 +2,21 @@ use crate::base::ExtCtxt;
 
 use rustc_ast as ast;
 use rustc_ast::token;
+use rustc_ast::token::Nonterminal;
+use rustc_ast::token::NtIdent;
 use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens};
 use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Diagnostic;
+use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
+use rustc_lint_defs::BuiltinLintDiagnostics;
 use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str};
 use rustc_session::parse::ParseSess;
+use rustc_span::hygiene::ExpnKind;
 use rustc_span::symbol::{self, kw, sym, Symbol};
-use rustc_span::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span};
+use rustc_span::{BytePos, FileName, MultiSpan, Pos, RealFileName, SourceFile, Span};
 
 use pm::bridge::{server, TokenTree};
 use pm::{Delimiter, Level, LineColumn, Spacing};
@@ -174,9 +179,7 @@ impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec<Self>)>
             }
 
             Interpolated(nt) => {
-                if let Some((name, is_raw)) =
-                    nt.ident_name_compatibility_hack(span, sess.source_map())
-                {
+                if let Some((name, is_raw)) = ident_name_compatibility_hack(&nt, span, sess) {
                     TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span))
                 } else {
                     let stream = nt_to_tokenstream(&nt, sess, CanSynthesizeMissingTokens::No);
@@ -711,3 +714,62 @@ impl server::Span for Rustc<'_> {
         self.sess.source_map().span_to_snippet(span).ok()
     }
 }
+
+// See issue #74616 for details
+fn ident_name_compatibility_hack(
+    nt: &Nonterminal,
+    orig_span: Span,
+    sess: &ParseSess,
+) -> Option<(rustc_span::symbol::Ident, bool)> {
+    if let NtIdent(ident, is_raw) = nt {
+        if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
+            let source_map = sess.source_map();
+            let filename = source_map.span_to_filename(orig_span);
+            if let FileName::Real(RealFileName::Named(path)) = filename {
+                let matches_prefix = |prefix, filename| {
+                    // Check for a path that ends with 'prefix*/src/<filename>'
+                    let mut iter = path.components().rev();
+                    iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename)
+                        && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
+                        && iter
+                            .next()
+                            .and_then(|p| p.as_os_str().to_str())
+                            .map_or(false, |p| p.starts_with(prefix))
+                };
+
+                let time_macros_impl =
+                    macro_name == sym::impl_macros && matches_prefix("time-macros-impl", "lib.rs");
+                if time_macros_impl
+                    || (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"))
+                {
+                    let snippet = source_map.span_to_snippet(orig_span);
+                    if snippet.as_deref() == Ok("$name") {
+                        if time_macros_impl {
+                            sess.buffer_lint_with_diagnostic(
+                                &PROC_MACRO_BACK_COMPAT,
+                                orig_span,
+                                ast::CRATE_NODE_ID,
+                                "using an old version of `time-macros-impl`",
+                                BuiltinLintDiagnostics::ProcMacroBackCompat(
+                                "the `time-macros-impl` crate will stop compiling in futures version of Rust. \
+                                Please update to the latest version of the `time` crate to avoid breakage".to_string())
+                            );
+                        }
+                        return Some((*ident, *is_raw));
+                    }
+                }
+
+                if macro_name == sym::tuple_from_req
+                    && (matches_prefix("actix-web", "extract.rs")
+                        || matches_prefix("actori-web", "extract.rs"))
+                {
+                    let snippet = source_map.span_to_snippet(orig_span);
+                    if snippet.as_deref() == Ok("$T") {
+                        return Some((*ident, *is_raw));
+                    }
+                }
+            }
+        }
+    }
+    None
+}
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 7d5577cdca6..42ead89ca4f 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -670,6 +670,9 @@ pub trait LintContext: Sized {
                         json
                     );
                 }
+                BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
+                    db.note(&note);
+                }
             }
             // Rewrap `db`, and pass control to the user.
             decorate(LintDiagnosticBuilder::new(db));
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index af48f6c2a5d..005c4f9f6ea 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -6,7 +6,7 @@
 //! compiler code, rather than using their own custom pass. Those
 //! lints are all available in `rustc_lint::builtin`.
 
-use crate::{declare_lint, declare_lint_pass};
+use crate::{declare_lint, declare_lint_pass, FutureBreakage};
 use rustc_span::edition::Edition;
 
 declare_lint! {
@@ -2955,6 +2955,7 @@ declare_lint_pass! {
         SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
         DISJOINT_CAPTURE_DROP_REORDER,
         LEGACY_DERIVE_HELPERS,
+        PROC_MACRO_BACK_COMPAT,
     ]
 }
 
@@ -3082,3 +3083,53 @@ declare_lint! {
         edition: None,
     };
 }
+
+declare_lint! {
+    /// The `proc_macro_back_compat` lint detects uses of old versions of certain
+    /// proc-macro crates, which have hardcoded workarounds in the compiler.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs-dependency)
+    ///
+    /// use time_macros_impl::impl_macros;
+    /// struct Foo;
+    /// impl_macros!(Foo);
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// warning: using an old version of `time-macros-impl`
+    ///   ::: $DIR/group-compat-hack.rs:27:5
+    ///    |
+    /// LL |     impl_macros!(Foo);
+    ///    |     ------------------ in this macro invocation
+    ///    |
+    ///    = note: `#[warn(proc_macro_back_compat)]` on by default
+    ///    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    ///    = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
+    ///    = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
+    ///    = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// Eventually, the backwards-compatibility hacks present in the compiler will be removed,
+    /// causing older versions of certain crates to stop compiling.
+    /// This is a [future-incompatible] lint to ease the transition to an error.
+    /// See [issue #83125] for more details.
+    ///
+    /// [issue #83125]: https://github.com/rust-lang/rust/issues/83125
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
+    pub PROC_MACRO_BACK_COMPAT,
+    Warn,
+    "detects usage of old versions of certain proc-macro crates",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #83125 <https://github.com/rust-lang/rust/issues/83125>",
+        edition: None,
+        future_breakage: Some(FutureBreakage {
+            date: None
+        })
+    };
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 4c7d3f6c8c0..400b367095e 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -266,6 +266,7 @@ pub enum BuiltinLintDiagnostics {
     PatternsInFnsWithoutBody(Span, Ident),
     LegacyDeriveHelpers(Span),
     ExternDepSpec(String, ExternDepSpec),
+    ProcMacroBackCompat(String),
 }
 
 /// Lints that are buffered up early on in the `Session` before the
diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs
index 652fabf34ac..7f3f5e36f50 100644
--- a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs
+++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.rs
@@ -24,7 +24,8 @@ mod no_version {
     }
 
     struct Foo;
-    impl_macros!(Foo);
+    impl_macros!(Foo); //~ WARN  using an old version
+                       //~| WARN this was previously
     arrays!(Foo);
     other!(Foo);
 }
@@ -40,7 +41,8 @@ mod with_version {
     }
 
     struct Foo;
-    impl_macros!(Foo);
+    impl_macros!(Foo); //~  WARN using an old version
+                       //~| WARN this was previously
     arrays!(Foo);
     other!(Foo);
 }
diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr
new file mode 100644
index 00000000000..9370440a635
--- /dev/null
+++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stderr
@@ -0,0 +1,70 @@
+warning: using an old version of `time-macros-impl`
+  --> $DIR/time-macros-impl/src/lib.rs:5:32
+   |
+LL |         #[my_macro] struct One($name);
+   |                                ^^^^^
+   | 
+  ::: $DIR/group-compat-hack.rs:27:5
+   |
+LL |     impl_macros!(Foo);
+   |     ------------------ in this macro invocation
+   |
+   = note: `#[warn(proc_macro_back_compat)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
+   = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
+   = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+warning: using an old version of `time-macros-impl`
+  --> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32
+   |
+LL |         #[my_macro] struct One($name);
+   |                                ^^^^^
+   | 
+  ::: $DIR/group-compat-hack.rs:44:5
+   |
+LL |     impl_macros!(Foo);
+   |     ------------------ in this macro invocation
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
+   = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
+   = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+warning: 2 warnings emitted
+
+Future incompatibility report: Future breakage date: None, diagnostic:
+warning: using an old version of `time-macros-impl`
+  --> $DIR/time-macros-impl/src/lib.rs:5:32
+   |
+LL |         #[my_macro] struct One($name);
+   |                                ^^^^^
+   | 
+  ::: $DIR/group-compat-hack.rs:27:5
+   |
+LL |     impl_macros!(Foo);
+   |     ------------------ in this macro invocation
+   |
+   = note: `#[warn(proc_macro_back_compat)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
+   = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
+   = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage date: None, diagnostic:
+warning: using an old version of `time-macros-impl`
+  --> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32
+   |
+LL |         #[my_macro] struct One($name);
+   |                                ^^^^^
+   | 
+  ::: $DIR/group-compat-hack.rs:44:5
+   |
+LL |     impl_macros!(Foo);
+   |     ------------------ in this macro invocation
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #83125 <https://github.com/rust-lang/rust/issues/83125>
+   = note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
+   = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
diff --git a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout
index c6b18ab674b..468cb511915 100644
--- a/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout
+++ b/src/test/ui/proc-macro/group-compat-hack/group-compat-hack.stdout
@@ -1,10 +1,10 @@
 Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl/src/lib.rs:5:21: 5:27 (#6) }, Ident { ident: "One", span: $DIR/time-macros-impl/src/lib.rs:5:28: 5:31 (#6) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:27:18: 27:21 (#0) }], span: $DIR/time-macros-impl/src/lib.rs:5:31: 5:38 (#6) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl/src/lib.rs:5:38: 5:39 (#6) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys/src/lib.rs:5:21: 5:27 (#10) }, Ident { ident: "Two", span: $DIR/js-sys/src/lib.rs:5:28: 5:31 (#10) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:28:13: 28:16 (#0) }], span: $DIR/js-sys/src/lib.rs:5:31: 5:38 (#10) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys/src/lib.rs:5:38: 5:39 (#10) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:22:25: 22:31 (#14) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:22:32: 22:37 (#14) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:29:12: 29:15 (#0) }], span: $DIR/group-compat-hack.rs:22:38: 22:43 (#14) }], span: $DIR/group-compat-hack.rs:22:37: 22:44 (#14) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:22:44: 22:45 (#14) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:21: 5:27 (#20) }, Ident { ident: "One", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:28: 5:31 (#20) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:43:18: 43:21 (#0) }], span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:31: 5:38 (#20) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:38: 5:39 (#20) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys-0.3.17/src/lib.rs:5:21: 5:27 (#24) }, Ident { ident: "Two", span: $DIR/js-sys-0.3.17/src/lib.rs:5:28: 5:31 (#24) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:44:13: 44:16 (#0) }], span: $DIR/js-sys-0.3.17/src/lib.rs:5:31: 5:38 (#24) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys-0.3.17/src/lib.rs:5:38: 5:39 (#24) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:38:25: 38:31 (#28) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:38:32: 38:37 (#28) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:45:12: 45:15 (#0) }], span: $DIR/group-compat-hack.rs:38:38: 38:43 (#28) }], span: $DIR/group-compat-hack.rs:38:37: 38:44 (#28) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:38:44: 38:45 (#28) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web/src/extract.rs:5:21: 5:27 (#33) }, Ident { ident: "Three", span: $DIR/actix-web/src/extract.rs:5:28: 5:33 (#33) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:52:21: 52:24 (#0) }], span: $DIR/actix-web/src/extract.rs:5:33: 5:37 (#33) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web/src/extract.rs:5:37: 5:38 (#33) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web-2.0.0/src/extract.rs:5:21: 5:27 (#38) }, Ident { ident: "Three", span: $DIR/actix-web-2.0.0/src/extract.rs:5:28: 5:33 (#38) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:59:21: 59:24 (#0) }], span: $DIR/actix-web-2.0.0/src/extract.rs:5:33: 5:37 (#38) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web-2.0.0/src/extract.rs:5:37: 5:38 (#38) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web/src/extract.rs:5:21: 5:27 (#43) }, Ident { ident: "Four", span: $DIR/actori-web/src/extract.rs:5:28: 5:32 (#43) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:66:21: 66:24 (#0) }], span: $DIR/actori-web/src/extract.rs:5:32: 5:36 (#43) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web/src/extract.rs:5:36: 5:37 (#43) }]
-Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web-2.0.0/src/extract.rs:5:21: 5:27 (#48) }, Ident { ident: "Four", span: $DIR/actori-web-2.0.0/src/extract.rs:5:28: 5:32 (#48) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:73:21: 73:24 (#0) }], span: $DIR/actori-web-2.0.0/src/extract.rs:5:32: 5:36 (#48) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web-2.0.0/src/extract.rs:5:36: 5:37 (#48) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys/src/lib.rs:5:21: 5:27 (#10) }, Ident { ident: "Two", span: $DIR/js-sys/src/lib.rs:5:28: 5:31 (#10) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:29:13: 29:16 (#0) }], span: $DIR/js-sys/src/lib.rs:5:31: 5:38 (#10) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys/src/lib.rs:5:38: 5:39 (#10) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:22:25: 22:31 (#14) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:22:32: 22:37 (#14) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:30:12: 30:15 (#0) }], span: $DIR/group-compat-hack.rs:22:38: 22:43 (#14) }], span: $DIR/group-compat-hack.rs:22:37: 22:44 (#14) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:22:44: 22:45 (#14) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:21: 5:27 (#20) }, Ident { ident: "One", span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:28: 5:31 (#20) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:44:18: 44:21 (#0) }], span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:31: 5:38 (#20) }, Punct { ch: ';', spacing: Alone, span: $DIR/time-macros-impl-0.1.0/src/lib.rs:5:38: 5:39 (#20) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/js-sys-0.3.17/src/lib.rs:5:21: 5:27 (#24) }, Ident { ident: "Two", span: $DIR/js-sys-0.3.17/src/lib.rs:5:28: 5:31 (#24) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:46:13: 46:16 (#0) }], span: $DIR/js-sys-0.3.17/src/lib.rs:5:31: 5:38 (#24) }, Punct { ch: ';', spacing: Alone, span: $DIR/js-sys-0.3.17/src/lib.rs:5:38: 5:39 (#24) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/group-compat-hack.rs:39:25: 39:31 (#28) }, Ident { ident: "Three", span: $DIR/group-compat-hack.rs:39:32: 39:37 (#28) }, Group { delimiter: Parenthesis, stream: TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:47:12: 47:15 (#0) }], span: $DIR/group-compat-hack.rs:39:38: 39:43 (#28) }], span: $DIR/group-compat-hack.rs:39:37: 39:44 (#28) }, Punct { ch: ';', spacing: Alone, span: $DIR/group-compat-hack.rs:39:44: 39:45 (#28) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web/src/extract.rs:5:21: 5:27 (#33) }, Ident { ident: "Three", span: $DIR/actix-web/src/extract.rs:5:28: 5:33 (#33) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:54:21: 54:24 (#0) }], span: $DIR/actix-web/src/extract.rs:5:33: 5:37 (#33) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web/src/extract.rs:5:37: 5:38 (#33) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actix-web-2.0.0/src/extract.rs:5:21: 5:27 (#38) }, Ident { ident: "Three", span: $DIR/actix-web-2.0.0/src/extract.rs:5:28: 5:33 (#38) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:61:21: 61:24 (#0) }], span: $DIR/actix-web-2.0.0/src/extract.rs:5:33: 5:37 (#38) }, Punct { ch: ';', spacing: Alone, span: $DIR/actix-web-2.0.0/src/extract.rs:5:37: 5:38 (#38) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web/src/extract.rs:5:21: 5:27 (#43) }, Ident { ident: "Four", span: $DIR/actori-web/src/extract.rs:5:28: 5:32 (#43) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:68:21: 68:24 (#0) }], span: $DIR/actori-web/src/extract.rs:5:32: 5:36 (#43) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web/src/extract.rs:5:36: 5:37 (#43) }]
+Called proc_macro_hack with TokenStream [Ident { ident: "struct", span: $DIR/actori-web-2.0.0/src/extract.rs:5:21: 5:27 (#48) }, Ident { ident: "Four", span: $DIR/actori-web-2.0.0/src/extract.rs:5:28: 5:32 (#48) }, Group { delimiter: Parenthesis, stream: TokenStream [Ident { ident: "Foo", span: $DIR/group-compat-hack.rs:75:21: 75:24 (#0) }], span: $DIR/actori-web-2.0.0/src/extract.rs:5:32: 5:36 (#48) }, Punct { ch: ';', spacing: Alone, span: $DIR/actori-web-2.0.0/src/extract.rs:5:36: 5:37 (#48) }]