From c6bbb95ce26e75214003190b643f5098e39b6428 Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Wed, 26 Mar 2014 16:14:07 -0700
Subject: [PATCH] syntax: Accept meta matchers in macros

This removes the `attr` matcher and adds a `meta` matcher. The previous `attr`
matcher is now ambiguous because it doesn't disambiguate whether it means inner
attribute or outer attribute.

The new behavior can still be achieved by taking an argument of the form
`#[$foo:meta]` (the brackets are part of the macro pattern).

Closes #13067
---
 src/libstd/comm/mod.rs                        |  6 +--
 src/libstd/io/test.rs                         |  6 +--
 src/libsyntax/ext/tt/macro_parser.rs          |  2 +-
 src/libsyntax/parse/attr.rs                   | 19 ++++----
 src/libsyntax/parse/token.rs                  |  8 ++--
 .../compile-fail/macro-inner-attributes.rs    |  4 +-
 .../compile-fail/macro-outer-attributes.rs    |  4 +-
 src/test/run-pass/macro-attributes.rs         |  4 +-
 src/test/run-pass/macro-meta-items.rs         | 43 +++++++++++++++++++
 9 files changed, 68 insertions(+), 28 deletions(-)
 create mode 100644 src/test/run-pass/macro-meta-items.rs

diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs
index 94e3d5ce2d3..ef8894a258c 100644
--- a/src/libstd/comm/mod.rs
+++ b/src/libstd/comm/mod.rs
@@ -252,7 +252,7 @@ use sync::arc::UnsafeArc;
 pub use comm::select::{Select, Handle};
 
 macro_rules! test (
-    { fn $name:ident() $b:block $($a:attr)*} => (
+    { fn $name:ident() $b:block $(#[$a:meta])*} => (
         mod $name {
             #[allow(unused_imports)];
 
@@ -265,8 +265,8 @@ macro_rules! test (
 
             fn f() $b
 
-            $($a)* #[test] fn uv() { f() }
-            $($a)* #[test] fn native() {
+            $(#[$a])* #[test] fn uv() { f() }
+            $(#[$a])* #[test] fn native() {
                 use native;
                 let (tx, rx) = channel();
                 native::task::spawn(proc() { tx.send(f()) });
diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs
index 9eeaf4635a4..6e987869238 100644
--- a/src/libstd/io/test.rs
+++ b/src/libstd/io/test.rs
@@ -19,7 +19,7 @@ use std::io::net::ip::*;
 use sync::atomics::{AtomicUint, INIT_ATOMIC_UINT, Relaxed};
 
 macro_rules! iotest (
-    { fn $name:ident() $b:block $($a:attr)* } => (
+    { fn $name:ident() $b:block $(#[$a:meta])* } => (
         mod $name {
             #[allow(unused_imports)];
 
@@ -43,8 +43,8 @@ macro_rules! iotest (
 
             fn f() $b
 
-            $($a)* #[test] fn green() { f() }
-            $($a)* #[test] fn native() {
+            $(#[$a])* #[test] fn green() { f() }
+            $(#[$a])* #[test] fn native() {
                 use native;
                 let (tx, rx) = channel();
                 native::task::spawn(proc() { tx.send(f()) });
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index aa4f192f779..0d90093a40f 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -441,7 +441,7 @@ pub fn parse_nt(p: &mut Parser, name: &str) -> Nonterminal {
       "path" => {
         token::NtPath(~p.parse_path(LifetimeAndTypesWithoutColons).path)
       }
-      "attr" => token::NtAttr(@p.parse_attribute(false)),
+      "meta" => token::NtMeta(p.parse_meta_item()),
       "tt" => {
         p.quote_depth += 1u; //but in theory, non-quoted tts might be useful
         let res = token::NtTT(@p.parse_token_tree());
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 8f7fb5749a1..b569bb28a7d 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -34,9 +34,6 @@ impl<'a> ParserAttr for Parser<'a> {
             debug!("parse_outer_attributes: self.token={:?}",
                    self.token);
             match self.token {
-              token::INTERPOLATED(token::NtAttr(..)) => {
-                attrs.push(self.parse_attribute(false));
-              }
               token::POUND => {
                 attrs.push(self.parse_attribute(false));
               }
@@ -66,11 +63,6 @@ impl<'a> ParserAttr for Parser<'a> {
         debug!("parse_attributes: permit_inner={:?} self.token={:?}",
                permit_inner, self.token);
         let (span, value, mut style) = match self.token {
-            INTERPOLATED(token::NtAttr(attr)) => {
-                assert!(attr.node.style == ast::AttrOuter);
-                self.bump();
-                (attr.span, attr.node.value, ast::AttrOuter)
-            }
             token::POUND => {
                 let lo = self.span.lo;
                 self.bump();
@@ -133,9 +125,6 @@ impl<'a> ParserAttr for Parser<'a> {
         let mut next_outer_attrs: Vec<ast::Attribute> = Vec::new();
         loop {
             let attr = match self.token {
-                token::INTERPOLATED(token::NtAttr(..)) => {
-                    self.parse_attribute(true)
-                }
                 token::POUND => {
                     self.parse_attribute(true)
                 }
@@ -163,6 +152,14 @@ impl<'a> ParserAttr for Parser<'a> {
     // | IDENT = lit
     // | IDENT meta_seq
     fn parse_meta_item(&mut self) -> @ast::MetaItem {
+        match self.token {
+            token::INTERPOLATED(token::NtMeta(e)) => {
+                self.bump();
+                return e
+            }
+            _ => {}
+        }
+
         let lo = self.span.lo;
         let ident = self.parse_ident();
         let name = self.id_to_interned_str(ident);
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 7bb920bdf56..e3eb1f1f711 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -113,7 +113,7 @@ pub enum Nonterminal {
     NtExpr(@ast::Expr),
     NtTy(  P<ast::Ty>),
     NtIdent(~ast::Ident, bool),
-    NtAttr(@ast::Attribute), // #[foo]
+    NtMeta(@ast::MetaItem), // stuff inside brackets for attributes
     NtPath(~ast::Path),
     NtTT(  @ast::TokenTree), // needs @ed to break a circularity
     NtMatchers(Vec<ast::Matcher> )
@@ -129,7 +129,7 @@ impl fmt::Show for Nonterminal {
             NtExpr(..) => f.pad("NtExpr(..)"),
             NtTy(..) => f.pad("NtTy(..)"),
             NtIdent(..) => f.pad("NtIdent(..)"),
-            NtAttr(..) => f.pad("NtAttr(..)"),
+            NtMeta(..) => f.pad("NtMeta(..)"),
             NtPath(..) => f.pad("NtPath(..)"),
             NtTT(..) => f.pad("NtTT(..)"),
             NtMatchers(..) => f.pad("NtMatchers(..)"),
@@ -241,7 +241,7 @@ pub fn to_str(t: &Token) -> ~str {
       INTERPOLATED(ref nt) => {
         match nt {
             &NtExpr(e) => ::print::pprust::expr_to_str(e),
-            &NtAttr(e) => ::print::pprust::attribute_to_str(e),
+            &NtMeta(e) => ::print::pprust::meta_item_to_str(e),
             _ => {
                 ~"an interpolated " +
                     match *nt {
@@ -249,7 +249,7 @@ pub fn to_str(t: &Token) -> ~str {
                         NtBlock(..) => ~"block",
                         NtStmt(..) => ~"statement",
                         NtPat(..) => ~"pattern",
-                        NtAttr(..) => fail!("should have been handled"),
+                        NtMeta(..) => fail!("should have been handled"),
                         NtExpr(..) => fail!("should have been handled above"),
                         NtTy(..) => ~"type",
                         NtIdent(..) => ~"identifier",
diff --git a/src/test/compile-fail/macro-inner-attributes.rs b/src/test/compile-fail/macro-inner-attributes.rs
index 104438848c4..753ab3d0368 100644
--- a/src/test/compile-fail/macro-inner-attributes.rs
+++ b/src/test/compile-fail/macro-inner-attributes.rs
@@ -11,8 +11,8 @@
 #[feature(macro_rules)];
 
 macro_rules! test ( ($nm:ident,
-                     $a:attr,
-                     $i:item) => (mod $nm { $a; $i }); )
+                     #[$a:meta],
+                     $i:item) => (mod $nm { #![$a] $i }); )
 
 test!(a,
       #[cfg(qux)],
diff --git a/src/test/compile-fail/macro-outer-attributes.rs b/src/test/compile-fail/macro-outer-attributes.rs
index 6bd184ce6a6..a4ded8e20b9 100644
--- a/src/test/compile-fail/macro-outer-attributes.rs
+++ b/src/test/compile-fail/macro-outer-attributes.rs
@@ -11,8 +11,8 @@
 #[feature(macro_rules)];
 
 macro_rules! test ( ($nm:ident,
-                     $a:attr,
-                     $i:item) => (mod $nm { $a $i }); )
+                     #[$a:meta],
+                     $i:item) => (mod $nm { #[$a] $i }); )
 
 test!(a,
       #[cfg(qux)],
diff --git a/src/test/run-pass/macro-attributes.rs b/src/test/run-pass/macro-attributes.rs
index 58095137428..076305002e4 100644
--- a/src/test/run-pass/macro-attributes.rs
+++ b/src/test/run-pass/macro-attributes.rs
@@ -13,11 +13,11 @@
 #[feature(macro_rules)];
 
 macro_rules! compiles_fine {
-    ($at:attr) => {
+    (#[$at:meta]) => {
         // test that the different types of attributes work
         #[attribute]
         /// Documentation!
-        $at
+        #[$at]
 
         // check that the attributes are recognised by requiring this
         // to be removed to avoid a compile error
diff --git a/src/test/run-pass/macro-meta-items.rs b/src/test/run-pass/macro-meta-items.rs
new file mode 100644
index 00000000000..e92685f3e58
--- /dev/null
+++ b/src/test/run-pass/macro-meta-items.rs
@@ -0,0 +1,43 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-pretty - token trees can't pretty print
+// compile-flags: --cfg foo
+
+#[feature(macro_rules)];
+
+macro_rules! compiles_fine {
+    ($at:meta) => {
+        #[cfg($at)]
+        static MISTYPED: () = "foo";
+    }
+}
+macro_rules! emit {
+    ($at:meta) => {
+        #[cfg($at)]
+        static MISTYPED: &'static str = "foo";
+    }
+}
+
+// item
+compiles_fine!(bar)
+emit!(foo)
+
+fn foo() {
+    println!("{}", MISTYPED);
+}
+
+pub fn main() {
+    // statement
+    compiles_fine!(baz);
+    emit!(baz);
+    println!("{}", MISTYPED);
+}
+