| use rustc_ast as ast; |
| use rustc_ast::{ItemKind, VariantData}; |
| use rustc_errors::MultiSpan; |
| use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; |
| use rustc_span::{Ident, Span, kw, sym}; |
| use thin_vec::thin_vec; |
| |
| use crate::deriving::generic::ty::{Bounds, Path, PathKind, Ty}; |
| use crate::deriving::generic::{ |
| BlockOrExpr, FieldlessVariantsStrategy, MethodDef, SubstructureFields, TraitDef, |
| combine_substructure, |
| }; |
| use crate::deriving::pathvec_std; |
| use crate::errors; |
| |
| /// Generate an implementation of the `From` trait, provided that `item` |
| /// is a struct or a tuple struct with exactly one field. |
| pub(crate) fn expand_deriving_from( |
| cx: &ExtCtxt<'_>, |
| span: Span, |
| mitem: &ast::MetaItem, |
| annotatable: &Annotatable, |
| push: &mut dyn FnMut(Annotatable), |
| is_const: bool, |
| ) { |
| let Annotatable::Item(item) = &annotatable else { |
| cx.dcx().bug("derive(From) used on something else than an item"); |
| }; |
| |
| let err_span = || { |
| let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span); |
| MultiSpan::from_spans(vec![span, item_span]) |
| }; |
| |
| // `#[derive(From)]` is currently usable only on structs with exactly one field. |
| let field = match &item.kind { |
| ItemKind::Struct(_, _, data) => { |
| if let [field] = data.fields() { |
| Ok(field.clone()) |
| } else { |
| let guar = cx.dcx().emit_err(errors::DeriveFromWrongFieldCount { |
| span: err_span(), |
| multiple_fields: data.fields().len() > 1, |
| }); |
| Err(guar) |
| } |
| } |
| ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => { |
| let guar = cx.dcx().emit_err(errors::DeriveFromWrongTarget { |
| span: err_span(), |
| kind: &format!("{} {}", item.kind.article(), item.kind.descr()), |
| }); |
| Err(guar) |
| } |
| _ => cx.dcx().bug("Invalid derive(From) ADT input"), |
| }; |
| |
| let from_type = Ty::AstTy(match field { |
| Ok(ref field) => field.ty.clone(), |
| Err(guar) => cx.ty(span, ast::TyKind::Err(guar)), |
| }); |
| |
| let path = |
| Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std); |
| |
| // Generate code like this: |
| // |
| // struct S(u32); |
| // #[automatically_derived] |
| // impl ::core::convert::From<u32> for S { |
| // #[inline] |
| // fn from(value: u32) -> S { |
| // Self(value) |
| // } |
| // } |
| let from_trait_def = TraitDef { |
| span, |
| path, |
| skip_path_as_bound: true, |
| needs_copy_as_bound_if_packed: false, |
| additional_bounds: Vec::new(), |
| supports_unions: false, |
| methods: vec![MethodDef { |
| name: sym::from, |
| generics: Bounds { bounds: vec![] }, |
| explicit_self: false, |
| nonself_args: vec![(from_type, sym::value)], |
| ret_ty: Ty::Self_, |
| attributes: thin_vec![cx.attr_word(sym::inline, span)], |
| fieldless_variants_strategy: FieldlessVariantsStrategy::Default, |
| combine_substructure: combine_substructure(Box::new(|cx, span, substructure| { |
| let field = match field { |
| Ok(ref field) => field, |
| Err(guar) => { |
| return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(guar))); |
| } |
| }; |
| |
| let self_kw = Ident::new(kw::SelfUpper, span); |
| let expr: Box<ast::Expr> = match substructure.fields { |
| SubstructureFields::StaticStruct(variant, _) => match variant { |
| // Self { field: value } |
| VariantData::Struct { .. } => cx.expr_struct_ident( |
| span, |
| self_kw, |
| thin_vec![cx.field_imm( |
| span, |
| field.ident.unwrap(), |
| cx.expr_ident(span, Ident::new(sym::value, span)) |
| )], |
| ), |
| // Self(value) |
| VariantData::Tuple(_, _) => cx.expr_call_ident( |
| span, |
| self_kw, |
| thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))], |
| ), |
| variant => { |
| cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}")); |
| } |
| }, |
| _ => cx.dcx().bug("Invalid derive(From) ADT input"), |
| }; |
| BlockOrExpr::new_expr(expr) |
| })), |
| }], |
| associated_types: Vec::new(), |
| is_const, |
| is_staged_api_crate: cx.ecfg.features.staged_api(), |
| }; |
| |
| from_trait_def.expand(cx, mitem, annotatable, push); |
| } |