13
13
// limitations under the License.
14
14
15
15
use proc_macro2:: { Delimiter , Group , Ident , Span , TokenStream , TokenTree } ;
16
- use quote:: quote;
16
+ use quote:: { quote, ToTokens } ;
17
17
use syn:: {
18
18
parse:: { Parse , ParseStream , Parser as _} ,
19
- parse_macro_input, Expr , ExprCall , Pat , Token ,
19
+ parse_macro_input,
20
+ token:: DotDot ,
21
+ Expr , ExprCall , Pat , Token ,
20
22
} ;
21
23
22
24
/// This is an implementation detail of `googletest::matches_pattern!`. It
@@ -162,7 +164,7 @@ fn parse_tuple_pattern_args(
162
164
struct_name : TokenStream ,
163
165
group_content : TokenStream ,
164
166
) -> syn:: Result < TokenStream > {
165
- let ( patterns, non_exhaustive ) =
167
+ let ( patterns, dot_dot ) =
166
168
parse_list_terminated_pattern :: < MaybeTupleFieldPattern > . parse2 ( group_content) ?;
167
169
let field_count = patterns. len ( ) ;
168
170
let field_patterns = patterns
@@ -181,27 +183,27 @@ fn parse_tuple_pattern_args(
181
183
)
182
184
} ;
183
185
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
- }
186
+ // Do a match to ensure;
187
+ // - Fields are exhaustively listed unless the pattern ended with `..`.
188
+ // - `UNDEFINED_SYMBOL(..)` fails to compile.
189
+ let empty_fields = std :: iter :: repeat ( quote ! { _ } )
190
+ . take ( field_count)
191
+ . chain ( dot_dot . map ( ToTokens :: into_token_stream ) ) ;
192
+ Ok ( quote ! {
193
+ googletest :: matchers :: __internal_unstable_do_not_depend_on_these :: compile_assert_and_match (
194
+ |actual| {
195
+ // Exhaustively check that all field names are specified.
196
+ match actual {
197
+ #struct_name ( # ( #empty_fields ) , * ) => ( ) ,
198
+ // The pattern below is unreachable if the type is a struct (as opposed to
199
+ // an enum). Since the macro can't know which it is, we always include it
200
+ // and just tell the compiler not to complain.
201
+ # [ allow ( unreachable_patterns ) ]
202
+ _ => { } ,
203
+ }
204
+ } ,
205
+ #matcher )
206
+ } )
205
207
}
206
208
207
209
////////////////////////////////////////////////////////////////////////////////
@@ -260,7 +262,7 @@ fn parse_braced_pattern_args(
260
262
struct_name : TokenStream ,
261
263
group_content : TokenStream ,
262
264
) -> syn:: Result < TokenStream > {
263
- let ( patterns, non_exhaustive ) = parse_list_terminated_pattern. parse2 ( group_content) ?;
265
+ let ( patterns, dot_dot ) = parse_list_terminated_pattern. parse2 ( group_content) ?;
264
266
let mut field_names = vec ! [ ] ;
265
267
let field_patterns: Vec < TokenStream > = patterns
266
268
. into_iter ( )
@@ -286,26 +288,30 @@ fn parse_braced_pattern_args(
286
288
)
287
289
} ;
288
290
289
- // Do an exhaustiveness check only if the pattern doesn't end with `..` and has
290
- // any fields in the pattern. This latter part is required because
291
+ // Do a match to ensure;
292
+ // - Fields are exhaustively listed unless the pattern ended with `..` and has
293
+ // any fields in the pattern.
294
+ // - `UNDEFINED_SYMBOL { .. }` fails to compile.
295
+ //
296
+ // The requisite that some fields are in the pattern is there because
291
297
// `matches_pattern!` also uses the brace notation for tuple structs when
292
- // asserting on method calls. i.e.
298
+ // asserting on method calls on tuple structs . i.e.
293
299
//
294
300
// ```
295
301
// struct Struct(u32);
296
302
// ...
297
303
// matches_pattern!(foo, Struct { bar(): eq(1) })
298
304
// ```
299
305
// and we can't emit an exhaustiveness check based on the `matches_pattern!`.
300
- if non_exhaustive || field_names. is_empty ( ) {
306
+ if field_names. is_empty ( ) && dot_dot . is_none ( ) {
301
307
Ok ( matcher)
302
308
} else {
303
309
Ok ( quote ! {
304
310
googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: compile_assert_and_match(
305
311
|actual| {
306
312
// Exhaustively check that all field names are specified.
307
313
match actual {
308
- #struct_name { #( #field_names: _) , * } => { } ,
314
+ #struct_name { #( #field_names: _, ) * #dot_dot } => { } ,
309
315
// The pattern below is unreachable if the type is a struct (as opposed to
310
316
// an enum). Since the macro can't know which it is, we always include it
311
317
// and just tell the compiler not to complain.
@@ -321,19 +327,22 @@ fn parse_braced_pattern_args(
321
327
////////////////////////////////////////////////////////////////////////////////
322
328
// General-purpose helpers
323
329
324
- /// Returns the parsed struct pattern body along with a boolean that indicates
325
- /// whether the body ended with `..` .
330
+ /// Returns the parsed struct pattern body along with a `..` if it appears at
331
+ /// the end of the body .
326
332
///
327
333
/// This is like `Punctuated::parse_terminated`, but additionally allows for an
328
334
/// optional `..`, which cannot be followed by a comma.
329
- fn parse_list_terminated_pattern < T : Parse > ( input : ParseStream < ' _ > ) -> syn:: Result < ( Vec < T > , bool ) > {
335
+ fn parse_list_terminated_pattern < T : Parse > (
336
+ input : ParseStream < ' _ > ,
337
+ ) -> syn:: Result < ( Vec < T > , Option < DotDot > ) > {
330
338
let mut patterns = vec ! [ ] ;
331
339
while !input. is_empty ( ) {
332
340
// Check for trailing `..`.
333
- if input. parse :: < Option < Token ! [ ..] > > ( ) ?. is_some ( ) {
341
+ let dot_dot = input. parse :: < Option < Token ! [ ..] > > ( ) ?;
342
+ if dot_dot. is_some ( ) {
334
343
// Must be at the end of the group content.
335
344
return if input. is_empty ( ) {
336
- Ok ( ( patterns, true ) )
345
+ Ok ( ( patterns, dot_dot ) )
337
346
} else {
338
347
compile_err ( input. span ( ) , "`..` must be at the end of the struct pattern" )
339
348
} ;
@@ -346,7 +355,7 @@ fn parse_list_terminated_pattern<T: Parse>(input: ParseStream<'_>) -> syn::Resul
346
355
}
347
356
input. parse :: < Token ! [ , ] > ( ) ?;
348
357
}
349
- Ok ( ( patterns, false ) )
358
+ Ok ( ( patterns, None ) )
350
359
}
351
360
352
361
fn compile_err < T > ( span : Span , message : & str ) -> syn:: Result < T > {
0 commit comments