@@ -6,11 +6,34 @@ extern crate synstructure;
6
6
#[ macro_use]
7
7
extern crate quote;
8
8
9
- use proc_macro2:: TokenStream ;
9
+ use proc_macro2:: { TokenStream , Span } ;
10
+ use syn:: spanned:: Spanned ;
11
+
12
+ #[ derive( Debug ) ]
13
+ struct Error ( TokenStream ) ;
14
+
15
+ impl Error {
16
+ fn new ( span : Span , message : & str ) -> Error {
17
+ Error ( quote_spanned ! { span =>
18
+ compile_error!( #message) ;
19
+ } )
20
+ }
21
+
22
+ fn into_tokens ( self ) -> TokenStream {
23
+ self . 0
24
+ }
25
+ }
10
26
11
27
decl_derive ! ( [ Fail , attributes( fail, cause) ] => fail_derive) ;
12
28
13
29
fn fail_derive ( s : synstructure:: Structure ) -> TokenStream {
30
+ match fail_derive_impl ( s) {
31
+ Err ( err) => err. into_tokens ( ) ,
32
+ Ok ( tokens) => tokens,
33
+ }
34
+ }
35
+
36
+ fn fail_derive_impl ( s : synstructure:: Structure ) -> Result < TokenStream , Error > {
14
37
let make_dyn = if cfg ! ( has_dyn_trait) {
15
38
quote ! { & dyn }
16
39
} else {
@@ -49,7 +72,7 @@ fn fail_derive(s: synstructure::Structure) -> TokenStream {
49
72
}
50
73
} ,
51
74
) ;
52
- let display = display_body ( & s) . map ( |display_body| {
75
+ let display = display_body ( & s) ? . map ( |display_body| {
53
76
s. unbound_impl (
54
77
quote ! ( :: failure:: _core:: fmt:: Display ) ,
55
78
quote ! {
@@ -62,38 +85,48 @@ fn fail_derive(s: synstructure::Structure) -> TokenStream {
62
85
)
63
86
} ) ;
64
87
65
- ( quote ! {
88
+ Ok ( quote ! {
66
89
#fail
67
90
#display
68
91
} )
69
- . into ( )
70
92
}
71
93
72
- fn display_body ( s : & synstructure:: Structure ) -> Option < quote:: __rt:: TokenStream > {
94
+ fn display_body ( s : & synstructure:: Structure ) -> Result < Option < quote:: __rt:: TokenStream > , Error > {
73
95
let mut msgs = s. variants ( ) . iter ( ) . map ( |v| find_error_msg ( & v. ast ( ) . attrs ) ) ;
74
- if msgs. all ( |msg| msg. is_none ( ) ) {
75
- return None ;
96
+ if msgs. all ( |msg| msg. map ( |m| m . is_none ( ) ) . unwrap_or ( true ) ) {
97
+ return Ok ( None ) ;
76
98
}
77
99
78
- Some ( s. each_variant ( |v| {
100
+ let mut tokens = TokenStream :: new ( ) ;
101
+ for v in s. variants ( ) {
79
102
let msg =
80
- find_error_msg ( & v. ast ( ) . attrs ) . expect ( "All variants must have display attribute." ) ;
103
+ find_error_msg ( & v. ast ( ) . attrs ) ?
104
+ . ok_or_else ( || Error :: new (
105
+ v. ast ( ) . ident . span ( ) ,
106
+ "All variants must have display attribute."
107
+ ) ) ?;
81
108
if msg. nested . is_empty ( ) {
82
- panic ! ( "Expected at least one argument to fail attribute" ) ;
109
+ return Err ( Error :: new (
110
+ msg. span ( ) ,
111
+ "Expected at least one argument to fail attribute"
112
+ ) ) ;
83
113
}
84
114
85
115
let format_string = match msg. nested [ 0 ] {
86
116
syn:: NestedMeta :: Meta ( syn:: Meta :: NameValue ( ref nv) ) if nv. ident == "display" => {
87
117
nv. lit . clone ( )
88
118
}
89
119
_ => {
90
- panic ! ( "Fail attribute must begin `display = \" \" ` to control the Display message." )
120
+ return Err ( Error :: new (
121
+ msg. span ( ) ,
122
+ "Fail attribute must begin `display = \" \" ` to control the Display message."
123
+ ) ) ;
91
124
}
92
125
} ;
93
126
let args = msg. nested . iter ( ) . skip ( 1 ) . map ( |arg| match * arg {
94
127
syn:: NestedMeta :: Literal ( syn:: Lit :: Int ( ref i) ) => {
95
128
let bi = & v. bindings ( ) [ i. value ( ) as usize ] ;
96
- quote ! ( #bi)
129
+ Ok ( quote ! ( #bi) )
97
130
}
98
131
syn:: NestedMeta :: Meta ( syn:: Meta :: Word ( ref id) ) => {
99
132
let id_s = id. to_string ( ) ;
@@ -102,59 +135,77 @@ fn display_body(s: &synstructure::Structure) -> Option<quote::__rt::TokenStream>
102
135
let bi = match v. bindings ( ) . get ( idx) {
103
136
Some ( bi) => bi,
104
137
None => {
105
- panic ! (
106
- "display attempted to access field `{}` in `{}::{}` which \
138
+ return Err ( Error :: new (
139
+ arg. span ( ) ,
140
+ & format ! (
141
+ "display attempted to access field `{}` in `{}::{}` which \
107
142
does not exist (there are {} field{})",
108
- idx,
109
- s. ast( ) . ident,
110
- v. ast( ) . ident,
111
- v. bindings( ) . len( ) ,
112
- if v. bindings( ) . len( ) != 1 { "s" } else { "" }
113
- ) ;
143
+ idx,
144
+ s. ast( ) . ident,
145
+ v. ast( ) . ident,
146
+ v. bindings( ) . len( ) ,
147
+ if v. bindings( ) . len( ) != 1 { "s" } else { "" }
148
+ )
149
+ ) ) ;
114
150
}
115
151
} ;
116
- return quote ! ( #bi) ;
152
+ return Ok ( quote ! ( #bi) ) ;
117
153
}
118
154
}
119
155
for bi in v. bindings ( ) {
120
156
if bi. ast ( ) . ident . as_ref ( ) == Some ( id) {
121
- return quote ! ( #bi) ;
157
+ return Ok ( quote ! ( #bi) ) ;
122
158
}
123
159
}
124
- panic ! (
125
- "Couldn't find field `{}` in `{}::{}`" ,
126
- id,
127
- s. ast( ) . ident,
128
- v. ast( ) . ident
129
- ) ;
160
+ return Err ( Error :: new (
161
+ arg. span ( ) ,
162
+ & format ! (
163
+ "Couldn't find field `{}` in `{}::{}`" ,
164
+ id,
165
+ s. ast( ) . ident,
166
+ v. ast( ) . ident
167
+ )
168
+ ) ) ;
130
169
}
131
- _ => panic ! ( "Invalid argument to fail attribute!" ) ,
170
+ ref arg => {
171
+ return Err ( Error :: new (
172
+ arg. span ( ) ,
173
+ "Invalid argument to fail attribute!"
174
+ ) ) ;
175
+ } ,
132
176
} ) ;
177
+ let args = args. collect :: < Result < Vec < _ > , _ > > ( ) ?;
133
178
134
- quote ! {
135
- return write!( f, #format_string #( , #args) * )
136
- }
137
- } ) )
179
+ let pat = v . pat ( ) ;
180
+ tokens . extend ( quote ! ( #pat => { return write!( f, #format_string #( , #args) * ) } ) ) ;
181
+ }
182
+ Ok ( Some ( tokens ) )
138
183
}
139
184
140
- fn find_error_msg ( attrs : & [ syn:: Attribute ] ) -> Option < syn:: MetaList > {
185
+ fn find_error_msg ( attrs : & [ syn:: Attribute ] ) -> Result < Option < syn:: MetaList > , Error > {
141
186
let mut error_msg = None ;
142
187
for attr in attrs {
143
188
if let Some ( meta) = attr. interpret_meta ( ) {
144
189
if meta. name ( ) == "fail" {
145
190
if error_msg. is_some ( ) {
146
- panic ! ( "Cannot have two display attributes" )
191
+ return Err ( Error :: new (
192
+ meta. span ( ) ,
193
+ "Cannot have two display attributes"
194
+ ) ) ;
147
195
} else {
148
196
if let syn:: Meta :: List ( list) = meta {
149
197
error_msg = Some ( list) ;
150
198
} else {
151
- panic ! ( "fail attribute must take a list in parentheses" )
199
+ return Err ( Error :: new (
200
+ meta. span ( ) ,
201
+ "fail attribute must take a list in parentheses"
202
+ ) ) ;
152
203
}
153
204
}
154
205
}
155
206
}
156
207
}
157
- error_msg
208
+ Ok ( error_msg)
158
209
}
159
210
160
211
fn is_backtrace ( bi : & & synstructure:: BindingInfo ) -> bool {
0 commit comments