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 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 = 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); }