From 0357257b72b9019b1c24ffd37ebc163c3d1bc6cb Mon Sep 17 00:00:00 2001
From: David Tolnay <dtolnay@gmail.com>
Date: Mon, 14 Sep 2020 18:59:41 -0700
Subject: [PATCH 1/2] Allow path as value in name-value attribute

---
 compiler/rustc_parse/src/parser/attr.rs       |  8 ++-
 compiler/rustc_parse/src/parser/mod.rs        | 22 +++++--
 src/test/ui/attr-eq-token-tree.rs             |  2 +-
 src/test/ui/attr-eq-token-tree.stderr         |  2 +-
 src/test/ui/attributes/path-eq-path.rs        | 11 ++++
 src/test/ui/attributes/path-eq-path.stderr    | 14 +++++
 src/test/ui/macros/macro-attribute.rs         |  2 +-
 src/test/ui/macros/macro-attribute.stderr     |  2 +-
 src/test/ui/parser/attr-bad-meta-2.rs         |  2 +-
 src/test/ui/parser/attr-bad-meta-2.stderr     |  2 +-
 src/test/ui/proc-macro/attributes-included.rs |  1 +
 .../ui/proc-macro/attributes-included.stderr  |  2 +-
 .../auxiliary/attributes-included.rs          | 58 ++++++++++++++++++-
 13 files changed, 114 insertions(+), 14 deletions(-)
 create mode 100644 src/test/ui/attributes/path-eq-path.rs
 create mode 100644 src/test/ui/attributes/path-eq-path.stderr

diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 053b7e0b75fe4..e46c39bef04db 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -159,6 +159,7 @@ impl<'a> Parser<'a> {
     ///     PATH `{` TOKEN_STREAM `}`
     ///     PATH
     ///     PATH `=` UNSUFFIXED_LIT
+    ///     PATH `=` PATH
     /// The delimiters or `=` are still put into the resulting token stream.
     pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> {
         let item = match self.token.kind {
@@ -230,6 +231,11 @@ impl<'a> Parser<'a> {
 
     crate fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
         let lit = self.parse_lit()?;
+        self.require_unsuffixed(&lit);
+        Ok(lit)
+    }
+
+    crate fn require_unsuffixed(&self, lit: &ast::Lit) {
         debug!("checking if {:?} is unusuffixed", lit);
 
         if !lit.kind.is_unsuffixed() {
@@ -240,8 +246,6 @@ impl<'a> Parser<'a> {
                 )
                 .emit();
         }
-
-        Ok(lit)
     }
 
     /// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index da1c54e88b5e2..f9cb42d56767c 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -934,16 +934,22 @@ impl<'a> Parser<'a> {
                             is_interpolated_expr = true;
                         }
                     }
-                    let token_tree = if is_interpolated_expr {
+                    let token_stream: TokenStream = if is_interpolated_expr {
                         // We need to accept arbitrary interpolated expressions to continue
                         // supporting things like `doc = $expr` that work on stable.
                         // Non-literal interpolated expressions are rejected after expansion.
-                        self.parse_token_tree()
+                        self.parse_token_tree().into()
+                    } else if let Some(lit) = self.parse_opt_lit() {
+                        self.require_unsuffixed(&lit);
+                        lit.token_tree().into()
+                    } else if self.check(&token::ModSep) || self.token.ident().is_some() {
+                        self.collect_tokens_only(|this| this.parse_path(PathStyle::Mod))?
                     } else {
-                        self.parse_unsuffixed_lit()?.token_tree()
+                        let msg = "expected a literal or ::-separated path";
+                        return Err(self.struct_span_err(self.token.span, msg));
                     };
 
-                    MacArgs::Eq(eq_span, token_tree.into())
+                    MacArgs::Eq(eq_span, token_stream)
                 } else {
                     MacArgs::Empty
                 }
@@ -1254,6 +1260,14 @@ impl<'a> Parser<'a> {
         Ok((ret, Some(LazyTokenStream::new(lazy_impl))))
     }
 
+    fn collect_tokens_only<R>(
+        &mut self,
+        f: impl FnOnce(&mut Self) -> PResult<'a, R>,
+    ) -> PResult<'a, TokenStream> {
+        let (_ignored, tokens) = self.collect_tokens(f)?;
+        Ok(tokens)
+    }
+
     /// `::{` or `::*`
     fn is_import_coupler(&mut self) -> bool {
         self.check(&token::ModSep)
diff --git a/src/test/ui/attr-eq-token-tree.rs b/src/test/ui/attr-eq-token-tree.rs
index c301492b9e21a..d9359da76ec30 100644
--- a/src/test/ui/attr-eq-token-tree.rs
+++ b/src/test/ui/attr-eq-token-tree.rs
@@ -1,2 +1,2 @@
-#[my_attr = !] //~ ERROR unexpected token: `!`
+#[my_attr = !] //~ ERROR expected a literal or ::-separated path
 fn main() {}
diff --git a/src/test/ui/attr-eq-token-tree.stderr b/src/test/ui/attr-eq-token-tree.stderr
index bb37c2e0cc473..f4ad7f5a3944f 100644
--- a/src/test/ui/attr-eq-token-tree.stderr
+++ b/src/test/ui/attr-eq-token-tree.stderr
@@ -1,4 +1,4 @@
-error: unexpected token: `!`
+error: expected a literal or ::-separated path
   --> $DIR/attr-eq-token-tree.rs:1:13
    |
 LL | #[my_attr = !]
diff --git a/src/test/ui/attributes/path-eq-path.rs b/src/test/ui/attributes/path-eq-path.rs
new file mode 100644
index 0000000000000..b189eb249ee68
--- /dev/null
+++ b/src/test/ui/attributes/path-eq-path.rs
@@ -0,0 +1,11 @@
+#[cfg(any())]
+extern "C++" {
+    #[namespace = std::experimental]
+    type any;
+
+    #[rust = std::option::Option<T>]
+    //~^ ERROR expected one of `::` or `]`, found `<`
+    type optional;
+}
+
+fn main() {}
diff --git a/src/test/ui/attributes/path-eq-path.stderr b/src/test/ui/attributes/path-eq-path.stderr
new file mode 100644
index 0000000000000..82603ff27e17c
--- /dev/null
+++ b/src/test/ui/attributes/path-eq-path.stderr
@@ -0,0 +1,14 @@
+error: expected one of `::` or `]`, found `<`
+  --> $DIR/path-eq-path.rs:6:33
+   |
+LL | extern "C++" {
+   |              - while parsing this item list starting here
+...
+LL |     #[rust = std::option::Option<T>]
+   |                                 ^ expected one of `::` or `]`
+...
+LL | }
+   | - the item list ends here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/macros/macro-attribute.rs b/src/test/ui/macros/macro-attribute.rs
index f580dfa8e34ed..c6656a975d68d 100644
--- a/src/test/ui/macros/macro-attribute.rs
+++ b/src/test/ui/macros/macro-attribute.rs
@@ -1,2 +1,2 @@
-#[doc = $not_there] //~ ERROR unexpected token: `$`
+#[doc = $not_there] //~ ERROR expected a literal or ::-separated path
 fn main() { }
diff --git a/src/test/ui/macros/macro-attribute.stderr b/src/test/ui/macros/macro-attribute.stderr
index d28ce25341d3a..3a8a3abf2b883 100644
--- a/src/test/ui/macros/macro-attribute.stderr
+++ b/src/test/ui/macros/macro-attribute.stderr
@@ -1,4 +1,4 @@
-error: unexpected token: `$`
+error: expected a literal or ::-separated path
   --> $DIR/macro-attribute.rs:1:9
    |
 LL | #[doc = $not_there]
diff --git a/src/test/ui/parser/attr-bad-meta-2.rs b/src/test/ui/parser/attr-bad-meta-2.rs
index cefd3369742be..a4232e8924cc5 100644
--- a/src/test/ui/parser/attr-bad-meta-2.rs
+++ b/src/test/ui/parser/attr-bad-meta-2.rs
@@ -1,2 +1,2 @@
-#[path =] //~ ERROR unexpected token: `]`
+#[path =] //~ ERROR expected a literal or ::-separated path
 mod m {}
diff --git a/src/test/ui/parser/attr-bad-meta-2.stderr b/src/test/ui/parser/attr-bad-meta-2.stderr
index 2d772dae69125..9fb8f8a12289f 100644
--- a/src/test/ui/parser/attr-bad-meta-2.stderr
+++ b/src/test/ui/parser/attr-bad-meta-2.stderr
@@ -1,4 +1,4 @@
-error: unexpected token: `]`
+error: expected a literal or ::-separated path
   --> $DIR/attr-bad-meta-2.rs:1:9
    |
 LL | #[path =]
diff --git a/src/test/ui/proc-macro/attributes-included.rs b/src/test/ui/proc-macro/attributes-included.rs
index 95e8e10a3ece9..fa9ead029c273 100644
--- a/src/test/ui/proc-macro/attributes-included.rs
+++ b/src/test/ui/proc-macro/attributes-included.rs
@@ -13,6 +13,7 @@ use attributes_included::*;
 #[foo]
 #[inline]
 /// doc
+#[namespace = std::experimental]
 fn foo() {
     let a: i32 = "foo"; //~ WARN: unused variable
 }
diff --git a/src/test/ui/proc-macro/attributes-included.stderr b/src/test/ui/proc-macro/attributes-included.stderr
index 72c88d5d8b752..1ce12e9f688ea 100644
--- a/src/test/ui/proc-macro/attributes-included.stderr
+++ b/src/test/ui/proc-macro/attributes-included.stderr
@@ -1,5 +1,5 @@
 warning: unused variable: `a`
-  --> $DIR/attributes-included.rs:17:9
+  --> $DIR/attributes-included.rs:18:9
    |
 LL |     let a: i32 = "foo";
    |         ^ help: if this is intentional, prefix it with an underscore: `_a`
diff --git a/src/test/ui/proc-macro/auxiliary/attributes-included.rs b/src/test/ui/proc-macro/auxiliary/attributes-included.rs
index a5eb40b28dca4..05942d305c47a 100644
--- a/src/test/ui/proc-macro/auxiliary/attributes-included.rs
+++ b/src/test/ui/proc-macro/auxiliary/attributes-included.rs
@@ -6,20 +6,30 @@
 extern crate proc_macro;
 
 use proc_macro::{TokenStream, TokenTree, Delimiter, Literal, Spacing, Group};
+use std::iter;
 
 #[proc_macro_attribute]
 pub fn foo(attr: TokenStream, input: TokenStream) -> TokenStream {
     assert!(attr.is_empty());
-    let input = input.into_iter().collect::<Vec<_>>();
+    let mut input = input.into_iter().collect::<Vec<_>>();
+    let (namespace_start, namespace_end);
     {
         let mut cursor = &input[..];
         assert_inline(&mut cursor);
         assert_doc(&mut cursor);
         assert_inline(&mut cursor);
         assert_doc(&mut cursor);
+
+        // Splice out the #[namespace = ...] attribute as it is an inert
+        // attribute, not a proc macro.
+        namespace_start = input.len() - cursor.len();
+        assert_namespace(&mut cursor);
+        namespace_end = input.len() - cursor.len();
+
         assert_foo(&mut cursor);
         assert!(cursor.is_empty());
     }
+    input.splice(namespace_start..namespace_end, iter::empty());
     fold_stream(input.into_iter().collect())
 }
 
@@ -34,6 +44,7 @@ pub fn bar(attr: TokenStream, input: TokenStream) -> TokenStream {
         assert_invoc(&mut cursor);
         assert_inline(&mut cursor);
         assert_doc(&mut cursor);
+        assert_namespace(&mut cursor);
         assert_foo(&mut cursor);
         assert!(cursor.is_empty());
     }
@@ -93,6 +104,51 @@ fn assert_doc(slice: &mut &[TokenTree]) {
     *slice = &slice[2..];
 }
 
+fn assert_namespace(slice: &mut &[TokenTree]) {
+    match &slice[0] {
+        TokenTree::Punct(tt) => assert_eq!(tt.as_char(), '#'),
+        _ => panic!("expected `#`"),
+    }
+    let inner = match &slice[1] {
+        TokenTree::Group(tt) => tt.stream().into_iter().collect::<Vec<_>>(),
+        _ => panic!("expected brackets"),
+    };
+    if inner.len() != 6 {
+        panic!("expected 6 tokens in namespace attr")
+    }
+    match &inner[0] {
+        TokenTree::Ident(tt) => assert_eq!("namespace", tt.to_string()),
+        _ => panic!("expected `namespace`"),
+    }
+    match &inner[1] {
+        TokenTree::Punct(tt) => assert_eq!(tt.as_char(), '='),
+        _ => panic!("expected `=`"),
+    }
+    match &inner[2] {
+        TokenTree::Ident(tt) => assert_eq!("std", tt.to_string()),
+        _ => panic!("expected `std`"),
+    }
+    match &inner[3] {
+        TokenTree::Punct(tt) => {
+            assert_eq!(tt.as_char(), ':');
+            assert_eq!(tt.spacing(), Spacing::Joint);
+        }
+        _ => panic!("expected `:`"),
+    }
+    match &inner[4] {
+        TokenTree::Punct(tt) => {
+            assert_eq!(tt.as_char(), ':');
+            assert_eq!(tt.spacing(), Spacing::Alone);
+        }
+        _ => panic!("expected `:`"),
+    }
+    match &inner[5] {
+        TokenTree::Ident(tt) => assert_eq!("experimental", tt.to_string()),
+        _ => panic!("expected `experimental`"),
+    }
+    *slice = &slice[2..];
+}
+
 fn assert_invoc(slice: &mut &[TokenTree]) {
     match &slice[0] {
         TokenTree::Punct(tt) => assert_eq!(tt.as_char(), '#'),

From 543c10af1f474a0c26d3309148c40c9eaf640155 Mon Sep 17 00:00:00 2001
From: David Tolnay <dtolnay@gmail.com>
Date: Mon, 2 Nov 2020 19:58:48 -0800
Subject: [PATCH 2/2] Resolve conflict with PR 77255

---
 compiler/rustc_parse/src/parser/mod.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index f9cb42d56767c..78fe0794f4acf 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -944,6 +944,8 @@ impl<'a> Parser<'a> {
                         lit.token_tree().into()
                     } else if self.check(&token::ModSep) || self.token.ident().is_some() {
                         self.collect_tokens_only(|this| this.parse_path(PathStyle::Mod))?
+                            .expect("parse_path cannot parse empty path")
+                            .create_token_stream()
                     } else {
                         let msg = "expected a literal or ::-separated path";
                         return Err(self.struct_span_err(self.token.span, msg));
@@ -1263,7 +1265,7 @@ impl<'a> Parser<'a> {
     fn collect_tokens_only<R>(
         &mut self,
         f: impl FnOnce(&mut Self) -> PResult<'a, R>,
-    ) -> PResult<'a, TokenStream> {
+    ) -> PResult<'a, Option<LazyTokenStream>> {
         let (_ignored, tokens) = self.collect_tokens(f)?;
         Ok(tokens)
     }