From c02d21033d3603f1dac3bfd6062c37f69a1681cd Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Tue, 23 Feb 2021 00:07:36 +0300
Subject: [PATCH 1/2] Add tests

---
 .../ui/proc-macro/auxiliary/test-macros.rs    | 22 +++++++++++----
 .../derive-helper-legacy-spurious.rs          | 13 +++++++++
 .../derive-helper-legacy-spurious.stderr      | 28 +++++++++++++++++++
 .../ui/proc-macro/inert-attribute-order.rs    | 23 +++++++++++++++
 .../proc-macro/inert-attribute-order.stdout   |  7 +++++
 5 files changed, 87 insertions(+), 6 deletions(-)
 create mode 100644 src/test/ui/proc-macro/derive-helper-legacy-spurious.rs
 create mode 100644 src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr
 create mode 100644 src/test/ui/proc-macro/inert-attribute-order.rs
 create mode 100644 src/test/ui/proc-macro/inert-attribute-order.stdout

diff --git a/src/test/ui/proc-macro/auxiliary/test-macros.rs b/src/test/ui/proc-macro/auxiliary/test-macros.rs
index 57a7ffa39ef00..a7ed4bc88250d 100644
--- a/src/test/ui/proc-macro/auxiliary/test-macros.rs
+++ b/src/test/ui/proc-macro/auxiliary/test-macros.rs
@@ -7,7 +7,7 @@
 #![crate_type = "proc-macro"]
 
 extern crate proc_macro;
-use proc_macro::TokenStream;
+use proc_macro::{TokenStream, TokenTree};
 
 // Macro that return empty token stream.
 
@@ -80,6 +80,10 @@ pub fn recollect_derive(input: TokenStream) -> TokenStream {
 // Macros that print their input in the original and re-collected forms (if they differ).
 
 fn print_helper(input: TokenStream, kind: &str) -> TokenStream {
+    print_helper_ext(input, kind, true)
+}
+
+fn print_helper_ext(input: TokenStream, kind: &str, debug: bool) -> TokenStream {
     let input_display = format!("{}", input);
     let input_debug = format!("{:#?}", input);
     let recollected = input.into_iter().collect();
@@ -89,9 +93,11 @@ fn print_helper(input: TokenStream, kind: &str) -> TokenStream {
     if recollected_display != input_display {
         println!("PRINT-{} RE-COLLECTED (DISPLAY): {}", kind, recollected_display);
     }
-    println!("PRINT-{} INPUT (DEBUG): {}", kind, input_debug);
-    if recollected_debug != input_debug {
-        println!("PRINT-{} RE-COLLECTED (DEBUG): {}", kind, recollected_debug);
+    if debug {
+        println!("PRINT-{} INPUT (DEBUG): {}", kind, input_debug);
+        if recollected_debug != input_debug {
+            println!("PRINT-{} RE-COLLECTED (DEBUG): {}", kind, recollected_debug);
+        }
     }
     recollected
 }
@@ -108,8 +114,12 @@ pub fn print_bang_consume(input: TokenStream) -> TokenStream {
 }
 
 #[proc_macro_attribute]
-pub fn print_attr(_: TokenStream, input: TokenStream) -> TokenStream {
-    print_helper(input, "ATTR")
+pub fn print_attr(args: TokenStream, input: TokenStream) -> TokenStream {
+    let debug = match &args.into_iter().collect::<Vec<_>>()[..] {
+        [TokenTree::Ident(ident)] if ident.to_string() == "nodebug" => false,
+        _ => true,
+    };
+    print_helper_ext(input, "ATTR", debug)
 }
 
 #[proc_macro_attribute]
diff --git a/src/test/ui/proc-macro/derive-helper-legacy-spurious.rs b/src/test/ui/proc-macro/derive-helper-legacy-spurious.rs
new file mode 100644
index 0000000000000..8180aab0caa13
--- /dev/null
+++ b/src/test/ui/proc-macro/derive-helper-legacy-spurious.rs
@@ -0,0 +1,13 @@
+// aux-build:test-macros.rs
+
+#![dummy] //~ ERROR cannot find attribute `dummy` in this scope
+
+#[macro_use]
+extern crate test_macros;
+
+#[derive(Empty)] //~ ERROR cannot determine resolution for the attribute macro `derive`
+#[empty_helper] //~ WARN derive helper attribute is used before it is introduced
+                //~| WARN this was previously accepted
+struct Foo {}
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr b/src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr
new file mode 100644
index 0000000000000..96754fed9933c
--- /dev/null
+++ b/src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr
@@ -0,0 +1,28 @@
+error: cannot find attribute `dummy` in this scope
+  --> $DIR/derive-helper-legacy-spurious.rs:3:4
+   |
+LL | #![dummy]
+   |    ^^^^^
+
+error: cannot determine resolution for the attribute macro `derive`
+  --> $DIR/derive-helper-legacy-spurious.rs:8:3
+   |
+LL | #[derive(Empty)]
+   |   ^^^^^^
+   |
+   = note: import resolution is stuck, try simplifying macro imports
+
+warning: derive helper attribute is used before it is introduced
+  --> $DIR/derive-helper-legacy-spurious.rs:9:3
+   |
+LL | #[derive(Empty)]
+   |          ----- the attribute is introduced here
+LL | #[empty_helper]
+   |   ^^^^^^^^^^^^
+   |
+   = note: `#[warn(legacy_derive_helpers)]` 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 #79202 <https://github.com/rust-lang/rust/issues/79202>
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
diff --git a/src/test/ui/proc-macro/inert-attribute-order.rs b/src/test/ui/proc-macro/inert-attribute-order.rs
new file mode 100644
index 0000000000000..f807967564116
--- /dev/null
+++ b/src/test/ui/proc-macro/inert-attribute-order.rs
@@ -0,0 +1,23 @@
+// Order of inert attributes, both built-in and custom is preserved during expansion.
+
+// check-pass
+// compile-flags: -Z span-debug
+// aux-build:test-macros.rs
+
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
+#[macro_use]
+extern crate test_macros;
+
+/// 1
+#[rustfmt::attr2]
+#[doc = "3"]
+#[print_attr(nodebug)]
+#[doc = "4"]
+#[rustfmt::attr5]
+/// 6
+#[print_attr(nodebug)]
+struct S;
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/inert-attribute-order.stdout b/src/test/ui/proc-macro/inert-attribute-order.stdout
new file mode 100644
index 0000000000000..7c0620b50b30f
--- /dev/null
+++ b/src/test/ui/proc-macro/inert-attribute-order.stdout
@@ -0,0 +1,7 @@
+PRINT-ATTR INPUT (DISPLAY): /// 1
+#[doc = "3"] #[doc = "4"] #[rustfmt :: attr5] /// 6
+#[print_attr(nodebug)] #[rustfmt :: attr2] struct S ;
+PRINT-ATTR RE-COLLECTED (DISPLAY): #[doc = " 1"] #[doc = "3"] #[doc = "4"] #[rustfmt :: attr5] #[doc = " 6"]
+#[print_attr(nodebug)] #[rustfmt :: attr2] struct S ;
+PRINT-ATTR INPUT (DISPLAY): #[doc = " 1"] #[doc = "3"] #[doc = "4"] #[doc = " 6"] #[rustfmt :: attr2]
+#[rustfmt :: attr5] struct S ;

From fc9d578bc5ada5162f49ed02a547cf87f2456a93 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Tue, 23 Feb 2021 00:54:09 +0300
Subject: [PATCH 2/2] expand: Preserve order of inert attributes during
 expansion

---
 compiler/rustc_expand/src/expand.rs           | 23 ++++---
 .../derive-helper-legacy-spurious.rs          |  3 +-
 .../derive-helper-legacy-spurious.stderr      | 10 +--
 .../proc-macro/inert-attribute-order.stdout   | 12 ++--
 .../proc-macro/issue-75930-derive-cfg.stdout  | 64 +++++++++----------
 5 files changed, 55 insertions(+), 57 deletions(-)

diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 5a4737842f0af..10c19ea105e4a 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -301,6 +301,8 @@ pub enum InvocationKind {
     },
     Attr {
         attr: ast::Attribute,
+        // Re-insertion position for inert attributes.
+        pos: usize,
         item: Annotatable,
         // Required for resolving derive helper attributes.
         derives: Vec<Path>,
@@ -690,7 +692,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 }
                 _ => unreachable!(),
             },
-            InvocationKind::Attr { attr, mut item, derives } => match ext {
+            InvocationKind::Attr { attr, pos, mut item, derives } => match ext {
                 SyntaxExtensionKind::Attr(expander) => {
                     self.gate_proc_macro_input(&item);
                     self.gate_proc_macro_attr_item(span, &item);
@@ -721,7 +723,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                                 ExpandResult::Retry(item) => {
                                     // Reassemble the original invocation for retrying.
                                     return ExpandResult::Retry(Invocation {
-                                        kind: InvocationKind::Attr { attr, item, derives },
+                                        kind: InvocationKind::Attr { attr, pos, item, derives },
                                         ..invoc
                                     });
                                 }
@@ -739,7 +741,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     if *mark_used {
                         self.cx.sess.mark_attr_used(&attr);
                     }
-                    item.visit_attrs(|attrs| attrs.push(attr));
+                    item.visit_attrs(|attrs| attrs.insert(pos, attr));
                     fragment_kind.expect_from_annotatables(iter::once(item))
                 }
                 _ => unreachable!(),
@@ -1000,17 +1002,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
 
     fn collect_attr(
         &mut self,
-        (attr, derives): (ast::Attribute, Vec<Path>),
+        (attr, pos, derives): (ast::Attribute, usize, Vec<Path>),
         item: Annotatable,
         kind: AstFragmentKind,
     ) -> AstFragment {
-        self.collect(kind, InvocationKind::Attr { attr, item, derives })
+        self.collect(kind, InvocationKind::Attr { attr, pos, item, derives })
     }
 
     /// If `item` is an attribute invocation, remove the attribute and return it together with
-    /// derives following it. We have to collect the derives in order to resolve legacy derive
-    /// helpers (helpers written before derives that introduce them).
-    fn take_first_attr(&mut self, item: &mut impl HasAttrs) -> Option<(ast::Attribute, Vec<Path>)> {
+    /// its position and derives following it. We have to collect the derives in order to resolve
+    /// legacy derive helpers (helpers written before derives that introduce them).
+    fn take_first_attr(
+        &mut self,
+        item: &mut impl HasAttrs,
+    ) -> Option<(ast::Attribute, usize, Vec<Path>)> {
         let mut attr = None;
 
         item.visit_attrs(|attrs| {
@@ -1033,7 +1038,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                         })
                         .collect();
 
-                    (attr, following_derives)
+                    (attr, attr_pos, following_derives)
                 })
         });
 
diff --git a/src/test/ui/proc-macro/derive-helper-legacy-spurious.rs b/src/test/ui/proc-macro/derive-helper-legacy-spurious.rs
index 8180aab0caa13..4a7e48eed46c3 100644
--- a/src/test/ui/proc-macro/derive-helper-legacy-spurious.rs
+++ b/src/test/ui/proc-macro/derive-helper-legacy-spurious.rs
@@ -6,8 +6,7 @@
 extern crate test_macros;
 
 #[derive(Empty)] //~ ERROR cannot determine resolution for the attribute macro `derive`
-#[empty_helper] //~ WARN derive helper attribute is used before it is introduced
-                //~| WARN this was previously accepted
+#[empty_helper] //~ ERROR cannot find attribute `empty_helper` in this scope
 struct Foo {}
 
 fn main() {}
diff --git a/src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr b/src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr
index 96754fed9933c..fd1ed8a3d0ff3 100644
--- a/src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr
+++ b/src/test/ui/proc-macro/derive-helper-legacy-spurious.stderr
@@ -12,17 +12,11 @@ LL | #[derive(Empty)]
    |
    = note: import resolution is stuck, try simplifying macro imports
 
-warning: derive helper attribute is used before it is introduced
+error: cannot find attribute `empty_helper` in this scope
   --> $DIR/derive-helper-legacy-spurious.rs:9:3
    |
-LL | #[derive(Empty)]
-   |          ----- the attribute is introduced here
 LL | #[empty_helper]
    |   ^^^^^^^^^^^^
-   |
-   = note: `#[warn(legacy_derive_helpers)]` 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 #79202 <https://github.com/rust-lang/rust/issues/79202>
 
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/proc-macro/inert-attribute-order.stdout b/src/test/ui/proc-macro/inert-attribute-order.stdout
index 7c0620b50b30f..cc215545952df 100644
--- a/src/test/ui/proc-macro/inert-attribute-order.stdout
+++ b/src/test/ui/proc-macro/inert-attribute-order.stdout
@@ -1,7 +1,7 @@
 PRINT-ATTR INPUT (DISPLAY): /// 1
-#[doc = "3"] #[doc = "4"] #[rustfmt :: attr5] /// 6
-#[print_attr(nodebug)] #[rustfmt :: attr2] struct S ;
-PRINT-ATTR RE-COLLECTED (DISPLAY): #[doc = " 1"] #[doc = "3"] #[doc = "4"] #[rustfmt :: attr5] #[doc = " 6"]
-#[print_attr(nodebug)] #[rustfmt :: attr2] struct S ;
-PRINT-ATTR INPUT (DISPLAY): #[doc = " 1"] #[doc = "3"] #[doc = "4"] #[doc = " 6"] #[rustfmt :: attr2]
-#[rustfmt :: attr5] struct S ;
+#[rustfmt :: attr2] #[doc = "3"] #[doc = "4"] #[rustfmt :: attr5] /// 6
+#[print_attr(nodebug)] struct S ;
+PRINT-ATTR RE-COLLECTED (DISPLAY): #[doc = " 1"] #[rustfmt :: attr2] #[doc = "3"] #[doc = "4"]
+#[rustfmt :: attr5] #[doc = " 6"] #[print_attr(nodebug)] struct S ;
+PRINT-ATTR INPUT (DISPLAY): #[doc = " 1"] #[rustfmt :: attr2] #[doc = "3"] #[doc = "4"]
+#[rustfmt :: attr5] #[doc = " 6"] struct S ;
diff --git a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout
index 19aa4dfb60e36..15e63c20eb9bc 100644
--- a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout
+++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout
@@ -1,4 +1,4 @@
-PRINT-ATTR INPUT (DISPLAY): #[allow(dead_code)] #[derive(Print)] #[print_helper(b)] #[print_helper(a)]
+PRINT-ATTR INPUT (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[derive(Print)] #[print_helper(b)]
 struct Foo < #[cfg(FALSE)] A, B >
 {
     #[cfg(FALSE)] first : String, #[cfg_attr(FALSE, deny(warnings))] second :
@@ -23,6 +23,31 @@ struct Foo < #[cfg(FALSE)] A, B >
      }], #[print_helper(d)] fourth : B
 }
 PRINT-ATTR INPUT (DEBUG): TokenStream [
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/issue-75930-derive-cfg.rs:16:1: 16:2 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "print_helper",
+                span: $DIR/issue-75930-derive-cfg.rs:16:3: 16:15 (#0),
+            },
+            Group {
+                delimiter: Parenthesis,
+                stream: TokenStream [
+                    Ident {
+                        ident: "a",
+                        span: $DIR/issue-75930-derive-cfg.rs:16:16: 16:17 (#0),
+                    },
+                ],
+                span: $DIR/issue-75930-derive-cfg.rs:16:15: 16:18 (#0),
+            },
+        ],
+        span: $DIR/issue-75930-derive-cfg.rs:16:2: 16:19 (#0),
+    },
     Punct {
         ch: '#',
         spacing: Alone,
@@ -98,31 +123,6 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         ],
         span: $DIR/issue-75930-derive-cfg.rs:21:2: 21:19 (#0),
     },
-    Punct {
-        ch: '#',
-        spacing: Alone,
-        span: $DIR/issue-75930-derive-cfg.rs:16:1: 16:2 (#0),
-    },
-    Group {
-        delimiter: Bracket,
-        stream: TokenStream [
-            Ident {
-                ident: "print_helper",
-                span: $DIR/issue-75930-derive-cfg.rs:16:3: 16:15 (#0),
-            },
-            Group {
-                delimiter: Parenthesis,
-                stream: TokenStream [
-                    Ident {
-                        ident: "a",
-                        span: $DIR/issue-75930-derive-cfg.rs:16:16: 16:17 (#0),
-                    },
-                ],
-                span: $DIR/issue-75930-derive-cfg.rs:16:15: 16:18 (#0),
-            },
-        ],
-        span: $DIR/issue-75930-derive-cfg.rs:16:2: 16:19 (#0),
-    },
     Ident {
         ident: "struct",
         span: $DIR/issue-75930-derive-cfg.rs:22:1: 22:7 (#0),
@@ -1194,7 +1194,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
         span: $DIR/issue-75930-derive-cfg.rs:22:32: 65:2 (#0),
     },
 ]
-PRINT-DERIVE INPUT (DISPLAY): #[allow(dead_code)] #[print_helper(b)] #[print_helper(a)] struct Foo < B >
+PRINT-DERIVE INPUT (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[print_helper(b)] struct Foo < B >
 {
     second : bool, third :
     [u8 ;
@@ -1217,14 +1217,14 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
         delimiter: Bracket,
         stream: TokenStream [
             Ident {
-                ident: "allow",
+                ident: "print_helper",
                 span: $DIR/issue-75930-derive-cfg.rs:22:1: 65:2 (#0),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
-                        ident: "dead_code",
+                        ident: "a",
                         span: $DIR/issue-75930-derive-cfg.rs:22:1: 65:2 (#0),
                     },
                 ],
@@ -1242,14 +1242,14 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
         delimiter: Bracket,
         stream: TokenStream [
             Ident {
-                ident: "print_helper",
+                ident: "allow",
                 span: $DIR/issue-75930-derive-cfg.rs:22:1: 65:2 (#0),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
-                        ident: "b",
+                        ident: "dead_code",
                         span: $DIR/issue-75930-derive-cfg.rs:22:1: 65:2 (#0),
                     },
                 ],
@@ -1274,7 +1274,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
-                        ident: "a",
+                        ident: "b",
                         span: $DIR/issue-75930-derive-cfg.rs:22:1: 65:2 (#0),
                     },
                 ],