1
1
use rustc_ast as ast;
2
+ use rustc_ast:: { FieldDef , Item , ItemKind , VariantData } ;
2
3
use 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;
4
6
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.
5
17
pub ( crate ) fn expand_deriving_from (
6
18
cx : & ExtCtxt < ' _ > ,
7
19
span : Span ,
@@ -10,4 +22,99 @@ pub(crate) fn expand_deriving_from(
10
22
push : & mut dyn FnMut ( Annotatable ) ,
11
23
is_const : bool ,
12
24
) {
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
+ }
13
120
}
0 commit comments