|
| 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