rustc_builtin_macros/deriving/
from.rs1use rustc_ast as ast;
2use rustc_ast::{ItemKind, VariantData};
3use rustc_errors::MultiSpan;
4use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
5use rustc_span::{Ident, Span, kw, sym};
6use thin_vec::thin_vec;
7
8use crate::deriving::generic::ty::{Bounds, Path, PathKind, Ty};
9use crate::deriving::generic::{
10 BlockOrExpr, FieldlessVariantsStrategy, MethodDef, SubstructureFields, TraitDef,
11 combine_substructure,
12};
13use crate::deriving::pathvec_std;
14use crate::errors;
15
16pub(crate) fn expand_deriving_from(
19 cx: &ExtCtxt<'_>,
20 span: Span,
21 mitem: &ast::MetaItem,
22 annotatable: &Annotatable,
23 push: &mut dyn FnMut(Annotatable),
24 is_const: bool,
25) {
26 let Annotatable::Item(item) = &annotatable else {
27 cx.dcx().bug("derive(From) used on something else than an item");
28 };
29
30 let err_span = || {
31 let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
32 MultiSpan::from_spans(vec![span, item_span])
33 };
34
35 let field = match &item.kind {
37 ItemKind::Struct(_, _, data) => {
38 if let [field] = data.fields() {
39 Ok(field.clone())
40 } else {
41 let guar = cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
42 span: err_span(),
43 multiple_fields: data.fields().len() > 1,
44 });
45 Err(guar)
46 }
47 }
48 ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
49 let guar = cx.dcx().emit_err(errors::DeriveFromWrongTarget {
50 span: err_span(),
51 kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
52 });
53 Err(guar)
54 }
55 _ => cx.dcx().bug("Invalid derive(From) ADT input"),
56 };
57
58 let from_type = Ty::AstTy(match field {
59 Ok(ref field) => field.ty.clone(),
60 Err(guar) => cx.ty(span, ast::TyKind::Err(guar)),
61 });
62
63 let path =
64 Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std);
65
66 let from_trait_def = TraitDef {
77 span,
78 path,
79 skip_path_as_bound: true,
80 needs_copy_as_bound_if_packed: false,
81 additional_bounds: Vec::new(),
82 supports_unions: false,
83 methods: vec![MethodDef {
84 name: sym::from,
85 generics: Bounds { bounds: vec![] },
86 explicit_self: false,
87 nonself_args: vec![(from_type, sym::value)],
88 ret_ty: Ty::Self_,
89 attributes: thin_vec![cx.attr_word(sym::inline, span)],
90 fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
91 combine_substructure: combine_substructure(Box::new(|cx, span, substructure| {
92 let field = match field {
93 Ok(ref field) => field,
94 Err(guar) => {
95 return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(guar)));
96 }
97 };
98
99 let self_kw = Ident::new(kw::SelfUpper, span);
100 let expr: Box<ast::Expr> = match substructure.fields {
101 SubstructureFields::StaticStruct(variant, _) => match variant {
102 VariantData::Struct { .. } => cx.expr_struct_ident(
104 span,
105 self_kw,
106 thin_vec![cx.field_imm(
107 span,
108 field.ident.unwrap(),
109 cx.expr_ident(span, Ident::new(sym::value, span))
110 )],
111 ),
112 VariantData::Tuple(_, _) => cx.expr_call_ident(
114 span,
115 self_kw,
116 thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))],
117 ),
118 variant => {
119 cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}"));
120 }
121 },
122 _ => cx.dcx().bug("Invalid derive(From) ADT input"),
123 };
124 BlockOrExpr::new_expr(expr)
125 })),
126 }],
127 associated_types: Vec::new(),
128 is_const,
129 is_staged_api_crate: cx.ecfg.features.staged_api(),
130 };
131
132 from_trait_def.expand(cx, mitem, annotatable, push);
133}