Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc: Tweak custom attribute capabilities #50120

Merged
merged 1 commit into from
Apr 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,18 @@ impl<'a> Resolver<'a> {

fn resolve_macro_to_def(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool)
-> Result<Def, Determinacy> {
if path.segments.len() > 1 {
if !self.session.features_untracked().proc_macro_path_invoc {
emit_feature_err(
&self.session.parse_sess,
"proc_macro_path_invoc",
path.span,
GateIssue::Language,
"paths of length greater than one in macro invocations are \
currently unstable",
);
}
}
let def = self.resolve_macro_to_def_inner(scope, path, kind, force);
if def != Err(Determinacy::Undetermined) {
// Do not report duplicated errors on every undetermined resolution.
Expand Down
75 changes: 74 additions & 1 deletion src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand All @@ -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(..) => {
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Copy link
Contributor

@petrochenkov petrochenkov Apr 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if a proc macro attribute is applied to something else, like match arm (match 0 { #[attr] _ => {} }), or a generic parameter (fn f<#[attr] T>), or enum variant?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it already produces some kind of error, but it would be good to have a test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting! Turns out we don't expand procedural macros in those locations right now so you get an "unknown attribute" error. I've added a test to ensure it's at least an error.

};
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,
Expand Down Expand Up @@ -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,
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

//! Attributes producing expressions in invalid locations

#![feature(proc_macro, stmt_expr_attributes)]
#![feature(proc_macro, stmt_expr_attributes, proc_macro_expr)]

extern crate attr_stmt_expr;
use attr_stmt_expr::{duplicate, no_output};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// aux-build:attr-stmt-expr.rs
// ignore-stage1

#![feature(proc_macro)]
#![feature(proc_macro, proc_macro_expr)]

extern crate attr_stmt_expr;
use attr_stmt_expr::{expect_let, expect_print_stmt, expect_expr, expect_print_expr};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// aux-build:attributes-included.rs
// ignore-stage1

#![feature(proc_macro, rustc_attrs)]
#![feature(proc_macro, rustc_attrs, proc_macro_path_invoc)]
#![warn(unused)]

extern crate attributes_included;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2018 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.

// no-prefer-dynamic
// force-host

#![crate_type = "proc-macro"]
#![feature(proc_macro)]

extern crate proc_macro;

use proc_macro::*;

#[proc_macro]
pub fn m(a: TokenStream) -> TokenStream {
a
}

#[proc_macro_attribute]
pub fn a(_a: TokenStream, b: TokenStream) -> TokenStream {
b
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// aux-build:bang_proc_macro2.rs
// ignore-stage1

#![feature(proc_macro)]
#![feature(proc_macro, proc_macro_non_items)]
#![allow(unused_macros)]

extern crate bang_proc_macro2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

// aux-build:bang_proc_macro.rs

#![feature(proc_macro)]
#![feature(proc_macro, proc_macro_non_items)]

#[macro_use]
extern crate bang_proc_macro;
Expand Down
54 changes: 54 additions & 0 deletions src/test/compile-fail-fulldeps/proc-macro/proc-macro-gates.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2018 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.

// aux-build:proc-macro-gates.rs
// gate-test-proc_macro_non_items
// gate-test-proc_macro_path_invoc
// gate-test-proc_macro_mod line
// gate-test-proc_macro_expr
// gate-test-proc_macro_mod

#![feature(proc_macro, stmt_expr_attributes)]

extern crate proc_macro_gates as foo;

use foo::*;

#[foo::a] //~ ERROR: paths of length greater than one
fn _test() {}

#[a] //~ ERROR: custom attributes cannot be applied to modules
mod _test2 {}

#[a = y] //~ ERROR: must only be followed by a delimiter token
fn _test3() {}

#[a = ] //~ ERROR: must only be followed by a delimiter token
fn _test4() {}

#[a () = ] //~ ERROR: must only be followed by a delimiter token
fn _test5() {}

fn main() {
#[a] //~ ERROR: custom attributes cannot be applied to statements
let _x = 2;
let _x = #[a] 2;
//~^ ERROR: custom attributes cannot be applied to expressions

let _x: m!(u32) = 3;
//~^ ERROR: procedural macros cannot be expanded to types
if let m!(Some(_x)) = Some(3) {
//~^ ERROR: procedural macros cannot be expanded to patterns
}
let _x = m!(3);
//~^ ERROR: procedural macros cannot be expanded to expressions
m!(let _x = 3;);
//~^ ERROR: procedural macros cannot be expanded to statements
}
35 changes: 35 additions & 0 deletions src/test/compile-fail-fulldeps/proc-macro/proc-macro-gates2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2018 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.

// aux-build:proc-macro-gates.rs

#![feature(proc_macro, stmt_expr_attributes)]

extern crate proc_macro_gates as foo;

use foo::*;

// NB. these errors aren't the best errors right now, but they're definitely
// intended to be errors. Somehow using a custom attribute in these positions
// should either require a feature gate or not be allowed on stable.

fn _test6<#[a] T>() {}
//~^ ERROR: unknown to the compiler

fn _test7() {
match 1 {
#[a] //~ ERROR: unknown to the compiler
0 => {}
_ => {}
}
}

fn main() {
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/extern-macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

// #41719

#![feature(use_extern_macros)]
#![feature(use_extern_macros, proc_macro_path_invoc)]

fn main() {
enum Foo {}
Expand Down
2 changes: 2 additions & 0 deletions src/test/compile-fail/macro-with-seps-err-msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

// gate-test-use_extern_macros

#![feature(proc_macro_path_invoc)]

fn main() {
globnar::brotz!(); //~ ERROR non-ident macro paths are experimental
#[derive(foo::Bar)] struct T; //~ ERROR non-ident macro paths are experimental
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/macros-nonfatal-errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#![feature(asm)]
#![feature(trace_macros, concat_idents)]
#![feature(proc_macro_path_invoc)]

#[derive(Default)] //~ ERROR
enum OrDeriveThis {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#![feature(decl_macro, associated_type_defaults)]
#![allow(unused, private_in_public)]
#![feature(proc_macro_path_invoc)]

mod priv_nominal {
pub struct Pub;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

// ignore-tidy-linelength

#![feature(proc_macro_path_invoc)]
#![feature(decl_macro, associated_type_defaults)]
#![allow(unused, private_in_public)]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(proc_macro_path_invoc)]
#![feature(decl_macro, associated_type_defaults)]
#![allow(unused, private_in_public)]

Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/private-inferred-type-3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// error-pattern:type `fn(u8) -> ext::PubTupleStruct {ext::PubTupleStruct::{{constructor}}}` is priv
// error-pattern:type `for<'r> fn(&'r ext::Pub<u8>) {<ext::Pub<u8>>::priv_method}` is private

#![feature(proc_macro_path_invoc)]
#![feature(decl_macro)]

extern crate private_inferred_type as ext;
Expand Down
1 change: 1 addition & 0 deletions src/test/compile-fail/private-inferred-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#![feature(associated_consts)]
#![feature(decl_macro)]
#![allow(private_in_public)]
#![feature(proc_macro_path_invoc)]

mod m {
fn priv_fn() {}
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass-fulldeps/auxiliary/cond_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// no-prefer-dynamic

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

extern crate proc_macro;

Expand Down
Loading