use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{self, Determinacy, MultiModifier, MultiDecorator};
use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
-use syntax::ext::expand::{AstFragment, Invocation, InvocationKind};
+use syntax::ext::expand::{AstFragment, Invocation, InvocationKind, TogetherWith};
use syntax::ext::hygiene::{self, Mark};
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{self, feature_err, emit_feature_err, is_builtin_attr_name, GateIssue};
fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: Mark, force: bool)
-> Result<Option<Lrc<SyntaxExtension>>, Determinacy> {
- let (path, kind, derives_in_scope) = match invoc.kind {
+ let (path, kind, derives_in_scope, together_with) = match invoc.kind {
InvocationKind::Attr { attr: None, .. } =>
return Ok(None),
- InvocationKind::Attr { attr: Some(ref attr), ref traits, .. } =>
- (&attr.path, MacroKind::Attr, traits.clone()),
+ InvocationKind::Attr { attr: Some(ref attr), ref traits, together_with, .. } =>
+ (&attr.path, MacroKind::Attr, traits.clone(), together_with),
InvocationKind::Bang { ref mac, .. } =>
- (&mac.node.path, MacroKind::Bang, Vec::new()),
+ (&mac.node.path, MacroKind::Bang, Vec::new(), TogetherWith::None),
InvocationKind::Derive { ref path, .. } =>
- (path, MacroKind::Derive, Vec::new()),
+ (path, MacroKind::Derive, Vec::new(), TogetherWith::None),
};
let parent_scope = self.invoc_parent_scope(invoc_id, derives_in_scope);
let (def, ext) = self.resolve_macro_to_def(path, kind, &parent_scope, force)?;
if let Def::Macro(def_id, _) = def {
+ match together_with {
+ TogetherWith::Derive =>
+ self.session.span_err(invoc.span(),
+ "macro attributes must be placed before `#[derive]`"),
+ TogetherWith::TestBench if !self.session.features_untracked().plugin =>
+ self.session.span_err(invoc.span(),
+ "macro attributes cannot be used together with `#[test]` or `#[bench]`"),
+ _ => {}
+ }
self.macro_defs.insert(invoc.expansion_data.mark, def_id);
let normal_module_def_id =
self.macro_def_scope(invoc.expansion_data.mark).normal_ancestor_id;
pub expansion_data: ExpansionData,
}
+// Needed for feature-gating attributes used after derives or together with test/bench
+#[derive(Clone, Copy, PartialEq)]
+pub enum TogetherWith {
+ None,
+ Derive,
+ TestBench,
+}
+
pub enum InvocationKind {
Bang {
mac: ast::Mac,
attr: Option<ast::Attribute>,
traits: Vec<Path>,
item: Annotatable,
+ together_with: TogetherWith,
},
Derive {
path: Path,
let dummy = invoc.fragment_kind.dummy(invoc.span()).unwrap();
let fragment = self.expand_invoc(invoc, &*ext).unwrap_or(dummy);
self.collect_invocations(fragment, &[])
- } else if let InvocationKind::Attr { attr: None, traits, item } = invoc.kind {
+ } else if let InvocationKind::Attr { attr: None, traits, item, .. } = invoc.kind {
if !item.derive_allowed() {
let attr = attr::find_by_name(item.attrs(), "derive")
.expect("`derive` attribute should exist");
attr: Option<ast::Attribute>,
traits: Vec<Path>,
item: Annotatable,
- kind: AstFragmentKind)
+ kind: AstFragmentKind,
+ together_with: TogetherWith)
-> AstFragment {
- self.collect(kind, InvocationKind::Attr { attr, traits, item })
+ self.collect(kind, InvocationKind::Attr { attr, traits, item, together_with })
}
- fn find_attr_invoc(&self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
+ fn find_attr_invoc(&self, attrs: &mut Vec<ast::Attribute>, together_with: &mut TogetherWith)
+ -> Option<ast::Attribute> {
let attr = attrs.iter()
- .position(|a| !attr::is_known(a) && !is_builtin_attr(a))
+ .position(|a| {
+ if a.path == "derive" {
+ *together_with = TogetherWith::Derive
+ } else if a.path == "rustc_test_marker2" {
+ *together_with = TogetherWith::TestBench
+ }
+ !attr::is_known(a) && !is_builtin_attr(a)
+ })
.map(|i| attrs.remove(i));
if let Some(attr) = &attr {
if !self.cx.ecfg.enable_custom_inner_attributes() &&
"non-builtin inner attributes are unstable");
}
}
+ if together_with == &TogetherWith::None &&
+ attrs.iter().any(|a| a.path == "rustc_test_marker2") {
+ *together_with = TogetherWith::TestBench;
+ }
attr
}
/// If `item` is an attr invocation, remove and return the macro attribute and derive traits.
- fn classify_item<T>(&mut self, mut item: T) -> (Option<ast::Attribute>, Vec<Path>, T)
+ fn classify_item<T>(&mut self, mut item: T)
+ -> (Option<ast::Attribute>, Vec<Path>, T, TogetherWith)
where T: HasAttrs,
{
- let (mut attr, mut traits) = (None, Vec::new());
+ let (mut attr, mut traits, mut together_with) = (None, Vec::new(), TogetherWith::None);
item = item.map_attrs(|mut attrs| {
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs,
return attrs;
}
- attr = self.find_attr_invoc(&mut attrs);
+ attr = self.find_attr_invoc(&mut attrs, &mut together_with);
traits = collect_derives(&mut self.cx, &mut attrs);
attrs
});
- (attr, traits, item)
+ (attr, traits, item, together_with)
}
/// Alternative of `classify_item()` that ignores `#[derive]` so invocations fallthrough
/// to the unused-attributes lint (making it an error on statements and expressions
/// is a breaking change)
- fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T) -> (Option<ast::Attribute>, T) {
- let mut attr = None;
+ fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T)
+ -> (Option<ast::Attribute>, T, TogetherWith) {
+ let (mut attr, mut together_with) = (None, TogetherWith::None);
item = item.map_attrs(|mut attrs| {
if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs,
return attrs;
}
- attr = self.find_attr_invoc(&mut attrs);
+ attr = self.find_attr_invoc(&mut attrs, &mut together_with);
attrs
});
- (attr, item)
+ (attr, item, together_with)
}
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
expr.node = self.cfg.configure_expr_kind(expr.node);
// ignore derives so they remain unused
- let (attr, expr) = self.classify_nonitem(expr);
+ let (attr, expr, together_with) = self.classify_nonitem(expr);
if attr.is_some() {
// collect the invoc regardless of whether or not attributes are permitted here
// AstFragmentKind::Expr requires the macro to emit an expression
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
- AstFragmentKind::Expr).make_expr();
+ AstFragmentKind::Expr, together_with).make_expr();
}
if let ast::ExprKind::Mac(mac) = expr.node {
expr.node = self.cfg.configure_expr_kind(expr.node);
// ignore derives so they remain unused
- let (attr, expr) = self.classify_nonitem(expr);
+ let (attr, expr, together_with) = self.classify_nonitem(expr);
if attr.is_some() {
attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a));
return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)),
- AstFragmentKind::OptExpr)
- .make_opt_expr();
+ AstFragmentKind::OptExpr, together_with).make_opt_expr();
}
if let ast::ExprKind::Mac(mac) = expr.node {
// we'll expand attributes on expressions separately
if !stmt.is_expr() {
- let (attr, derives, stmt_) = if stmt.is_item() {
+ let (attr, derives, stmt_, together_with) = if stmt.is_item() {
self.classify_item(stmt)
} else {
// ignore derives on non-item statements so it falls through
// to the unused-attributes lint
- let (attr, stmt) = self.classify_nonitem(stmt);
- (attr, vec![], stmt)
+ let (attr, stmt, together_with) = self.classify_nonitem(stmt);
+ (attr, vec![], stmt, together_with)
};
if attr.is_some() || !derives.is_empty() {
- return self.collect_attr(attr, derives,
- Annotatable::Stmt(P(stmt_)), AstFragmentKind::Stmts)
- .make_stmts();
+ return self.collect_attr(attr, derives, Annotatable::Stmt(P(stmt_)),
+ AstFragmentKind::Stmts, together_with).make_stmts();
}
stmt = stmt_;
fn fold_item(&mut self, item: P<ast::Item>) -> OneVector<P<ast::Item>> {
let item = configure!(self, item);
- let (attr, traits, item) = self.classify_item(item);
+ let (attr, traits, item, together_with) = self.classify_item(item);
if attr.is_some() || !traits.is_empty() {
- let item = Annotatable::Item(item);
- return self.collect_attr(attr, traits, item, AstFragmentKind::Items).make_items();
+ return self.collect_attr(attr, traits, Annotatable::Item(item),
+ AstFragmentKind::Items, together_with).make_items();
}
match item.node {
fn fold_trait_item(&mut self, item: ast::TraitItem) -> OneVector<ast::TraitItem> {
let item = configure!(self, item);
- let (attr, traits, item) = self.classify_item(item);
+ let (attr, traits, item, together_with) = self.classify_item(item);
if attr.is_some() || !traits.is_empty() {
- let item = Annotatable::TraitItem(P(item));
- return self.collect_attr(attr, traits, item, AstFragmentKind::TraitItems)
- .make_trait_items()
+ return self.collect_attr(attr, traits, Annotatable::TraitItem(P(item)),
+ AstFragmentKind::TraitItems, together_with).make_trait_items()
}
match item.node {
fn fold_impl_item(&mut self, item: ast::ImplItem) -> OneVector<ast::ImplItem> {
let item = configure!(self, item);
- let (attr, traits, item) = self.classify_item(item);
+ let (attr, traits, item, together_with) = self.classify_item(item);
if attr.is_some() || !traits.is_empty() {
- let item = Annotatable::ImplItem(P(item));
- return self.collect_attr(attr, traits, item, AstFragmentKind::ImplItems)
- .make_impl_items();
+ return self.collect_attr(attr, traits, Annotatable::ImplItem(P(item)),
+ AstFragmentKind::ImplItems, together_with).make_impl_items();
}
match item.node {
fn fold_foreign_item(&mut self,
foreign_item: ast::ForeignItem) -> OneVector<ast::ForeignItem> {
- let (attr, traits, foreign_item) = self.classify_item(foreign_item);
+ let (attr, traits, foreign_item, together_with) = self.classify_item(foreign_item);
if attr.is_some() || !traits.is_empty() {
- let item = Annotatable::ForeignItem(P(foreign_item));
- return self.collect_attr(attr, traits, item, AstFragmentKind::ForeignItems)
- .make_foreign_items();
+ return self.collect_attr(attr, traits, Annotatable::ForeignItem(P(foreign_item)),
+ AstFragmentKind::ForeignItems, together_with)
+ .make_foreign_items();
}
if let ast::ForeignItemKind::Macro(mac) = foreign_item.node {