Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update syn to 2.0 #56

Merged
merged 3 commits into from Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -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"
6 changes: 3 additions & 3 deletions derive/Cargo.toml
Expand Up @@ -17,6 +17,6 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
serde_derive_internals = "0.26"
syn = "1.0"
syn-path = "1.0"
serde_derive_internals = "0.27"
syn = "2.0"
syn-path = "2.0"
42 changes: 27 additions & 15 deletions 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<Option<LitStr>> {
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
})
}

Expand Down Expand Up @@ -36,25 +40,33 @@ pub(super) struct ContainerAttributes {
pub(super) deny_unknown_fields: bool
}

struct ParseHelper(Punctuated<Meta, Token![,]>);

impl Parse for ParseHelper {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
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<Meta, Token![,]> = 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::<ParseHelper>(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") => {
Expand Down Expand Up @@ -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<Meta, Token![,]> = 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::<ParseHelper>(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") => {
Expand Down
33 changes: 21 additions & 12 deletions derive/src/lib.rs
Expand Up @@ -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;
Expand All @@ -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<TokenStream2> {
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());
}
Expand Down
32 changes: 18 additions & 14 deletions derive/src/parser.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -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<Vec<ParseDataField>> {
let mut fields: Vec<ParseDataField> = 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 {
Expand All @@ -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());
}
Expand Down Expand Up @@ -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"
))
}
Expand Down
11 changes: 10 additions & 1 deletion 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 {
Expand Down Expand Up @@ -37,3 +37,12 @@ impl ExpectLit for Lit {
}
}
}

impl ExpectLit for Expr {
fn expect_str(self) -> syn::Result<LitStr> {
match self {
Expr::Lit(lit) => lit.lit.expect_str(),
_ => Err(syn::Error::new(self.span(), "Expected string literal"))
}
}
}
4 changes: 1 addition & 3 deletions tests/fail/not_openapitype_generics.stderr
Expand Up @@ -15,11 +15,9 @@ error[E0599]: the function or associated item `schema` exists for struct `Foo<Ba
|
= note: trait bound `Bar: OpenapiType` was not satisfied
= note: the following trait bounds were not satisfied:
`Bar: OpenapiType`
which is required by `Foo<Bar>: OpenapiType`
`Foo<Bar>: OpenapiType`
which is required by `&Foo<Bar>: OpenapiType`
note: the following trait must be implemented
note: the trait `OpenapiType` must be implemented
--> src/lib.rs
|
| pub trait OpenapiType {
Expand Down