From 7680cb0c1d7cbf812118081dbcdfe5bc67083488 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Thu, 12 Jun 2025 09:46:59 +0100 Subject: [PATCH] Parse default field values as `Type::Verbatim` Adapted from #1851. Co-Authored-By: =?UTF-8?q?Esteban=20K=C3=BCber?= --- src/data.rs | 14 +++++++++++ src/parse_quote.rs | 2 ++ tests/test_item.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/src/data.rs b/src/data.rs index 96db2a0b7c..2058bf1c5a 100644 --- a/src/data.rs +++ b/src/data.rs @@ -334,6 +334,7 @@ pub(crate) mod parsing { let colon_token: Token![:] = input.parse()?; + let type_start = input.fork(); let ty: Type = if unnamed_field && (input.peek(Token![struct]) || input.peek(Token![union]) && input.peek2(token::Brace)) @@ -346,6 +347,19 @@ pub(crate) mod parsing { input.parse()? }; + let ty = if input.peek(Token![=]) { + // We don't support parsing default_field_values, a (currently unstable) feature (see https://github.com/rust-lang/rfcs/pull/3681) + // because `Field` is not `non_exhaustive`, and so can't have new fields added to it. + // We know that this isn't the correct semantics; the default field value is not part of the type. + // However, for parsing purposes, it is in type position. + // We use a similar solution for negative inherent impls, e.g. `impl !SomeType {}` + input.parse::()?; + input.parse::()?; + Type::Verbatim(verbatim::between(&type_start, input)) + } else { + ty + }; + Ok(Field { attrs, vis, diff --git a/src/parse_quote.rs b/src/parse_quote.rs index 2db20597c4..4fd25bc1c2 100644 --- a/src/parse_quote.rs +++ b/src/parse_quote.rs @@ -194,6 +194,8 @@ impl ParseQuote for Field { let ty: Type = input.parse()?; + // TODO: Once `Field` supports default_field_values, parse them here + Ok(Field { attrs, vis, diff --git a/tests/test_item.rs b/tests/test_item.rs index 0a12b7aadc..650dceaf09 100644 --- a/tests/test_item.rs +++ b/tests/test_item.rs @@ -274,6 +274,67 @@ fn test_impl_visibility() { snapshot!(tokens as Item, @"Item::Verbatim(`pub default unsafe impl union { }`)"); } +#[test] +fn test_struct_default_field_values() { + let tokens = quote! { + struct Foo { + field: i32 = const { 42 }, + } + }; + snapshot!(tokens as Item, @r#" + Item::Struct { + vis: Visibility::Inherited, + ident: "Foo", + generics: Generics, + fields: Fields::Named { + named: [ + Field { + vis: Visibility::Inherited, + ident: Some("field"), + colon_token: Some, + ty: Type::Verbatim(`i32 = const { 42 }`), + }, + Token![,], + ], + }, + } + "#); +} + +#[test] +fn test_enum_default_field_values() { + let tokens = quote! { + enum Foo { + Bar { + field: i32 = 42, + } + } + }; + snapshot!(tokens as Item, @r#" + Item::Enum { + vis: Visibility::Inherited, + ident: "Foo", + generics: Generics, + variants: [ + Variant { + ident: "Bar", + fields: Fields::Named { + named: [ + Field { + vis: Visibility::Inherited, + ident: Some("field"), + colon_token: Some, + ty: Type::Verbatim(`i32 = 42`), + }, + Token![,], + ], + }, + }, + ], + } + "#); +} + #[test] fn test_impl_type_parameter_defaults() { #[cfg(any())]