-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Attribute `#[delegate_to(...)]
- Loading branch information
Showing
12 changed files
with
631 additions
and
10 deletions.
There are no files selected for viewing
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
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,30 @@ | ||
use syn::parse::{Parse, ParseStream}; | ||
|
||
pub(crate) struct DelegateToArg { | ||
pub ident: syn::Ident, | ||
#[allow(unused)] | ||
pub fat_arrow_token: syn::token::FatArrow, | ||
pub expr: syn::Expr, | ||
} | ||
|
||
impl Parse for DelegateToArg { | ||
fn parse(input: ParseStream) -> syn::Result<DelegateToArg> { | ||
Ok(DelegateToArg { | ||
ident: input.parse()?, | ||
fat_arrow_token: input.parse()?, | ||
expr: input.parse()?, | ||
}) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use quote::quote; | ||
|
||
#[test] | ||
fn parsable() { | ||
syn::parse2::<DelegateToArg>(quote! { x => &x.0 }).unwrap(); | ||
syn::parse2::<DelegateToArg>(quote! { x => x::x(&x.0) }).unwrap(); | ||
} | ||
} |
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,34 @@ | ||
use syn::visit_mut::VisitMut; | ||
|
||
pub(crate) fn check_non_existence(item: &mut syn::Item) -> syn::Result<()> { | ||
let mut visitor = Visitor { error: None }; | ||
visitor.visit_item_mut(item); | ||
match visitor.error { | ||
None => Ok(()), | ||
Some(e) => Err(e), | ||
} | ||
} | ||
|
||
struct Visitor { | ||
error: Option<syn::Error>, | ||
} | ||
|
||
// Use `visit_*_mut()` as we may need to change enum variant when it matches. | ||
impl VisitMut for Visitor { | ||
fn visit_attribute_mut(&mut self, node: &mut syn::Attribute) { | ||
syn::visit_mut::visit_attribute_mut(self, node); | ||
|
||
#[allow(clippy::single_match)] | ||
match &node.meta { | ||
syn::Meta::List(meta_list) if meta_list.path.is_ident("delegate_to") => { | ||
self.error.get_or_insert_with(|| { | ||
syn::Error::new_spanned( | ||
node, | ||
"#[delegate_to(...)] requires feature flag `unstable_delegate_to`", | ||
) | ||
}); | ||
} | ||
_ => {} | ||
} | ||
} | ||
} |
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,24 @@ | ||
use itertools::Itertools; | ||
use syn::visit_mut::VisitMut; | ||
|
||
pub(crate) fn remove_delegate_to(item: &mut syn::Item) { | ||
let mut visitor = Visitor; | ||
visitor.visit_item_mut(item); | ||
} | ||
|
||
struct Visitor; | ||
|
||
// Use `visit_*_mut()` as we may need to change enum variant when it matches. | ||
impl VisitMut for Visitor { | ||
fn visit_variant_mut(&mut self, node: &mut syn::Variant) { | ||
syn::visit_mut::visit_variant_mut(self, node); | ||
|
||
// TODO: Use `Vec::extract_if()` once it is stabilized. | ||
if let Some((i, _)) = node.attrs.iter().find_position(|attr| | ||
matches!(&attr.meta, syn::Meta::List(meta_list) if meta_list.path.is_ident("delegate_to")) | ||
) { | ||
// Note that it is already checked that `#[delegate_to(...)]` appears at most once. | ||
node.attrs.remove(i); | ||
} | ||
} | ||
} |
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,89 @@ | ||
use syn::visit_mut::VisitMut; | ||
|
||
pub(crate) fn replace_ident_in_expr( | ||
orig: syn::Ident, | ||
subst: syn::Ident, | ||
mut target: syn::Expr, | ||
) -> syn::Expr { | ||
let mut visitor = Visitor { orig, subst }; | ||
visitor.visit_expr_mut(&mut target); | ||
target | ||
} | ||
|
||
struct Visitor { | ||
orig: syn::Ident, | ||
subst: syn::Ident, | ||
} | ||
|
||
// Use `visit_*_mut()` as we may need to change enum variant when it matches. | ||
impl VisitMut for Visitor { | ||
fn visit_expr_mut(&mut self, node: &mut syn::Expr) { | ||
syn::visit_mut::visit_expr_mut(self, node); | ||
|
||
#[allow(clippy::single_match)] | ||
match node { | ||
syn::Expr::Path(expr_path) => { | ||
if expr_path.path.is_ident(&self.orig) { | ||
let path = syn::Path::from(syn::PathSegment::from(self.subst.clone())); | ||
*node = syn::Expr::Path(syn::ExprPath { | ||
attrs: vec![], | ||
qself: None, | ||
path, | ||
}); | ||
} | ||
} | ||
_ => {} | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use quote::{quote, ToTokens}; | ||
|
||
macro_rules! test_replace_ident_in_expr { | ||
( | ||
$test_name:ident, | ||
$orig:expr, | ||
$subst:expr, | ||
$target:expr, | ||
$expected:expr, | ||
) => { | ||
#[test] | ||
fn $test_name() -> Result<(), syn::Error> { | ||
let orig = syn::parse2::<syn::Ident>($orig).unwrap(); | ||
let subst = syn::parse2::<syn::Ident>($subst).unwrap(); | ||
let target = syn::parse2::<syn::Expr>($target).unwrap(); | ||
let expected = syn::parse2::<syn::Expr>($expected).unwrap(); | ||
|
||
let got = replace_ident_in_expr(orig, subst, target.clone()); | ||
assert_eq!( | ||
got, | ||
expected, | ||
"\n got = {},\n expected = {}", | ||
got.to_token_stream(), | ||
expected.to_token_stream(), | ||
); | ||
|
||
Ok(()) | ||
} | ||
}; | ||
} | ||
|
||
test_replace_ident_in_expr! { | ||
ref_0, | ||
quote! { x }, | ||
quote! { y }, | ||
quote! { &x.0 }, | ||
quote! { &y.0 }, | ||
} | ||
|
||
test_replace_ident_in_expr! { | ||
dont_replace_not_is_ident, | ||
quote! { x }, | ||
quote! { y }, | ||
quote! { x::x(&x.0) }, | ||
quote! { x::x(&y.0) }, | ||
} | ||
} |
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
Oops, something went wrong.