11use rustc_ast as ast;
2+ use rustc_ast:: { FieldDef , Item , ItemKind , VariantData } ;
23use rustc_expand:: base:: { Annotatable , ExtCtxt } ;
3- use rustc_span:: { Span , sym} ;
4+ use rustc_span:: { Ident , Span , kw, sym} ;
5+ use thin_vec:: thin_vec;
46
7+ use crate :: deriving:: generic:: ty:: { Bounds , Path , PathKind , Ty } ;
8+ use crate :: deriving:: generic:: {
9+ BlockOrExpr , FieldlessVariantsStrategy , MethodDef , SubstructureFields , TraitDef ,
10+ combine_substructure,
11+ } ;
12+ use crate :: deriving:: pathvec_std;
13+ use crate :: errors;
14+
15+ /// Generate an implementation of the `From` trait, provided that `item`
16+ /// is a struct or a tuple struct with exactly one field.
517pub ( crate ) fn expand_deriving_from (
618 cx : & ExtCtxt < ' _ > ,
719 span : Span ,
@@ -10,4 +22,99 @@ pub(crate) fn expand_deriving_from(
1022 push : & mut dyn FnMut ( Annotatable ) ,
1123 is_const : bool ,
1224) {
25+ let mut visitor = ExtractNonSingleFieldStruct { cx, field : None } ;
26+ item. visit_with ( & mut visitor) ;
27+
28+ // Make sure that the derive is only invoked on single-field [tuple] structs.
29+ // From this point below, we know that there is exactly one field.
30+ let Some ( field) = visitor. field else { return } ;
31+
32+ let path = Path :: new_ (
33+ pathvec_std ! ( convert:: From ) ,
34+ vec ! [ Box :: new( Ty :: AstTy ( field. ty. clone( ) ) ) ] ,
35+ PathKind :: Std ,
36+ ) ;
37+
38+ // Generate code like this:
39+ //
40+ // struct S(u32);
41+ // #[automatically_derived]
42+ // impl ::core::convert::From<u32> for S {
43+ // #[inline]
44+ // fn from(value: u32) -> S {
45+ // Self(value)
46+ // }
47+ // }
48+ let from_trait_def = TraitDef {
49+ span,
50+ path,
51+ skip_path_as_bound : true ,
52+ needs_copy_as_bound_if_packed : false ,
53+ additional_bounds : Vec :: new ( ) ,
54+ supports_unions : false ,
55+ methods : vec ! [ MethodDef {
56+ name: sym:: from,
57+ generics: Bounds { bounds: vec![ ] } ,
58+ explicit_self: false ,
59+ nonself_args: vec![ ( Ty :: AstTy ( field. ty) , sym:: value) ] ,
60+ ret_ty: Ty :: Self_ ,
61+ attributes: thin_vec![ cx. attr_word( sym:: inline, span) ] ,
62+ fieldless_variants_strategy: FieldlessVariantsStrategy :: Default ,
63+ combine_substructure: combine_substructure( Box :: new( |cx, span, substructure| {
64+ let self_kw = Ident :: new( kw:: SelfUpper , span) ;
65+ let expr: Box <ast:: Expr > = match substructure. fields {
66+ SubstructureFields :: StaticStruct ( variant, _) => match variant {
67+ // Self {
68+ // field: value
69+ // }
70+ VariantData :: Struct { .. } => cx. expr_struct_ident(
71+ span,
72+ self_kw,
73+ thin_vec![ cx. field_imm(
74+ span,
75+ field. ident. unwrap( ) ,
76+ cx. expr_ident( span, Ident :: new( sym:: value, span) )
77+ ) ] ,
78+ ) ,
79+ // Self(value)
80+ VariantData :: Tuple ( _, _) => cx. expr_call_ident(
81+ span,
82+ self_kw,
83+ thin_vec![ cx. expr_ident( span, Ident :: new( sym:: value, span) ) ] ,
84+ ) ,
85+ variant => {
86+ cx. dcx( ) . bug( format!( "Invalid derive(From) ADT variant: {variant:?}" ) ) ;
87+ }
88+ } ,
89+ _ => cx. dcx( ) . bug( "Invalid derive(From) ADT input" ) ,
90+ } ;
91+ BlockOrExpr :: new_expr( expr)
92+ } ) ) ,
93+ } ] ,
94+ associated_types : Vec :: new ( ) ,
95+ is_const,
96+ is_staged_api_crate : cx. ecfg . features . staged_api ( ) ,
97+ } ;
98+
99+ from_trait_def. expand ( cx, mitem, item, push) ;
100+ }
101+
102+ struct ExtractNonSingleFieldStruct < ' a , ' b > {
103+ cx : & ' a ExtCtxt < ' b > ,
104+ field : Option < FieldDef > ,
105+ }
106+
107+ impl < ' a , ' b > rustc_ast:: visit:: Visitor < ' a > for ExtractNonSingleFieldStruct < ' a , ' b > {
108+ fn visit_item ( & mut self , item : & ' a Item ) -> Self :: Result {
109+ match & item. kind {
110+ ItemKind :: Struct ( _, _, data) => match data. fields ( ) {
111+ [ field] => self . field = Some ( field. clone ( ) ) ,
112+ _ => { }
113+ } ,
114+ _ => { }
115+ } ;
116+ if self . field . is_none ( ) {
117+ self . cx . dcx ( ) . emit_err ( errors:: DeriveFromWrongTarget { span : item. span } ) ;
118+ }
119+ }
13120}
0 commit comments