Skip to content

Commit 222551f

Browse files
committed
Auto merge of #50120 - alexcrichton:more-proc-macro-gates, r=petrochenkov
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]: #50038
2 parents e59f78f + 79630d4 commit 222551f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+265
-45
lines changed

src/librustc_resolve/macros.rs

+12
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,18 @@ impl<'a> Resolver<'a> {
397397

398398
fn resolve_macro_to_def(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool)
399399
-> Result<Def, Determinacy> {
400+
if path.segments.len() > 1 {
401+
if !self.session.features_untracked().proc_macro_path_invoc {
402+
emit_feature_err(
403+
&self.session.parse_sess,
404+
"proc_macro_path_invoc",
405+
path.span,
406+
GateIssue::Language,
407+
"paths of length greater than one in macro invocations are \
408+
currently unstable",
409+
);
410+
}
411+
}
400412
let def = self.resolve_macro_to_def_inner(scope, path, kind, force);
401413
if def != Err(Determinacy::Undetermined) {
402414
// Do not report duplicated errors on every undetermined resolution.

src/libsyntax/ext/expand.rs

+74-1
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
514514
Some(kind.expect_from_annotatables(items))
515515
}
516516
AttrProcMacro(ref mac) => {
517+
self.gate_proc_macro_attr_item(attr.span, &item);
517518
let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item {
518519
Annotatable::Item(item) => token::NtItem(item),
519520
Annotatable::TraitItem(item) => token::NtTraitItem(item.into_inner()),
@@ -522,7 +523,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
522523
Annotatable::Stmt(stmt) => token::NtStmt(stmt.into_inner()),
523524
Annotatable::Expr(expr) => token::NtExpr(expr),
524525
})).into();
525-
let tok_result = mac.expand(self.cx, attr.span, attr.tokens, item_tok);
526+
let input = self.extract_proc_macro_attr_input(attr.tokens, attr.span);
527+
let tok_result = mac.expand(self.cx, attr.span, input, item_tok);
526528
self.parse_expansion(tok_result, kind, &attr.path, attr.span)
527529
}
528530
ProcMacroDerive(..) | BuiltinDerive(..) => {
@@ -539,6 +541,49 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
539541
}
540542
}
541543

544+
fn extract_proc_macro_attr_input(&self, tokens: TokenStream, span: Span) -> TokenStream {
545+
let mut trees = tokens.trees();
546+
match trees.next() {
547+
Some(TokenTree::Delimited(_, delim)) => {
548+
if trees.next().is_none() {
549+
return delim.tts.into()
550+
}
551+
}
552+
Some(TokenTree::Token(..)) => {}
553+
None => return TokenStream::empty(),
554+
}
555+
self.cx.span_err(span, "custom attribute invocations must be \
556+
of the form #[foo] or #[foo(..)], the macro name must only be \
557+
followed by a delimiter token");
558+
TokenStream::empty()
559+
}
560+
561+
fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
562+
let (kind, gate) = match *item {
563+
Annotatable::Item(ref item) => {
564+
match item.node {
565+
ItemKind::Mod(_) if self.cx.ecfg.proc_macro_mod() => return,
566+
ItemKind::Mod(_) => ("modules", "proc_macro_mod"),
567+
_ => return,
568+
}
569+
}
570+
Annotatable::TraitItem(_) => return,
571+
Annotatable::ImplItem(_) => return,
572+
Annotatable::ForeignItem(_) => return,
573+
Annotatable::Stmt(_) |
574+
Annotatable::Expr(_) if self.cx.ecfg.proc_macro_expr() => return,
575+
Annotatable::Stmt(_) => ("statements", "proc_macro_expr"),
576+
Annotatable::Expr(_) => ("expressions", "proc_macro_expr"),
577+
};
578+
emit_feature_err(
579+
self.cx.parse_sess,
580+
gate,
581+
span,
582+
GateIssue::Language,
583+
&format!("custom attributes cannot be applied to {}", kind),
584+
);
585+
}
586+
542587
/// Expand a macro invocation. Returns the result of expansion.
543588
fn expand_bang_invoc(&mut self,
544589
invoc: Invocation,
@@ -665,6 +710,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
665710
self.cx.trace_macros_diag();
666711
kind.dummy(span)
667712
} else {
713+
self.gate_proc_macro_expansion_kind(span, kind);
668714
invoc.expansion_data.mark.set_expn_info(ExpnInfo {
669715
call_site: span,
670716
callee: NameAndSpan {
@@ -695,6 +741,30 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
695741
}
696742
}
697743

744+
fn gate_proc_macro_expansion_kind(&self, span: Span, kind: ExpansionKind) {
745+
let kind = match kind {
746+
ExpansionKind::Expr => "expressions",
747+
ExpansionKind::OptExpr => "expressions",
748+
ExpansionKind::Pat => "patterns",
749+
ExpansionKind::Ty => "types",
750+
ExpansionKind::Stmts => "statements",
751+
ExpansionKind::Items => return,
752+
ExpansionKind::TraitItems => return,
753+
ExpansionKind::ImplItems => return,
754+
ExpansionKind::ForeignItems => return,
755+
};
756+
if self.cx.ecfg.proc_macro_non_items() {
757+
return
758+
}
759+
emit_feature_err(
760+
self.cx.parse_sess,
761+
"proc_macro_non_items",
762+
span,
763+
GateIssue::Language,
764+
&format!("procedural macros cannot be expanded to {}", kind),
765+
);
766+
}
767+
698768
/// Expand a derive invocation. Returns the result of expansion.
699769
fn expand_derive_invoc(&mut self,
700770
invoc: Invocation,
@@ -1370,6 +1440,9 @@ impl<'feat> ExpansionConfig<'feat> {
13701440
fn enable_custom_derive = custom_derive,
13711441
fn proc_macro_enabled = proc_macro,
13721442
fn macros_in_extern_enabled = macros_in_extern,
1443+
fn proc_macro_mod = proc_macro_mod,
1444+
fn proc_macro_expr = proc_macro_expr,
1445+
fn proc_macro_non_items = proc_macro_non_items,
13731446
}
13741447
}
13751448

src/libsyntax/feature_gate.rs

+9
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,15 @@ declare_features! (
451451
(active, mmx_target_feature, "1.27.0", None, None),
452452
(active, sse4a_target_feature, "1.27.0", None, None),
453453
(active, tbm_target_feature, "1.27.0", None, None),
454+
455+
// Allows macro invocations of the form `#[foo::bar]`
456+
(active, proc_macro_path_invoc, "1.27.0", None, None),
457+
458+
// Allows macro invocations on modules expressions and statements and
459+
// procedural macros to expand to non-items.
460+
(active, proc_macro_mod, "1.27.0", None, None),
461+
(active, proc_macro_expr, "1.27.0", None, None),
462+
(active, proc_macro_non_items, "1.27.0", None, None),
454463
);
455464

456465
declare_features! (

src/test/compile-fail-fulldeps/proc-macro/attr-invalid-exprs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
//! Attributes producing expressions in invalid locations
1515
16-
#![feature(proc_macro, stmt_expr_attributes)]
16+
#![feature(proc_macro, stmt_expr_attributes, proc_macro_expr)]
1717

1818
extern crate attr_stmt_expr;
1919
use attr_stmt_expr::{duplicate, no_output};

src/test/compile-fail-fulldeps/proc-macro/attr-stmt-expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// aux-build:attr-stmt-expr.rs
1212
// ignore-stage1
1313

14-
#![feature(proc_macro)]
14+
#![feature(proc_macro, proc_macro_expr)]
1515

1616
extern crate attr_stmt_expr;
1717
use attr_stmt_expr::{expect_let, expect_print_stmt, expect_expr, expect_print_expr};

src/test/compile-fail-fulldeps/proc-macro/attributes-included.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// aux-build:attributes-included.rs
1212
// ignore-stage1
1313

14-
#![feature(proc_macro, rustc_attrs)]
14+
#![feature(proc_macro, rustc_attrs, proc_macro_path_invoc)]
1515
#![warn(unused)]
1616

1717
extern crate attributes_included;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
// force-host
13+
14+
#![crate_type = "proc-macro"]
15+
#![feature(proc_macro)]
16+
17+
extern crate proc_macro;
18+
19+
use proc_macro::*;
20+
21+
#[proc_macro]
22+
pub fn m(a: TokenStream) -> TokenStream {
23+
a
24+
}
25+
26+
#[proc_macro_attribute]
27+
pub fn a(_a: TokenStream, b: TokenStream) -> TokenStream {
28+
b
29+
}

src/test/compile-fail-fulldeps/proc-macro/lints_in_proc_macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// aux-build:bang_proc_macro2.rs
1212
// ignore-stage1
1313

14-
#![feature(proc_macro)]
14+
#![feature(proc_macro, proc_macro_non_items)]
1515
#![allow(unused_macros)]
1616

1717
extern crate bang_proc_macro2;

src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
// aux-build:bang_proc_macro.rs
1212

13-
#![feature(proc_macro)]
13+
#![feature(proc_macro, proc_macro_non_items)]
1414

1515
#[macro_use]
1616
extern crate bang_proc_macro;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:proc-macro-gates.rs
12+
// gate-test-proc_macro_non_items
13+
// gate-test-proc_macro_path_invoc
14+
// gate-test-proc_macro_mod line
15+
// gate-test-proc_macro_expr
16+
// gate-test-proc_macro_mod
17+
18+
#![feature(proc_macro, stmt_expr_attributes)]
19+
20+
extern crate proc_macro_gates as foo;
21+
22+
use foo::*;
23+
24+
#[foo::a] //~ ERROR: paths of length greater than one
25+
fn _test() {}
26+
27+
#[a] //~ ERROR: custom attributes cannot be applied to modules
28+
mod _test2 {}
29+
30+
#[a = y] //~ ERROR: must only be followed by a delimiter token
31+
fn _test3() {}
32+
33+
#[a = ] //~ ERROR: must only be followed by a delimiter token
34+
fn _test4() {}
35+
36+
#[a () = ] //~ ERROR: must only be followed by a delimiter token
37+
fn _test5() {}
38+
39+
fn main() {
40+
#[a] //~ ERROR: custom attributes cannot be applied to statements
41+
let _x = 2;
42+
let _x = #[a] 2;
43+
//~^ ERROR: custom attributes cannot be applied to expressions
44+
45+
let _x: m!(u32) = 3;
46+
//~^ ERROR: procedural macros cannot be expanded to types
47+
if let m!(Some(_x)) = Some(3) {
48+
//~^ ERROR: procedural macros cannot be expanded to patterns
49+
}
50+
let _x = m!(3);
51+
//~^ ERROR: procedural macros cannot be expanded to expressions
52+
m!(let _x = 3;);
53+
//~^ ERROR: procedural macros cannot be expanded to statements
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:proc-macro-gates.rs
12+
13+
#![feature(proc_macro, stmt_expr_attributes)]
14+
15+
extern crate proc_macro_gates as foo;
16+
17+
use foo::*;
18+
19+
// NB. these errors aren't the best errors right now, but they're definitely
20+
// intended to be errors. Somehow using a custom attribute in these positions
21+
// should either require a feature gate or not be allowed on stable.
22+
23+
fn _test6<#[a] T>() {}
24+
//~^ ERROR: unknown to the compiler
25+
26+
fn _test7() {
27+
match 1 {
28+
#[a] //~ ERROR: unknown to the compiler
29+
0 => {}
30+
_ => {}
31+
}
32+
}
33+
34+
fn main() {
35+
}

src/test/compile-fail/extern-macro.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
// #41719
1212

13-
#![feature(use_extern_macros)]
13+
#![feature(use_extern_macros, proc_macro_path_invoc)]
1414

1515
fn main() {
1616
enum Foo {}

src/test/compile-fail/macro-with-seps-err-msg.rs

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
// gate-test-use_extern_macros
1212

13+
#![feature(proc_macro_path_invoc)]
14+
1315
fn main() {
1416
globnar::brotz!(); //~ ERROR non-ident macro paths are experimental
1517
#[derive(foo::Bar)] struct T; //~ ERROR non-ident macro paths are experimental

src/test/compile-fail/macros-nonfatal-errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#![feature(asm)]
1515
#![feature(trace_macros, concat_idents)]
16+
#![feature(proc_macro_path_invoc)]
1617

1718
#[derive(Default)] //~ ERROR
1819
enum OrDeriveThis {}

src/test/compile-fail/privacy/associated-item-privacy-inherent.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#![feature(decl_macro, associated_type_defaults)]
1212
#![allow(unused, private_in_public)]
13+
#![feature(proc_macro_path_invoc)]
1314

1415
mod priv_nominal {
1516
pub struct Pub;

src/test/compile-fail/privacy/associated-item-privacy-trait.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
// ignore-tidy-linelength
1212

13+
#![feature(proc_macro_path_invoc)]
1314
#![feature(decl_macro, associated_type_defaults)]
1415
#![allow(unused, private_in_public)]
1516

src/test/compile-fail/privacy/associated-item-privacy-type-binding.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(proc_macro_path_invoc)]
1112
#![feature(decl_macro, associated_type_defaults)]
1213
#![allow(unused, private_in_public)]
1314

src/test/compile-fail/private-inferred-type-3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
// error-pattern:type `fn(u8) -> ext::PubTupleStruct {ext::PubTupleStruct::{{constructor}}}` is priv
1919
// error-pattern:type `for<'r> fn(&'r ext::Pub<u8>) {<ext::Pub<u8>>::priv_method}` is private
2020

21+
#![feature(proc_macro_path_invoc)]
2122
#![feature(decl_macro)]
2223

2324
extern crate private_inferred_type as ext;

src/test/compile-fail/private-inferred-type.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#![feature(associated_consts)]
1212
#![feature(decl_macro)]
1313
#![allow(private_in_public)]
14+
#![feature(proc_macro_path_invoc)]
1415

1516
mod m {
1617
fn priv_fn() {}

src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// no-prefer-dynamic
1212

1313
#![crate_type = "proc-macro"]
14-
#![feature(proc_macro)]
14+
#![feature(proc_macro, proc_macro_non_items)]
1515

1616
extern crate proc_macro;
1717

0 commit comments

Comments
 (0)