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 field = if let ItemKind::Struct(_, _, data) = &item.kind
32 && let [field] = data.fields()
33 {
34 Some(field.clone())
35 } else {
36 None
37 };
38
39 let from_type = match &field {
40 Some(field) => Ty::AstTy(field.ty.clone()),
41 None => Ty::Unit,
44 };
45 let path =
46 Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std);
47
48 let from_trait_def = TraitDef {
59 span,
60 path,
61 skip_path_as_bound: true,
62 needs_copy_as_bound_if_packed: false,
63 additional_bounds: Vec::new(),
64 supports_unions: false,
65 methods: vec![MethodDef {
66 name: sym::from,
67 generics: Bounds { bounds: vec![] },
68 explicit_self: false,
69 nonself_args: vec![(from_type, sym::value)],
70 ret_ty: Ty::Self_,
71 attributes: thin_vec![cx.attr_word(sym::inline, span)],
72 fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
73 combine_substructure: combine_substructure(Box::new(|cx, span, substructure| {
74 let Some(field) = &field else {
75 let item_span = item.kind.ident().map(|ident| ident.span).unwrap_or(item.span);
76 let err_span = MultiSpan::from_spans(vec![span, item_span]);
77 let error = match &item.kind {
78 ItemKind::Struct(_, _, data) => {
79 cx.dcx().emit_err(errors::DeriveFromWrongFieldCount {
80 span: err_span,
81 multiple_fields: data.fields().len() > 1,
82 })
83 }
84 ItemKind::Enum(_, _, _) | ItemKind::Union(_, _, _) => {
85 cx.dcx().emit_err(errors::DeriveFromWrongTarget {
86 span: err_span,
87 kind: &format!("{} {}", item.kind.article(), item.kind.descr()),
88 })
89 }
90 _ => cx.dcx().bug("Invalid derive(From) ADT input"),
91 };
92
93 return BlockOrExpr::new_expr(DummyResult::raw_expr(span, Some(error)));
94 };
95
96 let self_kw = Ident::new(kw::SelfUpper, span);
97 let expr: Box<ast::Expr> = match substructure.fields {
98 SubstructureFields::StaticStruct(variant, _) => match variant {
99 VariantData::Struct { .. } => cx.expr_struct_ident(
103 span,
104 self_kw,
105 thin_vec![cx.field_imm(
106 span,
107 field.ident.unwrap(),
108 cx.expr_ident(span, Ident::new(sym::value, span))
109 )],
110 ),
111 VariantData::Tuple(_, _) => cx.expr_call_ident(
113 span,
114 self_kw,
115 thin_vec![cx.expr_ident(span, Ident::new(sym::value, span))],
116 ),
117 variant => {
118 cx.dcx().bug(format!("Invalid derive(From) ADT variant: {variant:?}"));
119 }
120 },
121 _ => cx.dcx().bug("Invalid derive(From) ADT input"),
122 };
123 BlockOrExpr::new_expr(expr)
124 })),
125 }],
126 associated_types: Vec::new(),
127 is_const,
128 is_staged_api_crate: cx.ecfg.features.staged_api(),
129 };
130
131 from_trait_def.expand(cx, mitem, annotatable, push);
132}