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    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    // `#[derive(From)]` is currently usable only on structs with exactly one field.
36    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    // Generate code like this:
67    //
68    // struct S(u32);
69    // #[automatically_derived]
70    // impl ::core::convert::From<u32> for S {
71    //     #[inline]
72    //     fn from(value: u32) -> S {
73    //         Self(value)
74    //     }
75    // }
76    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                        // Self { field: value }
103                        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                        // Self(value)
113                        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}