rustc_builtin_macros/deriving/
from.rs

1use 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
16/// Generate an implementation of the `From` trait, provided that `item`
17/// is a struct or a tuple struct with exactly one field.
18pub(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    // #[derive(From)] is currently usable only on structs with exactly one field.
31    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        // We don't have a type to put into From<...> if we don't have a single field, so just put
42        // unit there.
43        None => Ty::Unit,
44    };
45    let path =
46        Path::new_(pathvec_std!(convert::From), vec![Box::new(from_type.clone())], PathKind::Std);
47
48    // Generate code like this:
49    //
50    // struct S(u32);
51    // #[automatically_derived]
52    // impl ::core::convert::From<u32> for S {
53    //     #[inline]
54    //     fn from(value: u32) -> S {
55    //         Self(value)
56    //     }
57    // }
58    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                        // Self {
100                        //     field: value
101                        // }
102                        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                        // Self(value)
112                        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}