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,34 @@ 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 ( ) &&
307
+ // If there are no fields, then this check means that there are method patterns, and we can
308
+ // no longer be confident that this is a braced struct rather than a tuple struct.
309
+ !field_patterns. is_empty ( )
310
+ {
301
311
Ok ( matcher)
302
312
} else {
303
313
Ok ( quote ! {
304
314
googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: compile_assert_and_match(
305
315
|actual| {
306
316
// Exhaustively check that all field names are specified.
307
317
match actual {
308
- #struct_name { #( #field_names: _) , * } => { } ,
318
+ #struct_name { #( #field_names: _, ) * #dot_dot } => { } ,
309
319
// The pattern below is unreachable if the type is a struct (as opposed to
310
320
// an enum). Since the macro can't know which it is, we always include it
311
321
// and just tell the compiler not to complain.
@@ -321,19 +331,22 @@ fn parse_braced_pattern_args(
321
331
////////////////////////////////////////////////////////////////////////////////
322
332
// General-purpose helpers
323
333
324
- /// Returns the parsed struct pattern body along with a boolean that indicates
325
- /// whether the body ended with `..` .
334
+ /// Returns the parsed struct pattern body along with a `..` if it appears at
335
+ /// the end of the body .
326
336
///
327
337
/// This is like `Punctuated::parse_terminated`, but additionally allows for an
328
338
/// optional `..`, which cannot be followed by a comma.
329
- fn parse_list_terminated_pattern < T : Parse > ( input : ParseStream < ' _ > ) -> syn:: Result < ( Vec < T > , bool ) > {
339
+ fn parse_list_terminated_pattern < T : Parse > (
340
+ input : ParseStream < ' _ > ,
341
+ ) -> syn:: Result < ( Vec < T > , Option < DotDot > ) > {
330
342
let mut patterns = vec ! [ ] ;
331
343
while !input. is_empty ( ) {
332
344
// Check for trailing `..`.
333
- if input. parse :: < Option < Token ! [ ..] > > ( ) ?. is_some ( ) {
345
+ let dot_dot = input. parse :: < Option < Token ! [ ..] > > ( ) ?;
346
+ if dot_dot. is_some ( ) {
334
347
// Must be at the end of the group content.
335
348
return if input. is_empty ( ) {
336
- Ok ( ( patterns, true ) )
349
+ Ok ( ( patterns, dot_dot ) )
337
350
} else {
338
351
compile_err ( input. span ( ) , "`..` must be at the end of the struct pattern" )
339
352
} ;
@@ -346,7 +359,7 @@ fn parse_list_terminated_pattern<T: Parse>(input: ParseStream<'_>) -> syn::Resul
346
359
}
347
360
input. parse :: < Token ! [ , ] > ( ) ?;
348
361
}
349
- Ok ( ( patterns, false ) )
362
+ Ok ( ( patterns, None ) )
350
363
}
351
364
352
365
fn compile_err < T > ( span : Span , message : & str ) -> syn:: Result < T > {
0 commit comments