Skip to content

Commit

Permalink
Update syn to version 2. (#2482)
Browse files Browse the repository at this point in the history
* Update syn to version 2.

* fixed updated config macro

* looking into variadic macro

* fixed varargs

* docs + clippy

* remove comment

* remove comment

* also add variadics to hook_guard_fn

* fix docs

* changelog
  • Loading branch information
meowjesty authored Jun 4, 2024
1 parent 788af2c commit bae7524
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 110 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions changelog.d/1235.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update syn to version 2.
2 changes: 1 addition & 1 deletion mirrord/config/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ proc-macro = true
proc-macro2 = "1"
proc-macro2-diagnostics = "0.10"
quote = "1"
syn = { version = "1", features = ["full", "extra-traits"] }
syn = { version = "2", features = ["full", "extra-traits"] }
238 changes: 138 additions & 100 deletions mirrord/config/derive/src/config/flag.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use proc_macro2::{Span, TokenStream};
use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt};
use quote::{quote, ToTokens};
use syn::{spanned::Spanned, Attribute, Ident, Lit, Meta, NestedMeta};
use syn::{
punctuated::Punctuated, spanned::Spanned, Attribute, Expr, ExprLit, Ident, Lit, Meta,
MetaNameValue, Token,
};

#[derive(Debug, Eq, PartialEq)]
pub enum ConfigFlagsType {
Expand All @@ -17,8 +20,16 @@ pub enum ConfigFlagsType {
pub struct ConfigFlags {
pub doc: Vec<Attribute>,

/// Derive [`struct@Ident`]s that we want the config type to have.
///
/// `#[config(map_to = "Bear", derive = "Metallic")` will produce a type
/// `#[derive(Metallic)] struct Bear {}`
pub derive: Vec<Ident>,
pub generator: Option<Ident>,

/// Generates a mapped type that implements config stuff (?).
///
/// `#[config(map_to = "Bear")` will produce a type `struct Bear {}`
pub map_to: Option<Ident>,

pub default: Option<DefaultFlag>,
Expand All @@ -30,120 +41,147 @@ pub struct ConfigFlags {
pub deprecated: Option<Lit>,
}

/// Retrieves the [`enum@Lit`] that is inside a [`MetaNameValue`].
///
/// The [`enum@Lit`] can be:
/// 1. The `CatFile` in `map_to = "CatFile"`;
/// 2. The `JsonSchema` in `derive = "JsonSchema"`;
/// 3. The `CatUserConfig` in `generator = "CatUserConfig"`;
fn lit_in_meta_name_value(meta: &MetaNameValue) -> Option<Lit> {
if let MetaNameValue {
path: _,
eq_token: _,
value: Expr::Lit(ExprLit { lit, .. }),
} = meta
{
Some(lit.clone())
} else {
None
}
}

impl ConfigFlags {
/// Digs into the [`syn::MetaList`] of the list of [`Attribute`] to fill up our
///[`ConfigFlags`].
pub fn new(attrs: &[Attribute], mode: ConfigFlagsType) -> Result<Self, Diagnostic> {
let mut flags = ConfigFlags {
doc: attrs
.iter()
.filter(|attr| attr.path.is_ident("doc"))
.filter(|attr| attr.path().is_ident("doc"))
.cloned()
.collect(),
..Default::default()
};

for meta in attrs
for meta_list in attrs
.iter()
.filter(|attr| attr.path.is_ident("config"))
.filter_map(|attr| attr.parse_meta().ok())
.filter(|attr| attr.path().is_ident("config"))
.filter_map(|attr| attr.meta.require_list().ok())
{
if let Meta::List(list) = meta {
for meta in list.nested {
match meta {
NestedMeta::Meta(Meta::Path(path))
if mode == ConfigFlagsType::Field && path.is_ident("nested") =>
{
flags.nested = true
}
NestedMeta::Meta(Meta::Path(path))
if mode == ConfigFlagsType::Field && path.is_ident("toggleable") =>
{
flags.toggleable = true
}
NestedMeta::Meta(Meta::Path(path))
if mode == ConfigFlagsType::Field && path.is_ident("unstable") =>
{
flags.unstable = true
}
NestedMeta::Meta(Meta::NameValue(meta))
if mode == ConfigFlagsType::Field && meta.path.is_ident("env") =>
{
flags.env = Some(EnvFlag(meta.lit))
}
NestedMeta::Meta(Meta::Path(path))
if mode == ConfigFlagsType::Field && path.is_ident("default") =>
{
flags.default = Some(DefaultFlag::Flag)
}
NestedMeta::Meta(Meta::NameValue(meta))
if mode == ConfigFlagsType::Field && meta.path.is_ident("default") =>
{
flags.default = Some(DefaultFlag::Value(meta.lit))
}
NestedMeta::Meta(Meta::NameValue(meta))
if mode == ConfigFlagsType::Field && meta.path.is_ident("rename") =>
{
flags.rename = Some(meta.lit)
}
NestedMeta::Meta(Meta::NameValue(meta))
if mode == ConfigFlagsType::Field
&& meta.path.is_ident("deprecated") =>
{
flags.deprecated = Some(meta.lit)
}
NestedMeta::Meta(Meta::NameValue(meta))
if mode == ConfigFlagsType::Container
&& meta.path.is_ident("map_to") =>
{
match meta.lit {
Lit::Str(val) => {
flags.map_to = Some(Ident::new(&val.value(), Span::call_site()))
}
_ => {
return Err(meta
.lit
.span()
.error("map_to should be a string literal as value"))
}
// The more flexible way of parsing nested `Meta` in a `MetaList` is to use
// `parse_nested_meta`, but it's also more troublesome, as it parses the meta
// attribute in a weird way for our usecase.
// Consider something like
// `#[config(map_to = "CatFile", derive = "JsonSchema")]`, it would parse this
// as `Path {map_to, CatFile}` and the rest of the input would be
// `, derive = "JsonSchema"`, which is a bigger hassle to parse.
//
// Also, error handling becomes more problematic with `parse_nested_meta`.
let nested =
meta_list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;

// Let's look into the nested `Meta` to possibly set multiple flags.
for meta in nested {
match meta {
Meta::Path(path)
if mode == ConfigFlagsType::Field && path.is_ident("nested") =>
{
flags.nested = true;
}
Meta::Path(path)
if mode == ConfigFlagsType::Field && path.is_ident("toggleable") =>
{
flags.toggleable = true;
}
Meta::Path(path)
if mode == ConfigFlagsType::Field && path.is_ident("unstable") =>
{
flags.unstable = true;
}
Meta::NameValue(meta)
if mode == ConfigFlagsType::Field && meta.path.is_ident("env") =>
{
flags.env = lit_in_meta_name_value(&meta).map(EnvFlag);
}
Meta::Path(path)
if mode == ConfigFlagsType::Field && path.is_ident("default") =>
{
flags.default = Some(DefaultFlag::Flag);
}
Meta::NameValue(meta)
if mode == ConfigFlagsType::Field && meta.path.is_ident("default") =>
{
flags.default = lit_in_meta_name_value(&meta).map(DefaultFlag::Value);
}
Meta::NameValue(meta)
if mode == ConfigFlagsType::Field && meta.path.is_ident("rename") =>
{
flags.rename = lit_in_meta_name_value(&meta);
}
Meta::NameValue(meta)
if mode == ConfigFlagsType::Field && meta.path.is_ident("deprecated") =>
{
flags.deprecated = lit_in_meta_name_value(&meta);
}
Meta::NameValue(meta)
if mode == ConfigFlagsType::Container && meta.path.is_ident("map_to") =>
{
lit_in_meta_name_value(&meta).map(|lit| match lit {
Lit::Str(val) => {
flags.map_to = Some(Ident::new(&val.value(), Span::call_site()));
Ok(())
}
}
NestedMeta::Meta(Meta::NameValue(meta))
if mode == ConfigFlagsType::Container
&& meta.path.is_ident("derive") =>
{
match meta.lit {
Lit::Str(val) => {
flags.derive.extend(
val.value()
.split(',')
.map(|part| Ident::new(part.trim(), Span::call_site())),
);
}
_ => {
return Err(meta
.lit
.span()
.error("derive should be a string literal as value"))
}
_ => Err(lit
.span()
.error("map_to should be a string literal as value")),
});
}
Meta::NameValue(meta)
if mode == ConfigFlagsType::Container && meta.path.is_ident("derive") =>
{
lit_in_meta_name_value(&meta).map(|lit| match lit {
Lit::Str(val) => {
flags.derive.extend(
val.value()
.split(',')
.map(|part| Ident::new(part.trim(), Span::call_site())),
);
Ok(())
}
}
NestedMeta::Meta(Meta::NameValue(meta))
if mode == ConfigFlagsType::Container
&& meta.path.is_ident("generator") =>
{
match meta.lit {
Lit::Str(val) => {
flags.generator =
Some(Ident::new(&val.value(), Span::call_site()))
}
_ => {
return Err(meta
.lit
.span()
.error("derive should be a string literal as value"))
}
_ => Err(lit
.span()
.error("derive should be a string literal as value")),
});
}
Meta::NameValue(meta)
if mode == ConfigFlagsType::Container
&& meta.path.is_ident("generator") =>
{
lit_in_meta_name_value(&meta).map(|lit| match lit {
Lit::Str(val) => {
flags.generator = Some(Ident::new(&val.value(), Span::call_site()));
Ok(())
}
}
_ => return Err(meta.span().error("unsupported config attribute flag")),
_ => Err(lit
.span()
.error("derive should be a string literal as value")),
});
}
_ => {
return Err(meta.path().span().error(
"unsupported config
attribute flag",
))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion mirrord/layer/macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = { version = "1", features = ["full"] }
syn = { version = "2", features = ["full"] }
27 changes: 24 additions & 3 deletions mirrord/layer/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use proc_macro2::Span;
use quote::quote;
use syn::{parse::Parser, punctuated::Punctuated, token::Comma, Block, Ident, ItemFn};
use syn::{parse::Parser, punctuated::Punctuated, token::Comma, Block, Ident, ItemFn, Type};

/// `#[hook_fn]` annotates the C ffi functions (mirrord's `_detour`s), and is used to generate the
/// following boilerplate (using `close_detour` as an example):
Expand Down Expand Up @@ -49,7 +49,8 @@ pub fn hook_fn(
let unsafety = signature.unsafety;
let abi = signature.abi;

let fn_args = signature
// Function arguments without taking into account variadics!
let mut fn_args = signature
.inputs
.into_iter()
.map(|fn_arg| match fn_arg {
Expand All @@ -58,6 +59,16 @@ pub fn hook_fn(
})
.collect::<Vec<_>>();

// If we have `VaListImpl` args, then we push it to the end of the `fn_args` as
// just `...`.
if signature.variadic.is_some() {
let fixed_arg = quote! {
...
};

fn_args.push(Box::new(Type::Verbatim(fixed_arg)));
}

let return_type = signature.output;

// `unsafe extern "C" fn(i32) -> i32`
Expand Down Expand Up @@ -124,7 +135,7 @@ pub fn hook_guard_fn(
let unsafety = signature.unsafety;
let abi = signature.abi;

let fn_args = signature
let mut fn_args = signature
.inputs
.clone()
.into_iter()
Expand All @@ -134,6 +145,16 @@ pub fn hook_guard_fn(
})
.collect::<Vec<_>>();

// If we have `VaListImpl` args, then we push it to the end of the `fn_args` as
// just `...`.
if signature.variadic.is_some() {
let fixed_arg = quote! {
...
};

fn_args.push(Box::new(Type::Verbatim(fixed_arg)));
}

let fn_arg_names: Punctuated<_, Comma> = signature
.inputs
.into_iter()
Expand Down
4 changes: 2 additions & 2 deletions mirrord/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ proc-macro = true
[dependencies]
proc-macro2 = "1"
proc-macro2-diagnostics = "0.10"
syn = { version = "1", features = ["full", "extra-traits"] }
semver.workspace = true
syn = { version = "2", features = ["full", "extra-traits"] }
semver.workspace = true

0 comments on commit bae7524

Please sign in to comment.