Skip to content

Commit 354a29a

Browse files
committed
Auto merge of #54277 - petrochenkov:afterder, r=alexcrichton
Temporarily prohibit proc macro attributes placed after derives ... and also proc macro attributes used together with `#[test]`/`#[bench]`. Addresses item 6 from #50911 (comment). The end goal is straightforward predictable left-to-right expansion order for attributes. Right now derives are expanded last regardless of their relative ordering with macro attributes and right now it's simpler to temporarily prohibit macro attributes placed after derives than changing the expansion order. I'm not sure whether the new beta is already released or not, but if it's released, then this patch needs to be backported, so the solution needs to be minimal. How to fix broken code (derives): - Move macro attributes above derives. This won't change expansion order, they are expanded before derives anyway. Using attribute macros on same items with `#[test]` and `#[bench]` is prohibited for similar expansion order reasons, but this one is going to be reverted much sooner than restrictions on derives. How to fix broken code (test/bench): - Enable `#![feature(plugin)]` (don't ask why). r? @ghost
2 parents 186fe09 + 229df02 commit 354a29a

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
@@ -24,7 +24,7 @@ use syntax::attr;
2424
use syntax::errors::DiagnosticBuilder;
2525
use syntax::ext::base::{self, Determinacy, MultiModifier, MultiDecorator};
2626
use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
27-
use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
27+
use syntax::ext::expand::{AstFragment, Invocation, InvocationKind, TogetherWith};
2828
use syntax::ext::hygiene::{self, Mark};
2929
use syntax::ext::tt::macro_rules;
3030
use syntax::feature_gate::{self, feature_err, emit_feature_err, is_builtin_attr_name, GateIssue};
@@ -332,21 +332,30 @@ impl<'a, 'crateloader: 'a> base::Resolver for Resolver<'a, 'crateloader> {
332332

333333
fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: Mark, force: bool)
334334
-> Result<Option<Lrc<SyntaxExtension>>, Determinacy> {
335-
let (path, kind, derives_in_scope) = match invoc.kind {
335+
let (path, kind, derives_in_scope, together_with) = match invoc.kind {
336336
InvocationKind::Attr { attr: None, .. } =>
337337
return Ok(None),
338-
InvocationKind::Attr { attr: Some(ref attr), ref traits, .. } =>
339-
(&attr.path, MacroKind::Attr, traits.clone()),
338+
InvocationKind::Attr { attr: Some(ref attr), ref traits, together_with, .. } =>
339+
(&attr.path, MacroKind::Attr, traits.clone(), together_with),
340340
InvocationKind::Bang { ref mac, .. } =>
341-
(&mac.node.path, MacroKind::Bang, Vec::new()),
341+
(&mac.node.path, MacroKind::Bang, Vec::new(), TogetherWith::None),
342342
InvocationKind::Derive { ref path, .. } =>
343-
(path, MacroKind::Derive, Vec::new()),
343+
(path, MacroKind::Derive, Vec::new(), TogetherWith::None),
344344
};
345345

346346
let parent_scope = self.invoc_parent_scope(invoc_id, derives_in_scope);
347347
let (def, ext) = self.resolve_macro_to_def(path, kind, &parent_scope, force)?;
348348

349349
if let Def::Macro(def_id, _) = def {
350+
match together_with {
351+
TogetherWith::Derive =>
352+
self.session.span_err(invoc.span(),
353+
"macro attributes must be placed before `#[derive]`"),
354+
TogetherWith::TestBench if !self.session.features_untracked().plugin =>
355+
self.session.span_err(invoc.span(),
356+
"macro attributes cannot be used together with `#[test]` or `#[bench]`"),
357+
_ => {}
358+
}
350359
self.macro_defs.insert(invoc.expansion_data.mark, def_id);
351360
let normal_module_def_id =
352361
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)