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

Use the new parsing utils #296

Merged
merged 8 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 7 additions & 15 deletions impl/src/fmt/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use syn::{
Ident,
};

use crate::utils::skip;

use super::{ContainerAttributes, FmtAttribute};

/// Expands a [`fmt::Debug`] derive macro.
Expand Down Expand Up @@ -175,7 +177,7 @@ enum FieldAttribute {
Fmt(FmtAttribute),

/// Attribute for skipping field.
Skip,
Skip(skip::Attribute),
}

impl FieldAttribute {
Expand Down Expand Up @@ -207,17 +209,7 @@ impl Parse for FieldAttribute {
if input.peek(syn::LitStr) {
input.parse().map(Self::Fmt)
} else {
let _ = input.parse::<syn::Path>().and_then(|p| {
if ["skip", "ignore"].into_iter().any(|i| p.is_ident(i)) {
Ok(p)
} else {
Err(syn::Error::new(
p.span(),
"unknown attribute, expected `skip` or `ignore`",
))
}
})?;
Ok(Self::Skip)
input.parse::<skip::Attribute>().map(Self::Skip)
}
}
}
Expand Down Expand Up @@ -289,7 +281,7 @@ impl<'a> Expansion<'a> {
let out = unnamed.unnamed.iter().enumerate().try_fold(
out,
|out, (i, field)| match FieldAttribute::parse_attrs(&field.attrs)? {
Some(FieldAttribute::Skip) => {
Some(FieldAttribute::Skip(_)) => {
exhaustive = false;
Ok::<_, syn::Error>(out)
}
Expand Down Expand Up @@ -329,7 +321,7 @@ impl<'a> Expansion<'a> {
});
let field_str = field_ident.to_string();
match FieldAttribute::parse_attrs(&field.attrs)? {
Some(FieldAttribute::Skip) => {
Some(FieldAttribute::Skip(_)) => {
exhaustive = false;
Ok::<_, syn::Error>(out)
}
Expand Down Expand Up @@ -376,7 +368,7 @@ impl<'a> Expansion<'a> {
},
));
}
Some(FieldAttribute::Skip) => {}
Some(FieldAttribute::Skip(_)) => {}
None => out.extend([parse_quote! { #ty: ::core::fmt::Debug }]),
}
Ok(out)
Expand Down
46 changes: 26 additions & 20 deletions impl/src/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use syn::{

use crate::{
parsing::Type,
utils::{polyfill, Either},
utils::{forward, polyfill, skip, Either},
};

/// Expands a [`From`] derive macro.
Expand Down Expand Up @@ -44,7 +44,7 @@ pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result<TokenStr
Some(
VariantAttribute::From
| VariantAttribute::Types(_)
| VariantAttribute::Forward
| VariantAttribute::Forward(_)
),
) {
has_explicit_from = true;
Expand Down Expand Up @@ -87,7 +87,7 @@ enum StructAttribute {
Types(Punctuated<Type, token::Comma>),

/// Forward [`From`] implementation.
Forward,
Forward(forward::Attribute),
}

impl StructAttribute {
Expand Down Expand Up @@ -124,12 +124,14 @@ impl StructAttribute {

/// Parses single [`StructAttribute`].
fn parse(input: ParseStream<'_>, fields: &syn::Fields) -> syn::Result<Self> {
let ahead = input.fork();
if let Ok(attr) = ahead.parse::<forward::Attribute>() {
input.advance_to(&ahead);
return Ok(Self::Forward(attr));
}

let ahead = input.fork();
match ahead.parse::<syn::Path>() {
Ok(p) if p.is_ident("forward") => {
input.advance_to(&ahead);
Ok(Self::Forward)
}
Ok(p) if p.is_ident("types") => legacy_error(&ahead, input.span(), fields),
_ => input
.parse_terminated(Type::parse, token::Comma)
Expand All @@ -154,10 +156,10 @@ enum VariantAttribute {
Types(Punctuated<Type, token::Comma>),

/// Forward [`From`] implementation.
Forward,
Forward(forward::Attribute),

/// Skip variant.
Skip,
Skip(skip::Attribute),
}

impl VariantAttribute {
Expand Down Expand Up @@ -191,16 +193,20 @@ impl VariantAttribute {
}

attr.parse_args_with(|input: ParseStream<'_>| {
let ahead = input.fork();
if let Ok(attr) = ahead.parse::<forward::Attribute>() {
input.advance_to(&ahead);
return Ok(Self::Forward(attr));
}

let ahead = input.fork();
if let Ok(attr) = ahead.parse::<skip::Attribute>() {
input.advance_to(&ahead);
return Ok(Self::Skip(attr));
}

let ahead = input.fork();
match ahead.parse::<syn::Path>() {
Ok(p) if p.is_ident("forward") => {
input.advance_to(&ahead);
Ok(Self::Forward)
}
Ok(p) if p.is_ident("skip") || p.is_ident("ignore") => {
input.advance_to(&ahead);
Ok(Self::Skip)
}
Ok(p) if p.is_ident("types") => {
legacy_error(&ahead, input.span(), fields)
}
Expand All @@ -216,7 +222,7 @@ impl From<StructAttribute> for VariantAttribute {
fn from(value: StructAttribute) -> Self {
match value {
StructAttribute::Types(tys) => Self::Types(tys),
StructAttribute::Forward => Self::Forward,
StructAttribute::Forward(attr) => Self::Forward(attr),
}
}
}
Expand Down Expand Up @@ -312,7 +318,7 @@ impl<'a> Expansion<'a> {
}
})
}
(Some(VariantAttribute::Forward), _) => {
(Some(VariantAttribute::Forward(_)), _) => {
let mut i = 0;
let mut gen_idents = Vec::with_capacity(self.fields.len());
let init = self.expand_fields(|ident, ty, index| {
Expand Down Expand Up @@ -356,7 +362,7 @@ impl<'a> Expansion<'a> {
}
})
}
(Some(VariantAttribute::Skip), _) | (None, true) => {
(Some(VariantAttribute::Skip(_)), _) | (None, true) => {
Ok(TokenStream::new())
}
}
Expand Down
61 changes: 15 additions & 46 deletions impl/src/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::{borrow::Cow, iter};

use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens as _};
use quote::{format_ident, quote, ToTokens as _};
use syn::{
ext::IdentExt as _,
parse::{discouraged::Speculative as _, Parse, ParseStream},
Expand All @@ -14,7 +14,7 @@ use syn::{

use crate::{
parsing::Type,
utils::{polyfill, Either, FieldsExt as _},
utils::{polyfill, skip, Either, FieldsExt as _},
};

/// Expands an [`Into`] derive macro.
Expand Down Expand Up @@ -42,15 +42,18 @@ pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result<TokenStr
.fields
.iter()
.enumerate()
.filter_map(|(i, f)| match SkipFieldAttribute::parse_attrs(&f.attrs) {
Ok(None) => Some(Ok((
&f.ty,
f.ident
.as_ref()
.map_or_else(|| Either::Right(syn::Index::from(i)), Either::Left),
))),
Ok(Some(_)) => None,
Err(e) => Some(Err(e)),
.filter_map(|(i, f)| {
match SkipFieldAttribute::parse_attrs(&f.attrs, &format_ident!("into")) {
Ok(None) => Some(Ok((
&f.ty,
f.ident.as_ref().map_or_else(
|| Either::Right(syn::Index::from(i)),
Either::Left,
),
))),
Ok(Some(_)) => None,
Err(e) => Some(Err(e)),
}
})
.collect::<syn::Result<Vec<_>>>()?;
let (fields_tys, fields_idents): (Vec<_>, Vec<_>) = fields.into_iter().unzip();
Expand Down Expand Up @@ -253,41 +256,7 @@ impl StructAttribute {
}

/// `#[into(skip)]` field attribute.
struct SkipFieldAttribute;

impl SkipFieldAttribute {
/// Parses a [`SkipFieldAttribute`] from the provided [`syn::Attribute`]s.
fn parse_attrs(attrs: impl AsRef<[syn::Attribute]>) -> syn::Result<Option<Self>> {
Ok(attrs
.as_ref()
.iter()
.filter(|attr| attr.path().is_ident("into"))
.try_fold(None, |mut attrs, attr| {
let field_attr = attr.parse_args::<SkipFieldAttribute>()?;
if let Some((path, _)) = attrs.replace((attr.path(), field_attr)) {
Err(syn::Error::new(
path.span(),
"only single `#[into(...)]` attribute is allowed here",
))
} else {
Ok(attrs)
}
})?
.map(|(_, attr)| attr))
}
}

impl Parse for SkipFieldAttribute {
fn parse(content: ParseStream) -> syn::Result<Self> {
match content.parse::<syn::Path>()? {
p if p.is_ident("skip") | p.is_ident("ignore") => Ok(Self),
p => Err(syn::Error::new(
p.span(),
format!("expected `skip`, found: `{}`", p.into_token_stream()),
)),
}
}
}
type SkipFieldAttribute = skip::Attribute;

/// [`Error`]ors for legacy syntax: `#[into(types(i32, "&str"))]`.
fn check_legacy_syntax(
Expand Down
50 changes: 46 additions & 4 deletions impl/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ use syn::{
pub(crate) use self::either::Either;
#[cfg(any(feature = "from", feature = "into"))]
pub(crate) use self::fields_ext::FieldsExt;
#[cfg(feature = "as_ref")]
#[cfg(any(
feature = "as_ref",
feature = "from",
feature = "into",
feature = "debug",
))]
pub(crate) use self::spanning::Spanning;

#[derive(Clone, Copy, Default)]
Expand Down Expand Up @@ -1316,7 +1321,7 @@ pub fn is_type_parameter_used_in_type(
}
}

#[cfg(feature = "as_ref")]
#[cfg(any(feature = "as_ref", feature = "from"))]
pub(crate) mod forward {
use syn::{
parse::{Parse, ParseStream},
Expand Down Expand Up @@ -1370,13 +1375,20 @@ pub(crate) mod forward {
}
}

#[cfg(feature = "as_ref")]
#[cfg(any(
feature = "as_ref",
feature = "from",
feature = "into",
feature = "debug",
MegaBluejay marked this conversation as resolved.
Show resolved Hide resolved
))]
pub(crate) mod skip {
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned as _,
};

use super::Spanning;

/// Representation of a `skip`/`ignore` attribute.
///
/// ```rust,ignore
Expand All @@ -1385,6 +1397,31 @@ pub(crate) mod skip {
/// ```
pub(crate) struct Attribute(&'static str);

impl Attribute {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MegaBluejay why as a separate impl Attribute block? There is one already down bellow.

pub(crate) fn parse_attrs(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MegaBluejay why without docs, again?

attrs: impl AsRef<[syn::Attribute]>,
attr_ident: &syn::Ident,
) -> syn::Result<Option<Spanning<Self>>> {
attrs
.as_ref()
.iter()
.filter(|attr| attr.path().is_ident(attr_ident))
.try_fold(None, |mut attrs, attr| {
let parsed = Spanning::new(attr.parse_args()?, attr.span());
if attrs.replace(parsed).is_some() {
Err(syn::Error::new(
attr.span(),
format!(
"only single `#[{attr_ident}(skip)]`/`#[{attr_ident}(ignore)]` attribute is allowed here"
),
))
} else {
Ok(attrs)
}
})
}
}

impl Parse for Attribute {
fn parse(content: ParseStream<'_>) -> syn::Result<Self> {
match content.parse::<syn::Path>()? {
Expand Down Expand Up @@ -1474,7 +1511,12 @@ mod either {
}
}

#[cfg(feature = "as_ref")]
#[cfg(any(
feature = "as_ref",
feature = "from",
feature = "into",
feature = "debug",
))]
mod spanning {
use std::ops::{Deref, DerefMut};

Expand Down