1313// limitations under the License.
1414
1515use proc_macro2:: { Delimiter , Group , Ident , Span , TokenStream , TokenTree } ;
16- use quote:: quote;
16+ use quote:: { quote, ToTokens } ;
1717use syn:: {
1818 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 ,
2022} ;
2123
2224/// This is an implementation detail of `googletest::matches_pattern!`. It
@@ -162,7 +164,7 @@ fn parse_tuple_pattern_args(
162164 struct_name : TokenStream ,
163165 group_content : TokenStream ,
164166) -> syn:: Result < TokenStream > {
165- let ( patterns, non_exhaustive ) =
167+ let ( patterns, dot_dot ) =
166168 parse_list_terminated_pattern :: < MaybeTupleFieldPattern > . parse2 ( group_content) ?;
167169 let field_count = patterns. len ( ) ;
168170 let field_patterns = patterns
@@ -181,27 +183,27 @@ fn parse_tuple_pattern_args(
181183 )
182184 } ;
183185
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+ } )
205207}
206208
207209////////////////////////////////////////////////////////////////////////////////
@@ -260,7 +262,7 @@ fn parse_braced_pattern_args(
260262 struct_name : TokenStream ,
261263 group_content : TokenStream ,
262264) -> 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) ?;
264266 let mut field_names = vec ! [ ] ;
265267 let field_patterns: Vec < TokenStream > = patterns
266268 . into_iter ( )
@@ -286,26 +288,34 @@ fn parse_braced_pattern_args(
286288 )
287289 } ;
288290
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
291297 // `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.
293299 //
294300 // ```
295301 // struct Struct(u32);
296302 // ...
297303 // matches_pattern!(foo, Struct { bar(): eq(1) })
298304 // ```
299305 // 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+ {
301311 Ok ( matcher)
302312 } else {
303313 Ok ( quote ! {
304314 googletest:: matchers:: __internal_unstable_do_not_depend_on_these:: compile_assert_and_match(
305315 |actual| {
306316 // Exhaustively check that all field names are specified.
307317 match actual {
308- #struct_name { #( #field_names: _) , * } => { } ,
318+ #struct_name { #( #field_names: _, ) * #dot_dot } => { } ,
309319 // The pattern below is unreachable if the type is a struct (as opposed to
310320 // an enum). Since the macro can't know which it is, we always include it
311321 // and just tell the compiler not to complain.
@@ -321,19 +331,22 @@ fn parse_braced_pattern_args(
321331////////////////////////////////////////////////////////////////////////////////
322332// General-purpose helpers
323333
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 .
326336///
327337/// This is like `Punctuated::parse_terminated`, but additionally allows for an
328338/// 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 > ) > {
330342 let mut patterns = vec ! [ ] ;
331343 while !input. is_empty ( ) {
332344 // 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 ( ) {
334347 // Must be at the end of the group content.
335348 return if input. is_empty ( ) {
336- Ok ( ( patterns, true ) )
349+ Ok ( ( patterns, dot_dot ) )
337350 } else {
338351 compile_err ( input. span ( ) , "`..` must be at the end of the struct pattern" )
339352 } ;
@@ -346,7 +359,7 @@ fn parse_list_terminated_pattern<T: Parse>(input: ParseStream<'_>) -> syn::Resul
346359 }
347360 input. parse :: < Token ! [ , ] > ( ) ?;
348361 }
349- Ok ( ( patterns, false ) )
362+ Ok ( ( patterns, None ) )
350363}
351364
352365fn compile_err < T > ( span : Span , message : & str ) -> syn:: Result < T > {
0 commit comments