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