Skip to content

Commit

Permalink
Merge pull request #64 from o0Ignition0o/chore/fix_elided_lifetimes_w…
Browse files Browse the repository at this point in the history
…arning

chore: fix elided lifetimes warning.
  • Loading branch information
nrxus authored Dec 16, 2024
2 parents 2ca5d4b + 682a1e9 commit 64445c9
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 11 deletions.
67 changes: 56 additions & 11 deletions faux_macros/src/methods/morphed.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::{methods::receiver::Receiver, self_type::SelfType};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{spanned::Spanned, Generics, Ident, PathArguments, Type, TypePath};
use syn::{
spanned::Spanned, GenericArgument, Generics, Ident, Lifetime, PathArguments, PathSegment, Type,
TypePath,
};

pub struct Signature<'a> {
name: &'a syn::Ident,
Expand Down Expand Up @@ -352,31 +355,40 @@ impl MethodData<'_> {
generics,
..
} = self;
let receiver_ty = &receiver.ty;
let mut receiver_ty = receiver.ty.clone();
add_lifetime(&mut receiver_ty, &syn::parse(quote! { 'm }.into()).unwrap());

let arg_types = arg_types
.iter()
.map(|arg_type| {
let mut typ = arg_type.0.clone();
add_lifetime(&mut typ, &syn::parse(quote! { '_ }.into()).unwrap());
let new_arg_type = WhenArg(&typ);
quote! { #new_arg_type }
})
.collect::<Vec<_>>();

let when_ident =
syn::Ident::new(&format!("_when_{}", name), proc_macro2::Span::call_site());
let faux_ident =
syn::Ident::new(&format!("_faux_{}", name), proc_macro2::Span::call_site());

let empty = syn::parse_quote! { () };
let output = output.unwrap_or(&empty);
let mut output = output.unwrap_or(&empty).clone();
add_lifetime(&mut output, &syn::parse(quote! { '_ }.into()).unwrap());

let name_str = name.to_string();

let generics_contents = if generics.params.is_empty() {
None
} else {
let params = &generics.params;
Some(quote! { , #params })
};
let generic_params = &generics.params;
let generics_contents = quote! { 'm, #generic_params };

let generics_where_clause = &generics.where_clause;

let generic_idents = generic_type_idents(Some(generics.clone()));
let turbofish = turbofish(&generic_idents);

let when_method = syn::parse_quote! {
pub fn #when_ident<'m #generics_contents>(&'m mut self) -> faux::When<'m, #receiver_ty, (#(#arg_types),*), #output, faux::matcher::AnyInvocation> #generics_where_clause {
pub fn #when_ident<#generics_contents>(&'m mut self) -> faux::When<'m, #receiver_ty, (#(#arg_types),*), #output, faux::matcher::AnyInvocation> #generics_where_clause {
match &mut self.0 {
faux::MaybeFaux::Faux(_maybe_faux_faux) => faux::When::new(
<Self>::#faux_ident #turbofish,
Expand All @@ -395,7 +407,7 @@ impl MethodData<'_> {
let faux_method = syn::parse_quote! {
#[allow(clippy::needless_arbitrary_self_type)]
#[allow(clippy::boxed_local)]
pub fn #faux_ident #generics (self: #receiver_ty, _: (#(#arg_types),*)) -> #output #generics_where_clause {
pub fn #faux_ident <#generics_contents> (self: #receiver_ty, _: (#(#arg_types),*)) -> #output #generics_where_clause {
panic!(concat!(#panic_message, "{:?}"), #proxy as *const ())
}
};
Expand All @@ -404,6 +416,39 @@ impl MethodData<'_> {
}
}

// traverse a type, and add the provided lifetime to references that don't have any lifetime yet.
// Eg(pseudocode): `add_lifetime(Result<Option<&str>, ()>, 'a)`
// will return
// `Result<Option<&'a str>, ()>`
fn add_lifetime(t: &mut Type, new_lifetime: &Lifetime) {
match t {
Type::Reference(ref mut reference) => {
let lifetime = &mut reference.lifetime;
if lifetime.is_none() {
lifetime.replace(new_lifetime.clone());
}
}
Type::Path(TypePath {
path: syn::Path { segments, .. },
..
}) => {
if let Some(PathSegment {
arguments: syn::PathArguments::AngleBracketed(ref mut args),
..
}) = segments.last_mut()
{
if let Some(first_arg) = args.args.first_mut() {
match first_arg {
GenericArgument::Type(t) => add_lifetime(t, new_lifetime),
_ => {}
}
}
};
}
_ => {}
}
}

fn unhandled_self_return(spanned: impl Spanned) -> darling::Error {
darling::Error::custom("faux: the return type refers to the mocked struct in a way that faux cannot handle. Split this function into an `impl` block not marked by #[faux::methods]. If you believe this is a mistake or it's a case that should be handled by faux please file an issue").with_span(&spanned)
}
Expand Down
18 changes: 18 additions & 0 deletions tests/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ impl Foo {
pub fn ret_ref(&self, _: &u32) -> &u32 {
&self.a
}

pub fn ret_wrapped(&self, _: &u32) -> Option<&u32> {
Some(&self.a)
}

pub fn ret_wrapped_twice(&self, _: &u32) -> Option<Option<&u32>> {
Some(Some(&self.a))
}
}

fn load_a() -> Result<u32, Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -68,6 +76,16 @@ fn faux_ref_output() {
assert_eq!(*mock.ret_ref(&x), 60);
}

#[test]
fn faux_ref_wrapped_output() {
let mut mock = Foo::faux();
unsafe { faux::when!(mock.ret_wrapped).then_unchecked(|a| Some(a)) };
unsafe { faux::when!(mock.ret_wrapped_twice).then_unchecked(|a| Some(Some(a))) };
let x = 30 + 30;
assert_eq!(*mock.ret_wrapped(&x).unwrap(), 60);
assert_eq!(*mock.ret_wrapped_twice(&x).unwrap().unwrap(), 60);
}

#[test]
#[should_panic(expected = "`Foo::get_stuff` was called but never stubbed")]
fn unmocked_faux_panics() {
Expand Down

0 comments on commit 64445c9

Please sign in to comment.