mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 21:16:44 +00:00 
			
		
		
		
	 a6808335d4
			
		
	
	
		a6808335d4
		
	
	
	
	
		
			
			The equivalent for `Symbol`s was renamed some time ago (`kw::Invalid` -> `kw::Empty`), and it makes sense to do the same thing for `Ident`s.
		
			
				
	
	
		
			153 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use crate::cfg_eval::cfg_eval;
 | |
| 
 | |
| use rustc_ast as ast;
 | |
| use rustc_ast::{attr, token, GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
 | |
| use rustc_errors::{struct_span_err, Applicability};
 | |
| use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
 | |
| use rustc_feature::AttributeTemplate;
 | |
| use rustc_parse::validate_attr;
 | |
| use rustc_session::Session;
 | |
| use rustc_span::symbol::{sym, Ident};
 | |
| use rustc_span::Span;
 | |
| 
 | |
| crate struct Expander;
 | |
| 
 | |
| impl MultiItemModifier for Expander {
 | |
|     fn expand(
 | |
|         &self,
 | |
|         ecx: &mut ExtCtxt<'_>,
 | |
|         span: Span,
 | |
|         meta_item: &ast::MetaItem,
 | |
|         item: Annotatable,
 | |
|     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
 | |
|         let sess = ecx.sess;
 | |
|         if report_bad_target(sess, &item, span) {
 | |
|             // We don't want to pass inappropriate targets to derive macros to avoid
 | |
|             // follow up errors, all other errors below are recoverable.
 | |
|             return ExpandResult::Ready(vec![item]);
 | |
|         }
 | |
| 
 | |
|         let (sess, features) = (ecx.sess, ecx.ecfg.features);
 | |
|         let result =
 | |
|             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(
 | |
|                     &sess.parse_sess,
 | |
|                     &attr,
 | |
|                     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();
 | |
| 
 | |
|                 // 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 {
 | |
|                             *item = first_item.clone();
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 resolutions
 | |
|             });
 | |
| 
 | |
|         match result {
 | |
|             Ok(()) => ExpandResult::Ready(vec![item]),
 | |
|             Err(Indeterminate) => ExpandResult::Retry(item),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // The cheapest `Annotatable` to construct.
 | |
| fn dummy_annotatable() -> Annotatable {
 | |
|     Annotatable::GenericParam(ast::GenericParam {
 | |
|         id: ast::DUMMY_NODE_ID,
 | |
|         ident: Ident::empty(),
 | |
|         attrs: Default::default(),
 | |
|         bounds: Default::default(),
 | |
|         is_placeholder: false,
 | |
|         kind: GenericParamKind::Lifetime,
 | |
|     })
 | |
| }
 | |
| 
 | |
| fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
 | |
|     let item_kind = match item {
 | |
|         Annotatable::Item(item) => Some(&item.kind),
 | |
|         Annotatable::Stmt(stmt) => match &stmt.kind {
 | |
|             StmtKind::Item(item) => Some(&item.kind),
 | |
|             _ => None,
 | |
|         },
 | |
|         _ => 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();
 | |
|     }
 | |
|     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)
 | |
|         }
 | |
|         _ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(),
 | |
|     };
 | |
|     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();
 | |
| }
 | |
| 
 | |
| 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();
 | |
|     };
 | |
|     match meta.kind {
 | |
|         MetaItemKind::Word => {}
 | |
|         MetaItemKind::List(..) => report_error(
 | |
|             "traits in `#[derive(...)]` don't accept arguments",
 | |
|             "remove the arguments",
 | |
|         ),
 | |
|         MetaItemKind::NameValue(..) => {
 | |
|             report_error("traits in `#[derive(...)]` don't accept values", "remove the value")
 | |
|         }
 | |
|     }
 | |
| }
 |