From c31267f3e4058eca943f36cc9e847fd11e7381f8 Mon Sep 17 00:00:00 2001 From: Dominic Date: Mon, 20 Mar 2023 11:36:00 +0100 Subject: [PATCH 1/3] Update syn to 2.0 --- .github/workflows/rust.yml | 2 +- derive/Cargo.toml | 4 +-- derive/src/attrs.rs | 42 ++++++++++++++-------- derive/src/lib.rs | 33 ++++++++++------- derive/src/parser.rs | 32 +++++++++-------- derive/src/util.rs | 11 +++++- tests/fail/not_openapitype_generics.stderr | 4 +-- 7 files changed, 80 insertions(+), 48 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a1da685b4..abd94a2a9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: - toolchain: "1.66" + toolchain: "1.68" id: rust-toolchain - uses: actions/cache@v3 with: diff --git a/derive/Cargo.toml b/derive/Cargo.toml index be00f616c..ba8231d8d 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -18,5 +18,5 @@ proc-macro = true proc-macro2 = "1.0" quote = "1.0" serde_derive_internals = "0.26" -syn = "1.0" -syn-path = "1.0" +syn = "2.0" +syn-path = "2.0" diff --git a/derive/src/attrs.rs b/derive/src/attrs.rs index f9fa996ff..a8f15e6a0 100644 --- a/derive/src/attrs.rs +++ b/derive/src/attrs.rs @@ -1,12 +1,16 @@ use crate::util::ExpectLit; -use syn::{punctuated::Punctuated, spanned::Spanned as _, Attribute, LitStr, Meta, Token}; +use proc_macro2::TokenStream; +use syn::{ + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned as _, + Attribute, LitStr, Meta, Token +}; pub(super) fn parse_doc_attr(input: &Attribute) -> syn::Result> { - input.parse_meta().and_then(|meta| { - Ok(match meta { - Meta::NameValue(kv) => Some(kv.lit.expect_str()?), - _ => None - }) + Ok(match &input.meta { + Meta::NameValue(kv) => Some(kv.value.clone().expect_str()?), + _ => None }) } @@ -36,25 +40,33 @@ pub(super) struct ContainerAttributes { pub(super) deny_unknown_fields: bool } +struct ParseHelper(Punctuated); + +impl Parse for ParseHelper { + fn parse(input: ParseStream<'_>) -> syn::Result { + Ok(Self(Punctuated::parse_terminated(input)?)) + } +} + impl ContainerAttributes { - pub(super) fn parse_from(&mut self, input: &Attribute, error_on_unknown: bool) -> syn::Result<()> { - let tokens: Punctuated = input.parse_args_with(Punctuated::parse_terminated)?; + pub(super) fn parse_from(&mut self, tokens: TokenStream, error_on_unknown: bool) -> syn::Result<()> { + let tokens = syn::parse2::(tokens)?.0; for token in tokens { match token { Meta::NameValue(kv) if kv.path.is_ident("rename") => { - self.rename = Some(kv.lit.expect_str()?); + self.rename = Some(kv.value.expect_str()?); }, Meta::NameValue(kv) if kv.path.is_ident("rename_all") => { - self.rename_all = Some(kv.lit.expect_str()?); + self.rename_all = Some(kv.value.expect_str()?); }, Meta::NameValue(kv) if kv.path.is_ident("tag") => { - self.tag = Some(kv.lit.expect_str()?); + self.tag = Some(kv.value.expect_str()?); }, Meta::NameValue(kv) if kv.path.is_ident("content") => { - self.content = Some(kv.lit.expect_str()?); + self.content = Some(kv.value.expect_str()?); }, Meta::Path(path) if path.is_ident("untagged") => { @@ -87,12 +99,12 @@ pub(super) struct FieldAttributes { } impl FieldAttributes { - pub(super) fn parse_from(&mut self, input: &Attribute, error_on_unknown: bool) -> syn::Result<()> { - let tokens: Punctuated = input.parse_args_with(Punctuated::parse_terminated)?; + pub(super) fn parse_from(&mut self, tokens: TokenStream, error_on_unknown: bool) -> syn::Result<()> { + let tokens = syn::parse2::(tokens)?.0; for token in tokens { match token { Meta::NameValue(kv) if kv.path.is_ident("rename") => { - self.rename = Some(kv.lit.expect_str()?); + self.rename = Some(kv.value.expect_str()?); }, Meta::Path(path) if path.is_ident("default") => { diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 9791eff4d..5824d879d 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -8,7 +8,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{parse_macro_input, Data, DeriveInput, TraitBound, TraitBoundModifier, TypeParamBound}; +use syn::{parse_macro_input, Data, DeriveInput, Meta, TraitBound, TraitBoundModifier, TypeParamBound}; use syn_path::path; mod attrs; @@ -28,25 +28,34 @@ pub fn derive_openapi_type(input: TokenStream) -> TokenStream { expand_openapi_type(input).unwrap_or_else(|err| err.to_compile_error()).into() } +fn filter_parse_attrs( + attrs: &mut ContainerAttributes, + input: &DeriveInput, + filter: &str, + error_on_unknown: bool +) -> syn::Result<()> { + for attr in &input.attrs { + match &attr.meta { + Meta::List(meta) if meta.path.is_ident(filter) => { + attrs.parse_from(meta.tokens.clone(), error_on_unknown)?; + }, + _ => {} + } + } + Ok(()) +} + fn expand_openapi_type(mut input: DeriveInput) -> syn::Result { let ident = &input.ident; // parse #[serde] and #[openapi] attributes let mut attrs = ContainerAttributes::default(); - for attr in &input.attrs { - if attr.path.is_ident("serde") { - attrs.parse_from(attr, false)?; - } - } - for attr in &input.attrs { - if attr.path.is_ident("openapi") { - attrs.parse_from(attr, true)?; - } - } + filter_parse_attrs(&mut attrs, &input, "serde", false)?; + filter_parse_attrs(&mut attrs, &input, "openapi", true)?; // parse #[doc] attributes for attr in &input.attrs { - if attr.path.is_ident("doc") { + if attr.path().is_ident("doc") { if let Some(lit) = parse_doc_attr(attr)? { attrs.doc.push(lit.value()); } diff --git a/derive/src/parser.rs b/derive/src/parser.rs index e8668012c..81d192e05 100644 --- a/derive/src/parser.rs +++ b/derive/src/parser.rs @@ -5,8 +5,8 @@ use crate::{ use proc_macro2::{Ident, Span}; use serde_derive_internals::attr::RenameRule; use syn::{ - punctuated::Punctuated, spanned::Spanned as _, AngleBracketedGenericArguments, DataEnum, DataStruct, DataUnion, Fields, - FieldsNamed, GenericArgument, LitStr, PathArguments, Type, TypePath + punctuated::Punctuated, spanned::Spanned as _, AngleBracketedGenericArguments, DataEnum, DataStruct, DataUnion, Field, + Fields, FieldsNamed, GenericArgument, LitStr, Meta, PathArguments, Type, TypePath }; use syn_path::path; @@ -43,21 +43,25 @@ pub(super) enum ParseDataType { Unit } +fn filter_parse_attrs(attrs: &mut FieldAttributes, input: &Field, filter: &str, error_on_unknown: bool) -> syn::Result<()> { + for attr in &input.attrs { + match &attr.meta { + Meta::List(meta) if meta.path.is_ident(filter) => { + attrs.parse_from(meta.tokens.clone(), error_on_unknown)?; + }, + _ => {} + } + } + Ok(()) +} + fn parse_named_fields(named_fields: &FieldsNamed, rename_all: Option<&LitStr>) -> syn::Result> { let mut fields: Vec = Vec::new(); for f in &named_fields.named { // parse #[serde] and #[openapi] attributes let mut attrs = FieldAttributes::default(); - for attr in &f.attrs { - if attr.path.is_ident("serde") { - attrs.parse_from(attr, false)?; - } - } - for attr in &f.attrs { - if attr.path.is_ident("openapi") { - attrs.parse_from(attr, true)?; - } - } + filter_parse_attrs(&mut attrs, f, "serde", false)?; + filter_parse_attrs(&mut attrs, f, "openapi", true)?; // skip this field if desired if attrs.skip_serializing && attrs.skip_deserializing { @@ -67,7 +71,7 @@ fn parse_named_fields(named_fields: &FieldsNamed, rename_all: Option<&LitStr>) - // parse #[doc] attributes let mut doc = Vec::new(); for attr in &f.attrs { - if attr.path.is_ident("doc") { + if attr.path().is_ident("doc") { if let Some(lit) = parse_doc_attr(attr)? { doc.push(lit.value()); } @@ -339,7 +343,7 @@ pub(super) fn parse_enum(ident: &Ident, inum: &DataEnum, attrs: &ContainerAttrib }, // no variants (None, None) => Err(syn::Error::new( - inum.brace_token.span, + inum.brace_token.span.span(), "#[derive(OpenapiType)] does not support enums with no variants" )) } diff --git a/derive/src/util.rs b/derive/src/util.rs index 67cc51093..db6965ced 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -1,5 +1,5 @@ use proc_macro2::{Ident, Span}; -use syn::{Lit, LitStr}; +use syn::{spanned::Spanned as _, Expr, Lit, LitStr}; /// Convert [Ident], [String] and [&str] into a [LitStr]. pub(super) trait ToLitStr { @@ -37,3 +37,12 @@ impl ExpectLit for Lit { } } } + +impl ExpectLit for Expr { + fn expect_str(self) -> syn::Result { + match self { + Expr::Lit(lit) => lit.lit.expect_str(), + _ => Err(syn::Error::new(self.span(), "Expected string literal")) + } + } +} diff --git a/tests/fail/not_openapitype_generics.stderr b/tests/fail/not_openapitype_generics.stderr index 4fadc6052..079deae6e 100644 --- a/tests/fail/not_openapitype_generics.stderr +++ b/tests/fail/not_openapitype_generics.stderr @@ -15,11 +15,9 @@ error[E0599]: the function or associated item `schema` exists for struct `Foo: OpenapiType` `Foo: OpenapiType` which is required by `&Foo: OpenapiType` -note: the following trait must be implemented +note: the trait `OpenapiType` must be implemented --> src/lib.rs | | pub trait OpenapiType { From 0f4ca22a45130e390b5f9564ec62ca021596d63c Mon Sep 17 00:00:00 2001 From: Dominic Date: Mon, 20 Mar 2023 12:27:25 +0100 Subject: [PATCH 2/3] Require patched version of serde --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 13ac5f137..b97d3f16f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,5 +37,5 @@ uuid08 = { package = "uuid", version = "0.8", optional = true } [dev-dependencies] paste = "1.0" pretty_assertions = "1.0" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.158", features = ["derive"] } trybuild = "=1.0.79" From e234e77d2a29389f7b47f8d320d4ad495d16539b Mon Sep 17 00:00:00 2001 From: Dominic Date: Mon, 20 Mar 2023 12:34:52 +0100 Subject: [PATCH 3/3] Update serde_derive_internals --- derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index ba8231d8d..1f092c7f6 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -17,6 +17,6 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -serde_derive_internals = "0.26" +serde_derive_internals = "0.27" syn = "2.0" syn-path = "2.0"