-
Notifications
You must be signed in to change notification settings - Fork 278
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[refactor] #3830: iroha_ffi_derive: use darling to parse attributes a…
…nd use syn 2.0 WIP: do not fail fast in ffi!, ffi_import and ffi_export WIP: fix clippy lints WIP: remove commented out code WIP: make the presence of generic arguments a fatal error (it leads to dubious errors if codegen is ran) WIP: actually fix clippy lints
- Loading branch information
Showing
21 changed files
with
2,675 additions
and
822 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
//! This module provides parsing of `#[derive(...)]` attributes | ||
|
||
use darling::FromAttributes; | ||
use quote::ToTokens; | ||
use syn2::{punctuated::Punctuated, Attribute, Token}; | ||
|
||
use super::getset::GetSetDerive; | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
pub enum RustcDerive { | ||
Eq, | ||
PartialEq, | ||
Ord, | ||
PartialOrd, | ||
Clone, | ||
Copy, | ||
Hash, | ||
Default, | ||
Debug, | ||
} | ||
|
||
impl RustcDerive { | ||
fn try_from_path(path: &syn2::Path) -> Option<Self> { | ||
let Some(ident) = path.get_ident() else { | ||
return None; | ||
}; | ||
|
||
match ident.to_string().as_str() { | ||
"Eq" => Some(Self::Eq), | ||
"PartialEq" => Some(Self::PartialEq), | ||
"Ord" => Some(Self::Ord), | ||
"PartialOrd" => Some(Self::PartialOrd), | ||
"Clone" => Some(Self::Clone), | ||
"Copy" => Some(Self::Copy), | ||
"Hash" => Some(Self::Hash), | ||
"Default" => Some(Self::Default), | ||
"Debug" => Some(Self::Debug), | ||
_ => None, | ||
} | ||
} | ||
} | ||
|
||
#[allow(variant_size_differences)] // it's not like it's possible to change that.. | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
pub enum Derive { | ||
Rustc(RustcDerive), | ||
GetSet(GetSetDerive), | ||
Other(String), | ||
} | ||
|
||
/// Represents a collection of all `#[derive(...)]` attributes placed on the item | ||
/// | ||
/// NOTE: strictly speaking, correctly parsing this is impossible, since it requires | ||
/// us to resolve the paths in the attributes, which is not possible in a proc-macro context. | ||
/// | ||
/// We just __hope__ that the user refers to the derives by their canonical names (no aliases). | ||
/// | ||
/// This, however, will mistakingly thing that `derive_more` derives are actually rustc's built-in ones. | ||
/// | ||
/// Care should be taken, and it should be documented in the macro APIs that use this. | ||
#[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
pub struct DeriveAttr { | ||
pub derives: Vec<Derive>, | ||
} | ||
|
||
impl FromAttributes for DeriveAttr { | ||
fn from_attributes(attrs: &[Attribute]) -> darling::Result<Self> { | ||
let mut derives = Vec::new(); | ||
let mut accumulator = darling::error::Accumulator::default(); | ||
|
||
for attr in attrs { | ||
if attr.path().is_ident("derive") { | ||
let Some(list) = accumulator.handle(attr.meta.require_list().map_err(Into::into)) else { | ||
continue | ||
}; | ||
let Some(paths) = accumulator.handle( | ||
list.parse_args_with(Punctuated::<syn2::Path, Token![,]>::parse_terminated).map_err(Into::into) | ||
) else { | ||
continue | ||
}; | ||
|
||
for path in paths { | ||
// what clippy suggests here is much harder to read | ||
#[allow(clippy::option_if_let_else)] | ||
let derive = if let Some(derive) = RustcDerive::try_from_path(&path) { | ||
Derive::Rustc(derive) | ||
} else if let Some(derive) = GetSetDerive::try_from_path(&path) { | ||
Derive::GetSet(derive) | ||
} else { | ||
Derive::Other(path.to_token_stream().to_string()) | ||
}; | ||
|
||
// I __think__ it's an error to use the same derive twice | ||
// I don't think we care in this case though | ||
derives.push(derive); | ||
} | ||
} | ||
} | ||
|
||
accumulator.finish_with(Self { derives }) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use darling::FromAttributes; | ||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
use syn2::parse::ParseStream; | ||
|
||
use super::{Derive, DeriveAttr, GetSetDerive, RustcDerive}; | ||
|
||
fn parse_derives(attrs: TokenStream) -> darling::Result<DeriveAttr> { | ||
struct Attributes(Vec<syn2::Attribute>); | ||
|
||
impl syn2::parse::Parse for Attributes { | ||
fn parse(input: ParseStream) -> syn2::Result<Self> { | ||
Ok(Self(input.call(syn2::Attribute::parse_outer)?)) | ||
} | ||
} | ||
|
||
let attrs = syn2::parse2::<Attributes>(attrs) | ||
.expect("Failed to parse tokens as outer attributes") | ||
.0; | ||
DeriveAttr::from_attributes(&attrs) | ||
} | ||
|
||
macro_rules! assert_derive_ok { | ||
($( #[$meta:meta] )*, | ||
$expected:expr | ||
) => { | ||
assert_eq!(parse_derives(quote!( | ||
$( #[$meta] )* | ||
)).unwrap(), | ||
$expected | ||
) | ||
}; | ||
} | ||
|
||
#[test] | ||
fn derive_rustc() { | ||
assert_derive_ok!( | ||
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Default, Debug)], | ||
DeriveAttr { | ||
derives: vec![ | ||
RustcDerive::Eq, | ||
RustcDerive::PartialEq, | ||
RustcDerive::Ord, | ||
RustcDerive::PartialOrd, | ||
RustcDerive::Clone, | ||
RustcDerive::Copy, | ||
RustcDerive::Hash, | ||
RustcDerive::Default, | ||
RustcDerive::Debug, | ||
].into_iter().map(Derive::Rustc).collect(), | ||
} | ||
) | ||
} | ||
|
||
#[test] | ||
fn derive_getset() { | ||
assert_derive_ok!( | ||
#[derive(Getters, Setters, MutGetters, CopyGetters)], | ||
DeriveAttr { | ||
derives: vec![ | ||
GetSetDerive::Getters, | ||
GetSetDerive::Setters, | ||
GetSetDerive::MutGetters, | ||
GetSetDerive::CopyGetters, | ||
].into_iter().map(Derive::GetSet).collect(), | ||
} | ||
) | ||
} | ||
|
||
#[test] | ||
fn derive_unknown() { | ||
assert_derive_ok!( | ||
#[derive(Aboba, Kek)], | ||
DeriveAttr { | ||
derives: vec![ | ||
"Aboba".to_string(), | ||
"Kek".to_string(), | ||
].into_iter().map(Derive::Other).collect(), | ||
} | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
use darling::FromAttributes; | ||
use syn2::Attribute; | ||
|
||
pub struct DocAttrs { | ||
pub attrs: Vec<Attribute>, | ||
} | ||
|
||
impl FromAttributes for DocAttrs { | ||
fn from_attributes(attrs: &[Attribute]) -> darling::Result<Self> { | ||
let mut docs = Vec::new(); | ||
for attr in attrs { | ||
if attr.path().is_ident("doc") { | ||
docs.push(attr.clone()); | ||
} | ||
} | ||
Ok(DocAttrs { attrs: docs }) | ||
} | ||
} |
Oops, something went wrong.