Skip to content

Commit 229df02

Browse files
committed
Temporarily prohibit proc macro attributes placed after derives
... and also proc macro attributes used together with test/bench.
1 parent 32dc5a0 commit 229df02

File tree

10 files changed

+160
-51
lines changed

10 files changed

+160
-51
lines changed

src/librustc_resolve/macros.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use syntax::attr;
2525
use syntax::errors::DiagnosticBuilder;
2626
use syntax::ext::base::{self, Determinacy, MultiModifier, MultiDecorator};
2727
use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
28-
use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
28+
use syntax::ext::expand::{AstFragment, Invocation, InvocationKind, TogetherWith};
2929
use syntax::ext::hygiene::{self, Mark};
3030
use syntax::ext::tt::macro_rules;
3131
use syntax::feature_gate::{self, feature_err, emit_feature_err, is_builtin_attr_name, GateIssue};
@@ -341,21 +341,30 @@ impl<'a, 'crateloader: 'a> base::Resolver for Resolver<'a, 'crateloader> {
341341

342342
fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: Mark, force: bool)
343343
-> Result<Option<Lrc<SyntaxExtension>>, Determinacy> {
344-
let (path, kind, derives_in_scope) = match invoc.kind {
344+
let (path, kind, derives_in_scope, together_with) = match invoc.kind {
345345
InvocationKind::Attr { attr: None, .. } =>
346346
return Ok(None),
347-
InvocationKind::Attr { attr: Some(ref attr), ref traits, .. } =>
348-
(&attr.path, MacroKind::Attr, traits.clone()),
347+
InvocationKind::Attr { attr: Some(ref attr), ref traits, together_with, .. } =>
348+
(&attr.path, MacroKind::Attr, traits.clone(), together_with),
349349
InvocationKind::Bang { ref mac, .. } =>
350-
(&mac.node.path, MacroKind::Bang, Vec::new()),
350+
(&mac.node.path, MacroKind::Bang, Vec::new(), TogetherWith::None),
351351
InvocationKind::Derive { ref path, .. } =>
352-
(path, MacroKind::Derive, Vec::new()),
352+
(path, MacroKind::Derive, Vec::new(), TogetherWith::None),
353353
};
354354

355355
let parent_scope = self.invoc_parent_scope(invoc_id, derives_in_scope);
356356
let (def, ext) = self.resolve_macro_to_def(path, kind, &parent_scope, force)?;
357357

358358
if let Def::Macro(def_id, _) = def {
359+
match together_with {
360+
TogetherWith::Derive =>
361+
self.session.span_err(invoc.span(),
362+
"macro attributes must be placed before `#[derive]`"),
363+
TogetherWith::TestBench if !self.session.features_untracked().plugin =>
364+
self.session.span_err(invoc.span(),
365+
"macro attributes cannot be used together with `#[test]` or `#[bench]`"),
366+
_ => {}
367+
}
359368
self.macro_defs.insert(invoc.expansion_data.mark, def_id);
360369
let normal_module_def_id =
361370
self.macro_def_scope(invoc.expansion_data.mark).normal_ancestor_id;

src/libsyntax/ext/expand.rs

+59-39
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,14 @@ pub struct Invocation {
216216
pub expansion_data: ExpansionData,
217217
}
218218

219+
// Needed for feature-gating attributes used after derives or together with test/bench
220+
#[derive(Clone, Copy, PartialEq)]
221+
pub enum TogetherWith {
222+
None,
223+
Derive,
224+
TestBench,
225+
}
226+
219227
pub enum InvocationKind {
220228
Bang {
221229
mac: ast::Mac,
@@ -226,6 +234,7 @@ pub enum InvocationKind {
226234
attr: Option<ast::Attribute>,
227235
traits: Vec<Path>,
228236
item: Annotatable,
237+
together_with: TogetherWith,
229238
},
230239
Derive {
231240
path: Path,
@@ -353,7 +362,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
353362
let dummy = invoc.fragment_kind.dummy(invoc.span()).unwrap();
354363
let fragment = self.expand_invoc(invoc, &*ext).unwrap_or(dummy);
355364
self.collect_invocations(fragment, &[])
356-
} else if let InvocationKind::Attr { attr: None, traits, item } = invoc.kind {
365+
} else if let InvocationKind::Attr { attr: None, traits, item, .. } = invoc.kind {
357366
if !item.derive_allowed() {
358367
let attr = attr::find_by_name(item.attrs(), "derive")
359368
.expect("`derive` attribute should exist");
@@ -1069,14 +1078,23 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
10691078
attr: Option<ast::Attribute>,
10701079
traits: Vec<Path>,
10711080
item: Annotatable,
1072-
kind: AstFragmentKind)
1081+
kind: AstFragmentKind,
1082+
together_with: TogetherWith)
10731083
-> AstFragment {
1074-
self.collect(kind, InvocationKind::Attr { attr, traits, item })
1084+
self.collect(kind, InvocationKind::Attr { attr, traits, item, together_with })
10751085
}
10761086

1077-
fn find_attr_invoc(&self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
1087+
fn find_attr_invoc(&self, attrs: &mut Vec<ast::Attribute>, together_with: &mut TogetherWith)
1088+
-> Option<ast::Attribute> {
10781089
let attr = attrs.iter()
1079-
.position(|a| !attr::is_known(a) && !is_builtin_attr(a))
1090+
.position(|a| {
1091+
if a.path == "derive" {
1092+
*together_with = TogetherWith::Derive
1093+
} else if a.path == "rustc_test_marker2" {
1094+
*together_with = TogetherWith::TestBench
1095+
}
1096+
!attr::is_known(a) && !is_builtin_attr(a)
1097+
})
10801098
.map(|i| attrs.remove(i));
10811099
if let Some(attr) = &attr {
10821100
if !self.cx.ecfg.enable_custom_inner_attributes() &&
@@ -1086,14 +1104,19 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
10861104
"non-builtin inner attributes are unstable");
10871105
}
10881106
}
1107+
if together_with == &TogetherWith::None &&
1108+
attrs.iter().any(|a| a.path == "rustc_test_marker2") {
1109+
*together_with = TogetherWith::TestBench;
1110+
}
10891111
attr
10901112
}
10911113

10921114
/// If `item` is an attr invocation, remove and return the macro attribute and derive traits.
1093-
fn classify_item<T>(&mut self, mut item: T) -> (Option<ast::Attribute>, Vec<Path>, T)
1115+
fn classify_item<T>(&mut self, mut item: T)
1116+
-> (Option<ast::Attribute>, Vec<Path>, T, TogetherWith)
10941117
where T: HasAttrs,
10951118
{
1096-
let (mut attr, mut traits) = (None, Vec::new());
1119+
let (mut attr, mut traits, mut together_with) = (None, Vec::new(), TogetherWith::None);
10971120

10981121
item = item.map_attrs(|mut attrs| {
10991122
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs,
@@ -1102,19 +1125,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
11021125
return attrs;
11031126
}
11041127

1105-
attr = self.find_attr_invoc(&mut attrs);
1128+
attr = self.find_attr_invoc(&mut attrs, &mut together_with);
11061129
traits = collect_derives(&mut self.cx, &mut attrs);
11071130
attrs
11081131
});
11091132

1110-
(attr, traits, item)
1133+
(attr, traits, item, together_with)
11111134
}
11121135

11131136
/// Alternative of `classify_item()` that ignores `#[derive]` so invocations fallthrough
11141137
/// to the unused-attributes lint (making it an error on statements and expressions
11151138
/// is a breaking change)
1116-
fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T) -> (Option<ast::Attribute>, T) {
1117-
let mut attr = None;
1139+
fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T)
1140+
-> (Option<ast::Attribute>, T, TogetherWith) {
1141+
let (mut attr, mut together_with) = (None, TogetherWith::None);
11181142

11191143
item = item.map_attrs(|mut attrs| {
11201144
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs,
@@ -1123,11 +1147,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
11231147
return attrs;
11241148
}
11251149

1126-
attr = self.find_attr_invoc(&mut attrs);
1150+
attr = self.find_attr_invoc(&mut attrs, &mut together_with);
11271151
attrs
11281152
});
11291153

1130-
(attr, item)
1154+
(attr, item, together_with)
11311155
}
11321156

11331157
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
@@ -1166,7 +1190,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
11661190
expr.node = self.cfg.configure_expr_kind(expr.node);
11671191

11681192
// ignore derives so they remain unused
1169-
let (attr, expr) = self.classify_nonitem(expr);
1193+
let (attr, expr, together_with) = self.classify_nonitem(expr);
11701194

11711195
if attr.is_some() {
11721196
// collect the invoc regardless of whether or not attributes are permitted here
@@ -1175,7 +1199,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
11751199

11761200
// AstFragmentKind::Expr requires the macro to emit an expression
11771201
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
1178-
AstFragmentKind::Expr).make_expr();
1202+
AstFragmentKind::Expr, together_with).make_expr();
11791203
}
11801204

11811205
if let ast::ExprKind::Mac(mac) = expr.node {
@@ -1191,14 +1215,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
11911215
expr.node = self.cfg.configure_expr_kind(expr.node);
11921216

11931217
// ignore derives so they remain unused
1194-
let (attr, expr) = self.classify_nonitem(expr);
1218+
let (attr, expr, together_with) = self.classify_nonitem(expr);
11951219

11961220
if attr.is_some() {
11971221
attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
11981222

11991223
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
1200-
AstFragmentKind::OptExpr)
1201-
.make_opt_expr();
1224+
AstFragmentKind::OptExpr, together_with).make_opt_expr();
12021225
}
12031226

12041227
if let ast::ExprKind::Mac(mac) = expr.node {
@@ -1230,19 +1253,18 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
12301253

12311254
// we'll expand attributes on expressions separately
12321255
if !stmt.is_expr() {
1233-
let (attr, derives, stmt_) = if stmt.is_item() {
1256+
let (attr, derives, stmt_, together_with) = if stmt.is_item() {
12341257
self.classify_item(stmt)
12351258
} else {
12361259
// ignore derives on non-item statements so it falls through
12371260
// to the unused-attributes lint
1238-
let (attr, stmt) = self.classify_nonitem(stmt);
1239-
(attr, vec![], stmt)
1261+
let (attr, stmt, together_with) = self.classify_nonitem(stmt);
1262+
(attr, vec![], stmt, together_with)
12401263
};
12411264

12421265
if attr.is_some() || !derives.is_empty() {
1243-
return self.collect_attr(attr, derives,
1244-
Annotatable::Stmt(P(stmt_)), AstFragmentKind::Stmts)
1245-
.make_stmts();
1266+
return self.collect_attr(attr, derives, Annotatable::Stmt(P(stmt_)),
1267+
AstFragmentKind::Stmts, together_with).make_stmts();
12461268
}
12471269

12481270
stmt = stmt_;
@@ -1284,10 +1306,10 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
12841306
fn fold_item(&mut self, item: P<ast::Item>) -> OneVector<P<ast::Item>> {
12851307
let item = configure!(self, item);
12861308

1287-
let (attr, traits, item) = self.classify_item(item);
1309+
let (attr, traits, item, together_with) = self.classify_item(item);
12881310
if attr.is_some() || !traits.is_empty() {
1289-
let item = Annotatable::Item(item);
1290-
return self.collect_attr(attr, traits, item, AstFragmentKind::Items).make_items();
1311+
return self.collect_attr(attr, traits, Annotatable::Item(item),
1312+
AstFragmentKind::Items, together_with).make_items();
12911313
}
12921314

12931315
match item.node {
@@ -1359,11 +1381,10 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
13591381
fn fold_trait_item(&mut self, item: ast::TraitItem) -> OneVector<ast::TraitItem> {
13601382
let item = configure!(self, item);
13611383

1362-
let (attr, traits, item) = self.classify_item(item);
1384+
let (attr, traits, item, together_with) = self.classify_item(item);
13631385
if attr.is_some() || !traits.is_empty() {
1364-
let item = Annotatable::TraitItem(P(item));
1365-
return self.collect_attr(attr, traits, item, AstFragmentKind::TraitItems)
1366-
.make_trait_items()
1386+
return self.collect_attr(attr, traits, Annotatable::TraitItem(P(item)),
1387+
AstFragmentKind::TraitItems, together_with).make_trait_items()
13671388
}
13681389

13691390
match item.node {
@@ -1379,11 +1400,10 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
13791400
fn fold_impl_item(&mut self, item: ast::ImplItem) -> OneVector<ast::ImplItem> {
13801401
let item = configure!(self, item);
13811402

1382-
let (attr, traits, item) = self.classify_item(item);
1403+
let (attr, traits, item, together_with) = self.classify_item(item);
13831404
if attr.is_some() || !traits.is_empty() {
1384-
let item = Annotatable::ImplItem(P(item));
1385-
return self.collect_attr(attr, traits, item, AstFragmentKind::ImplItems)
1386-
.make_impl_items();
1405+
return self.collect_attr(attr, traits, Annotatable::ImplItem(P(item)),
1406+
AstFragmentKind::ImplItems, together_with).make_impl_items();
13871407
}
13881408

13891409
match item.node {
@@ -1414,12 +1434,12 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
14141434

14151435
fn fold_foreign_item(&mut self,
14161436
foreign_item: ast::ForeignItem) -> OneVector<ast::ForeignItem> {
1417-
let (attr, traits, foreign_item) = self.classify_item(foreign_item);
1437+
let (attr, traits, foreign_item, together_with) = self.classify_item(foreign_item);
14181438

14191439
if attr.is_some() || !traits.is_empty() {
1420-
let item = Annotatable::ForeignItem(P(foreign_item));
1421-
return self.collect_attr(attr, traits, item, AstFragmentKind::ForeignItems)
1422-
.make_foreign_items();
1440+
return self.collect_attr(attr, traits, Annotatable::ForeignItem(P(foreign_item)),
1441+
AstFragmentKind::ForeignItems, together_with)
1442+
.make_foreign_items();
14231443
}
14241444

14251445
if let ast::ForeignItemKind::Macro(mac) = foreign_item.node {

src/libsyntax/feature_gate.rs

+4
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,10 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
970970
"the `#[rustc_test_marker]` attribute \
971971
is used internally to track tests",
972972
cfg_fn!(rustc_attrs))),
973+
("rustc_test_marker2", Normal, Gated(Stability::Unstable,
974+
"rustc_attrs",
975+
"temporarily used by rustc to report some errors",
976+
cfg_fn!(rustc_attrs))),
973977
("rustc_transparent_macro", Whitelisted, Gated(Stability::Unstable,
974978
"rustc_attrs",
975979
"used internally for testing macro hygiene",

src/libsyntax/ptr.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
3939
use std::fmt::{self, Display, Debug};
4040
use std::iter::FromIterator;
41-
use std::ops::Deref;
41+
use std::ops::{Deref, DerefMut};
4242
use std::{mem, ptr, slice, vec};
4343

4444
use serialize::{Encodable, Decodable, Encoder, Decoder};
@@ -103,6 +103,12 @@ impl<T: ?Sized> Deref for P<T> {
103103
}
104104
}
105105

106+
impl<T: ?Sized> DerefMut for P<T> {
107+
fn deref_mut(&mut self) -> &mut T {
108+
&mut self.ptr
109+
}
110+
}
111+
106112
impl<T: 'static + Clone> Clone for P<T> {
107113
fn clone(&self) -> P<T> {
108114
P((**self).clone())

src/libsyntax_ext/test.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn expand_test_or_bench(
4949
// If we're not in test configuration, remove the annotated item
5050
if !cx.ecfg.should_test { return vec![]; }
5151

52-
let item =
52+
let mut item =
5353
if let Annotatable::Item(i) = item { i }
5454
else {
5555
cx.parse_sess.span_diagnostic.span_fatal(item.span(),
@@ -192,6 +192,12 @@ pub fn expand_test_or_bench(
192192

193193
debug!("Synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
194194

195+
// Temporarily add another marker to the original item for error reporting
196+
let marker2 = cx.attribute(
197+
attr_sp, cx.meta_word(attr_sp, Symbol::intern("rustc_test_marker2"))
198+
);
199+
item.attrs.push(marker2);
200+
195201
vec![
196202
// Access to libtest under a gensymed name
197203
Annotatable::Item(test_extern),

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
#[macro_use]
1717
extern crate derive_b;
1818

19-
#[derive(B)]
2019
#[B] //~ ERROR `B` is a derive mode
2120
#[C]
2221
#[B(D)]
2322
#[B(E = "foo")]
2423
#[B(arbitrary tokens)]
24+
#[derive(B)]
2525
struct B;
2626

2727
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// aux-build:attr_proc_macro.rs
2+
// compile-flags:--test
3+
4+
#![feature(test)]
5+
6+
extern crate test;
7+
extern crate attr_proc_macro;
8+
use attr_proc_macro::*;
9+
10+
#[attr_proc_macro] // OK
11+
#[derive(Clone)]
12+
struct Before;
13+
14+
#[derive(Clone)]
15+
#[attr_proc_macro] //~ ERROR macro attributes must be placed before `#[derive]`
16+
struct After;
17+
18+
#[attr_proc_macro] //~ ERROR macro attributes cannot be used together with `#[test]` or `#[bench]`
19+
#[test]
20+
fn test_before() {}
21+
22+
#[test]
23+
#[attr_proc_macro] //~ ERROR macro attributes cannot be used together with `#[test]` or `#[bench]`
24+
fn test_after() {}
25+
26+
#[attr_proc_macro] //~ ERROR macro attributes cannot be used together with `#[test]` or `#[bench]`
27+
#[bench]
28+
fn bench_before(b: &mut test::Bencher) {}
29+
30+
#[bench]
31+
#[attr_proc_macro] //~ ERROR macro attributes cannot be used together with `#[test]` or `#[bench]`
32+
fn bench_after(b: &mut test::Bencher) {}

0 commit comments

Comments
 (0)