|  | 
|  | 1 | +use rustc_ast as ast; | 
|  | 2 | +use rustc_ast::{ItemKind, VariantData}; | 
|  | 3 | +use rustc_errors::MultiSpan; | 
|  | 4 | +use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; | 
|  | 5 | +use rustc_span::{Ident, Span, kw, sym}; | 
|  | 6 | +use thin_vec::thin_vec; | 
|  | 7 | + | 
|  | 8 | +use crate::deriving::generic::ty::{Bounds, Path, PathKind, Ty}; | 
|  | 9 | +use crate::deriving::generic::{ | 
|  | 10 | +    BlockOrExpr, FieldlessVariantsStrategy, MethodDef, SubstructureFields, TraitDef, | 
|  | 11 | +    combine_substructure, | 
|  | 12 | +}; | 
|  | 13 | +use crate::deriving::pathvec_std; | 
|  | 14 | +use 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. | 
|  | 18 | +pub(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 | +} | 
0 commit comments