about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-04-20 07:50:39 -0700
committerAlex Crichton <alex@alexcrichton.com>2018-04-20 19:56:16 -0700
commit79630d4fdfc775b241cae0a209edec2687a29f0f (patch)
treea6aefd265a9250a523cc9d29362a567c54af5fb6 /src/libsyntax
parent8830a0304327ba8c983555ac5d42cec0569c31bb (diff)
downloadrust-79630d4fdfc775b241cae0a209edec2687a29f0f.tar.gz
rust-79630d4fdfc775b241cae0a209edec2687a29f0f.zip
rustc: Tweak custom attribute capabilities
This commit starts to lay some groundwork for the stabilization of custom
attribute invocations and general procedural macros. It applies a number of
changes discussed on [internals] as well as a [recent issue][issue], namely:

* The path used to specify a custom attribute must be of length one and cannot
  be a global path. This'll help future-proof us against any ambiguities and
  give us more time to settle the precise syntax. In the meantime though a bare
  identifier can be used and imported to invoke a custom attribute macro. A new
  feature gate, `proc_macro_path_invoc`, was added to gate multi-segment paths
  and absolute paths.

* The set of items which can be annotated by a custom procedural attribute has
  been restricted. Statements, expressions, and modules are disallowed behind
  two new feature gates: `proc_macro_expr` and `proc_macro_mod`.

* The input to procedural macro attributes has been restricted and adjusted.
  Today an invocation like `#[foo(bar)]` will receive `(bar)` as the input token
  stream, but after this PR it will only receive `bar` (the delimiters were
  removed). Invocations like `#[foo]` are still allowed and will be invoked in
  the same way as `#[foo()]`. This is a **breaking change** for all nightly
  users as the syntax coming in to procedural macros will be tweaked slightly.

* Procedural macros (`foo!()` style) can only be expanded to item-like items by
  default. A separate feature gate, `proc_macro_non_items`, is required to
  expand to items like expressions, statements, etc.

Closes #50038

[internals]: https://internals.rust-lang.org/t/help-stabilize-a-subset-of-macros-2-0/7252
[issue]: https://github.com/rust-lang/rust/issues/50038
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/expand.rs75
-rw-r--r--src/libsyntax/feature_gate.rs9
2 files changed, 83 insertions, 1 deletions
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 678c20402d6..1434e5fddea 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -514,6 +514,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 Some(kind.expect_from_annotatables(items))
             }
             AttrProcMacro(ref mac) => {
+                self.gate_proc_macro_attr_item(attr.span, &item);
                 let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item {
                     Annotatable::Item(item) => token::NtItem(item),
                     Annotatable::TraitItem(item) => token::NtTraitItem(item.into_inner()),
@@ -522,7 +523,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     Annotatable::Stmt(stmt) => token::NtStmt(stmt.into_inner()),
                     Annotatable::Expr(expr) => token::NtExpr(expr),
                 })).into();
-                let tok_result = mac.expand(self.cx, attr.span, attr.tokens, item_tok);
+                let input = self.extract_proc_macro_attr_input(attr.tokens, attr.span);
+                let tok_result = mac.expand(self.cx, attr.span, input, item_tok);
                 self.parse_expansion(tok_result, kind, &attr.path, attr.span)
             }
             ProcMacroDerive(..) | BuiltinDerive(..) => {
@@ -539,6 +541,49 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         }
     }
 
+    fn extract_proc_macro_attr_input(&self, tokens: TokenStream, span: Span) -> TokenStream {
+        let mut trees = tokens.trees();
+        match trees.next() {
+            Some(TokenTree::Delimited(_, delim)) => {
+                if trees.next().is_none() {
+                    return delim.tts.into()
+                }
+            }
+            Some(TokenTree::Token(..)) => {}
+            None => return TokenStream::empty(),
+        }
+        self.cx.span_err(span, "custom attribute invocations must be \
+            of the form #[foo] or #[foo(..)], the macro name must only be \
+            followed by a delimiter token");
+        TokenStream::empty()
+    }
+
+    fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
+        let (kind, gate) = match *item {
+            Annotatable::Item(ref item) => {
+                match item.node {
+                    ItemKind::Mod(_) if self.cx.ecfg.proc_macro_mod() => return,
+                    ItemKind::Mod(_) => ("modules", "proc_macro_mod"),
+                    _ => return,
+                }
+            }
+            Annotatable::TraitItem(_) => return,
+            Annotatable::ImplItem(_) => return,
+            Annotatable::ForeignItem(_) => return,
+            Annotatable::Stmt(_) |
+            Annotatable::Expr(_) if self.cx.ecfg.proc_macro_expr() => return,
+            Annotatable::Stmt(_) => ("statements", "proc_macro_expr"),
+            Annotatable::Expr(_) => ("expressions", "proc_macro_expr"),
+        };
+        emit_feature_err(
+            self.cx.parse_sess,
+            gate,
+            span,
+            GateIssue::Language,
+            &format!("custom attributes cannot be applied to {}", kind),
+        );
+    }
+
     /// Expand a macro invocation. Returns the result of expansion.
     fn expand_bang_invoc(&mut self,
                          invoc: Invocation,
@@ -665,6 +710,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     self.cx.trace_macros_diag();
                     kind.dummy(span)
                 } else {
+                    self.gate_proc_macro_expansion_kind(span, kind);
                     invoc.expansion_data.mark.set_expn_info(ExpnInfo {
                         call_site: span,
                         callee: NameAndSpan {
@@ -695,6 +741,30 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         }
     }
 
+    fn gate_proc_macro_expansion_kind(&self, span: Span, kind: ExpansionKind) {
+        let kind = match kind {
+            ExpansionKind::Expr => "expressions",
+            ExpansionKind::OptExpr => "expressions",
+            ExpansionKind::Pat => "patterns",
+            ExpansionKind::Ty => "types",
+            ExpansionKind::Stmts => "statements",
+            ExpansionKind::Items => return,
+            ExpansionKind::TraitItems => return,
+            ExpansionKind::ImplItems => return,
+            ExpansionKind::ForeignItems => return,
+        };
+        if self.cx.ecfg.proc_macro_non_items() {
+            return
+        }
+        emit_feature_err(
+            self.cx.parse_sess,
+            "proc_macro_non_items",
+            span,
+            GateIssue::Language,
+            &format!("procedural macros cannot be expanded to {}", kind),
+        );
+    }
+
     /// Expand a derive invocation. Returns the result of expansion.
     fn expand_derive_invoc(&mut self,
                            invoc: Invocation,
@@ -1370,6 +1440,9 @@ impl<'feat> ExpansionConfig<'feat> {
         fn enable_custom_derive = custom_derive,
         fn proc_macro_enabled = proc_macro,
         fn macros_in_extern_enabled = macros_in_extern,
+        fn proc_macro_mod = proc_macro_mod,
+        fn proc_macro_expr = proc_macro_expr,
+        fn proc_macro_non_items = proc_macro_non_items,
     }
 }
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 7b7cfe5eea0..6426c9a92f2 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -451,6 +451,15 @@ declare_features! (
     (active, mmx_target_feature, "1.27.0", None, None),
     (active, sse4a_target_feature, "1.27.0", None, None),
     (active, tbm_target_feature, "1.27.0", None, None),
+
+    // Allows macro invocations of the form `#[foo::bar]`
+    (active, proc_macro_path_invoc, "1.27.0", None, None),
+
+    // Allows macro invocations on modules expressions and statements and
+    // procedural macros to expand to non-items.
+    (active, proc_macro_mod, "1.27.0", None, None),
+    (active, proc_macro_expr, "1.27.0", None, None),
+    (active, proc_macro_non_items, "1.27.0", None, None),
 );
 
 declare_features! (