use crate::cfg_eval::cfg_eval;
+use crate::errors;
use rustc_ast as ast;
-use rustc_ast::{attr, token, GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
use rustc_feature::AttributeTemplate;
use rustc_parse::validate_attr;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
-crate struct Expander;
+pub(crate) struct Expander(pub bool);
impl MultiItemModifier for Expander {
fn expand(
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
+ _: bool,
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
let sess = ecx.sess;
if report_bad_target(sess, &item, span) {
ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
let template =
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
- let attr = attr::mk_attr_outer(meta_item.clone());
- validate_attr::check_builtin_attribute(
+ validate_attr::check_builtin_meta_item(
&sess.parse_sess,
- &attr,
+ &meta_item,
+ ast::AttrStyle::Outer,
sym::derive,
template,
);
- let mut resolutions: Vec<_> = attr
- .meta_item_list()
- .unwrap_or_default()
- .into_iter()
- .filter_map(|nested_meta| match nested_meta {
- NestedMetaItem::MetaItem(meta) => Some(meta),
- NestedMetaItem::Literal(lit) => {
- // Reject `#[derive("Debug")]`.
- report_unexpected_literal(sess, &lit);
- None
- }
- })
- .map(|meta| {
- // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
- report_path_args(sess, &meta);
- meta.path
- })
- .map(|path| (path, dummy_annotatable(), None))
- .collect();
+ let mut resolutions = match &meta_item.kind {
+ MetaItemKind::List(list) => {
+ list.iter()
+ .filter_map(|nested_meta| match nested_meta {
+ NestedMetaItem::MetaItem(meta) => Some(meta),
+ NestedMetaItem::Lit(lit) => {
+ // Reject `#[derive("Debug")]`.
+ report_unexpected_meta_item_lit(sess, &lit);
+ None
+ }
+ })
+ .map(|meta| {
+ // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
+ // paths.
+ report_path_args(sess, &meta);
+ meta.path.clone()
+ })
+ .map(|path| (path, dummy_annotatable(), None, self.0))
+ .collect()
+ }
+ _ => vec![],
+ };
// Do not configure or clone items unless necessary.
match &mut resolutions[..] {
[] => {}
- [(_, first_item, _), others @ ..] => {
- *first_item = cfg_eval(sess, features, item.clone());
- for (_, item, _) in others {
+ [(_, first_item, ..), others @ ..] => {
+ *first_item = cfg_eval(
+ sess,
+ features,
+ item.clone(),
+ ecx.current_expansion.lint_node_id,
+ );
+ for (_, item, _, _) in others {
*item = first_item.clone();
}
}
bounds: Default::default(),
is_placeholder: false,
kind: GenericParamKind::Lifetime,
+ colon_span: None,
})
}
let bad_target =
!matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
if bad_target {
- struct_span_err!(
- sess,
- span,
- E0774,
- "`derive` may only be applied to `struct`s, `enum`s and `union`s",
- )
- .span_label(span, "not applicable here")
- .span_label(item.span(), "not a `struct`, `enum` or `union`")
- .emit();
+ sess.emit_err(errors::BadDeriveTarget { span, item: item.span() });
}
bad_target
}
-fn report_unexpected_literal(sess: &Session, lit: &ast::Lit) {
- let help_msg = match lit.token.kind {
- token::Str if rustc_lexer::is_ident(&lit.token.symbol.as_str()) => {
- format!("try using `#[derive({})]`", lit.token.symbol)
+fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
+ let help = match lit.kind {
+ ast::LitKind::Str(_, ast::StrStyle::Cooked)
+ if rustc_lexer::is_ident(lit.symbol.as_str()) =>
+ {
+ errors::BadDeriveLitHelp::StrLit { sym: lit.symbol }
}
- _ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(),
+ _ => errors::BadDeriveLitHelp::Other,
};
- struct_span_err!(sess, lit.span, E0777, "expected path to a trait, found literal",)
- .span_label(lit.span, "not a trait")
- .help(&help_msg)
- .emit();
+ sess.emit_err(errors::BadDeriveLit { span: lit.span, help });
}
fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
- let report_error = |title, action| {
- let span = meta.span.with_lo(meta.path.span.hi());
- sess.struct_span_err(span, title)
- .span_suggestion(span, action, String::new(), Applicability::MachineApplicable)
- .emit();
- };
+ let span = meta.span.with_lo(meta.path.span.hi());
+
match meta.kind {
MetaItemKind::Word => {}
- MetaItemKind::List(..) => report_error(
- "traits in `#[derive(...)]` don't accept arguments",
- "remove the arguments",
- ),
+ MetaItemKind::List(..) => {
+ sess.emit_err(errors::DerivePathArgsList { span });
+ }
MetaItemKind::NameValue(..) => {
- report_error("traits in `#[derive(...)]` don't accept values", "remove the value")
+ sess.emit_err(errors::DerivePathArgsValue { span });
}
}
}