@@ -16,9 +16,7 @@ use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
16
16
use quote:: quote;
17
17
use syn:: {
18
18
parse:: { Parse , ParseStream , Parser as _} ,
19
- parse_macro_input,
20
- punctuated:: Punctuated ,
21
- Expr , ExprCall , Pat , Token ,
19
+ parse_macro_input, Expr , ExprCall , Pat , Token ,
22
20
} ;
23
21
24
22
/// This is an implementation detail of `googletest::matches_pattern!`. It
@@ -164,22 +162,46 @@ fn parse_tuple_pattern_args(
164
162
struct_name : TokenStream ,
165
163
group_content : TokenStream ,
166
164
) -> syn:: Result < TokenStream > {
167
- let parser = Punctuated :: < MaybeTupleFieldPattern , Token ! [ , ] > :: parse_terminated;
168
- let fields = parser
169
- . parse2 ( group_content) ?
165
+ let ( patterns, non_exhaustive) =
166
+ parse_list_terminated_pattern :: < MaybeTupleFieldPattern > . parse2 ( group_content) ?;
167
+ let field_count = patterns. len ( ) ;
168
+ let field_patterns = patterns
170
169
. into_iter ( )
171
170
. enumerate ( )
172
171
. filter_map ( |( index, maybe_pattern) | maybe_pattern. 0 . map ( |pattern| ( index, pattern) ) )
173
172
. map ( |( index, TupleFieldPattern { ref_token, matcher } ) | {
174
173
let index = syn:: Index :: from ( index) ;
175
174
quote ! { googletest:: matchers:: field!( #struct_name. #index, #ref_token #matcher) }
176
175
} ) ;
177
- Ok ( quote ! {
176
+
177
+ let matcher = quote ! {
178
178
googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: is(
179
179
stringify!( #struct_name) ,
180
- all!( #( #fields ) , * )
180
+ all!( #( #field_patterns ) , * )
181
181
)
182
- } )
182
+ } ;
183
+
184
+ // Do an exhaustiveness check only if the pattern doesn't end with `..`.
185
+ if non_exhaustive {
186
+ Ok ( matcher)
187
+ } else {
188
+ let empty_fields = std:: iter:: repeat ( quote ! { _ } ) . take ( field_count) ;
189
+ Ok ( quote ! {
190
+ googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: compile_assert_and_match(
191
+ |actual| {
192
+ // Exhaustively check that all field names are specified.
193
+ match actual {
194
+ #struct_name ( #( #empty_fields) , * ) => ( ) ,
195
+ // The pattern below is unreachable if the type is a struct (as opposed to
196
+ // an enum). Since the macro can't know which it is, we always include it
197
+ // and just tell the compiler not to complain.
198
+ #[ allow( unreachable_patterns) ]
199
+ _ => { } ,
200
+ }
201
+ } ,
202
+ #matcher)
203
+ } )
204
+ }
183
205
}
184
206
185
207
////////////////////////////////////////////////////////////////////////////////
@@ -257,6 +279,13 @@ fn parse_braced_pattern_args(
257
279
} )
258
280
. collect ( ) ;
259
281
282
+ let matcher = quote ! {
283
+ googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: is(
284
+ stringify!( #struct_name) ,
285
+ all!( #( #field_patterns) , * )
286
+ )
287
+ } ;
288
+
260
289
// Do an exhaustiveness check only if the pattern doesn't end with `..` and has
261
290
// any fields in the pattern. This latter part is required because
262
291
// `matches_pattern!` also uses the brace notation for tuple structs when
@@ -268,36 +297,25 @@ fn parse_braced_pattern_args(
268
297
// matches_pattern!(foo, Struct { bar(): eq(1) })
269
298
// ```
270
299
// and we can't emit an exhaustiveness check based on the `matches_pattern!`.
271
- let maybe_assert_exhaustive = if non_exhaustive || field_names. is_empty ( ) {
272
- None
300
+ if non_exhaustive || field_names. is_empty ( ) {
301
+ Ok ( matcher )
273
302
} else {
274
- // Note that `struct_name` might be an enum variant (`Enum::Foo`), which is not
275
- // a valid type. So we need to infer the type, produce an instance, and match on
276
- // it. Fortunately, `[_; 0]` can be trivially initialized to `[]` and can
277
- // produce an instance by indexing into it without failing compilation.
278
- Some ( quote ! {
279
- fn __matches_pattern_ensure_exhastive_match( i: usize ) {
280
- let val: [ _; 0 ] = [ ] ;
281
- let _ = match val[ i] {
282
- #struct_name { #( #field_names: _) , * } => ( ) ,
283
- // The pattern below is unreachable if the type is a struct (as opposed to an
284
- // enum). Since the macro can't know which it is, we always include it and just
285
- // tell the compiler not to complain.
286
- #[ allow( unreachable_patterns) ]
287
- _ => ( ) ,
288
- } ;
289
- }
303
+ Ok ( quote ! {
304
+ googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: compile_assert_and_match(
305
+ |actual| {
306
+ // Exhaustively check that all field names are specified.
307
+ match actual {
308
+ #struct_name { #( #field_names: _) , * } => { } ,
309
+ // The pattern below is unreachable if the type is a struct (as opposed to
310
+ // an enum). Since the macro can't know which it is, we always include it
311
+ // and just tell the compiler not to complain.
312
+ #[ allow( unreachable_patterns) ]
313
+ _ => { } ,
314
+ }
315
+ } ,
316
+ #matcher)
290
317
} )
291
- } ;
292
-
293
- Ok ( quote ! {
294
- googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: is(
295
- stringify!( #struct_name) , {
296
- #maybe_assert_exhaustive
297
- all!( #( #field_patterns) , * )
298
- }
299
- )
300
- } )
318
+ }
301
319
}
302
320
303
321
////////////////////////////////////////////////////////////////////////////////
0 commit comments