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