Skip to content

Commit

Permalink
Close #116 Implemented transitive reference and lifetimes clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
la10736 committed May 9, 2021
1 parent 3e91c4c commit f2ab8bb
Show file tree
Hide file tree
Showing 5 changed files with 311 additions and 155 deletions.
10 changes: 10 additions & 0 deletions src/refident.rs
Expand Up @@ -65,3 +65,13 @@ impl MaybeType for FnArg {
}
}
}

impl MaybeIdent for syn::GenericParam {
fn maybe_ident(&self) -> Option<&Ident> {
match self {
syn::GenericParam::Type(syn::TypeParam { ident, .. })
| syn::GenericParam::Const(syn::ConstParam { ident, .. }) => Some(ident),
syn::GenericParam::Lifetime(syn::LifetimeDef { lifetime, .. }) => Some(&lifetime.ident),
}
}
}
4 changes: 2 additions & 2 deletions src/render/fixture.rs
Expand Up @@ -3,10 +3,10 @@ use syn::{parse_quote, Ident, ItemFn};

use quote::quote;

use super::{generics_clean_up, inject, render_exec_call};
use crate::parse::fixture::FixtureInfo;
use super::{inject, render_exec_call};
use crate::resolver::{self, Resolver};
use crate::utils::{fn_args, fn_args_idents};
use crate::{parse::fixture::FixtureInfo, utils::generics_clean_up};

pub(crate) fn render<'a>(fixture: ItemFn, info: FixtureInfo) -> TokenStream {
let name = &fixture.sig.ident;
Expand Down
85 changes: 1 addition & 84 deletions src/render/mod.rs
Expand Up @@ -6,10 +6,7 @@ use std::collections::HashMap;
use syn::token::Async;

use proc_macro2::{Span, TokenStream};
use syn::{
parse_quote, Attribute, Expr, FnArg, Generics, Ident, ItemFn, Path, ReturnType, Stmt, Type,
WherePredicate,
};
use syn::{parse_quote, Attribute, Expr, FnArg, Ident, ItemFn, Path, ReturnType, Stmt};

use quote::{format_ident, quote};

Expand Down Expand Up @@ -266,86 +263,6 @@ fn trace_arguments<'a>(
}
}

fn where_predicate_bounded_type(wp: &WherePredicate) -> Option<&Type> {
match wp {
syn::WherePredicate::Type(pt) => Some(&pt.bounded_ty),
_ => None,
}
}

//noinspection RsTypeCheck
fn generics_clean_up<'a>(
original: &Generics,
inputs: impl Iterator<Item = &'a FnArg>,
output: &ReturnType,
) -> syn::Generics {
use syn::visit::Visit;
#[derive(Default, Debug)]
struct Used(std::collections::HashSet<proc_macro2::Ident>);
impl<'ast> syn::visit::Visit<'ast> for Used {
fn visit_type_path(&mut self, i: &'ast syn::TypePath) {
if let Some(id) = i.path.get_ident() {
self.0.insert(id.clone());
}
}
fn visit_type_array(&mut self, i: &'ast syn::TypeArray) {
let ep = match &i.len {
syn::Expr::Path(ep) => ep,
_ => return,
};
if let Some(id) = ep.path.get_ident() {
self.0.insert(id.clone());
}
}
}
let mut outs: Used = Default::default();
outs.visit_return_type(output);
inputs.for_each(|fn_arg| outs.visit_fn_arg(fn_arg));
let mut result: Generics = original.clone();
result.params = result
.params
.into_iter()
.filter(|p| match p {
syn::GenericParam::Type(syn::TypeParam { ident, .. })
| syn::GenericParam::Const(syn::ConstParam { ident, .. }) => outs.0.contains(ident),
syn::GenericParam::Lifetime(_) => true,
})
.collect();
result.where_clause.as_mut().map(|mut w| {
w.predicates = w
.predicates
.clone()
.into_iter()
.filter(|wp| {
where_predicate_bounded_type(wp)
.and_then(|t| first_type_path_segment_ident(t))
.map(|t| outs.0.contains(t))
.unwrap_or(true)
})
.collect()
});
result
}

// If type is not self and doesn't starts with :: return the first ident
// of its path segment: only if is a simple path.
// If type is a simple ident just return the this ident. That is useful to
// find the base type for associate type indication
fn first_type_path_segment_ident(t: &Type) -> Option<&Ident> {
match t {
Type::Path(tp) if tp.qself.is_none() && tp.path.leading_colon.is_none() => tp
.path
.segments
.iter()
.nth(0)
.and_then(|ps| match ps.arguments {
syn::PathArguments::None => Some(&ps.ident),
_ => None,
}),
_ => None,
}
}

struct TestCaseRender<'a> {
name: Ident,
attrs: &'a [syn::Attribute],
Expand Down
66 changes: 1 addition & 65 deletions src/render/test.rs
Expand Up @@ -1501,68 +1501,4 @@ mod complete_should {
assert_eq!(attrs, &f.attrs[3..]);
}
}
}

mod generics_clean_up_should {
use super::{assert_eq, *};

#[test]
fn remove_generics_not_in_output() {
// Should remove all generics parameters that are not present in output
let item_fn: ItemFn = r#"
pub fn test<R: AsRef<str>, B, F, H: Iterator<Item=u32>>() -> (H, B, String, &str)
where F: ToString,
B: Borrow<u32>
{ }
"#
.ast();

let expected: ItemFn = r#"
pub fn test<B, H: Iterator<Item=u32>>() -> (H, B, String, &str)
where B: Borrow<u32>
{ }
"#
.ast();

let cleaned = generics_clean_up(
&item_fn.sig.generics,
item_fn.sig.inputs.iter(),
&item_fn.sig.output,
);

assert_eq!(expected.sig.generics, cleaned);
}

#[test]
fn not_remove_generics_used_in_arguments() {
// Should remove all generics parameters that are not present in output
let item_fn: ItemFn = r#"
pub fn test<R: AsRef<str>, B, F, H: Iterator<Item=u32>>
(h: H, it: impl Iterator<Item=R>, j: &[B])
where F: ToString,
B: Borrow<u32>
{ }
"#
.ast();

let expected: ItemFn = r#"
pub fn test<R: AsRef<str>, B, H: Iterator<Item=u32>>
(h: H, it: impl Iterator<Item=R>, j: &[B])
where
B: Borrow<u32>
{ }
"#
.ast();

let cleaned = generics_clean_up(
&item_fn.sig.generics,
item_fn.sig.inputs.iter(),
&item_fn.sig.output,
);

dbg!(item_fn.sig.inputs);

assert_eq!(expected.sig.generics, cleaned);
}
}
}

0 comments on commit f2ab8bb

Please sign in to comment.