diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2018-04-20 07:50:39 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2018-04-20 19:56:16 -0700 |
| commit | 79630d4fdfc775b241cae0a209edec2687a29f0f (patch) | |
| tree | a6aefd265a9250a523cc9d29362a567c54af5fb6 /src/libsyntax | |
| parent | 8830a0304327ba8c983555ac5d42cec0569c31bb (diff) | |
| download | rust-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.rs | 75 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 9 |
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! ( |
