From 0fb498f1c475691d0a5b92ef606cb9efee8671ae Mon Sep 17 00:00:00 2001 From: michele Date: Sat, 27 Nov 2021 15:34:08 +0100 Subject: [PATCH] #119 Implemented error handling in for generics --- src/error.rs | 88 ++++++++++++++++++++++++++++++- tests/fixture/mod.rs | 30 +++++++++++ tests/resources/fixture/errors.rs | 14 ++++- 3 files changed, 129 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 09e05a2..8c0db23 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,8 +2,8 @@ use std::collections::HashMap; use proc_macro2::TokenStream; -use syn::spanned::Spanned; -use syn::ItemFn; +use syn::{spanned::Spanned, visit::Visit}; +use syn::{visit, ItemFn}; use crate::parse::{ fixture::FixtureInfo, @@ -26,6 +26,7 @@ pub(crate) fn fixture(test: &ItemFn, info: &FixtureInfo) -> TokenStream { missed_arguments(test, info.data.items.iter()) .chain(duplicate_arguments(info.data.items.iter())) .chain(async_once(test, &info)) + .chain(generics_once(test, &info)) .map(|e| e.to_compile_error()) .collect() } @@ -40,6 +41,44 @@ fn async_once<'a>(test: &'a ItemFn, info: &FixtureInfo) -> Errors<'a> { } } +#[derive(Default)] +struct SearchImpl(bool); + +impl<'ast> Visit<'ast> for SearchImpl { + fn visit_type(&mut self, i: &'ast syn::Type) { + if self.0 { + return; + } + match i { + syn::Type::ImplTrait(_) => self.0 = true, + _ => {} + } + visit::visit_type(self, i); + } +} + +impl SearchImpl { + fn function_has_some_impl(f: &ItemFn) -> bool { + let mut s = SearchImpl::default(); + visit::visit_item_fn(&mut s, f); + s.0 + } +} + +fn has_some_generics(test: &ItemFn) -> bool { + test.sig.generics.params.len() > 0 || SearchImpl::function_has_some_impl(test) +} + +fn generics_once<'a>(test: &'a ItemFn, info: &FixtureInfo) -> Errors<'a> { + match (has_some_generics(test), info.attributes.get_once()) { + (true, Some(once)) => Box::new(std::iter::once(syn::Error::new( + once.span(), + "Cannot apply #[once] on generic fixture.", + ))), + _ => Box::new(std::iter::empty()), + } +} + #[derive(Debug, Default)] pub(crate) struct ErrorsVec(Vec); @@ -189,3 +228,48 @@ fn case_args_without_cases(params: &RsTestData) -> Errors { } Box::new(std::iter::empty()) } + +#[cfg(test)] +mod test { + use crate::test::{assert_eq, *}; + use rstest_test::assert_in; + + use super::*; + + #[rstest] + #[case::generics("fn f(){}")] + #[case::const_generics("fn f(){}")] + #[case::lifetimes("fn f<'a>(){}")] + #[case::use_impl_in_answer("fn f() -> impl Iterator{}")] + #[case::use_impl_in_argumets("fn f(it: impl Iterator){}")] + #[should_panic] + #[case::sanity_check_with_no_generics("fn f() {}")] + fn generics_once_should_return_error(#[case] f: &str) { + let f: ItemFn = f.ast(); + let info = FixtureInfo::default().with_once(); + + let errors = generics_once(&f, &info); + + let out = errors + .map(|e| format!("{:?}", e)) + .collect::>() + .join("-----------------------\n"); + + assert_in!(out, "Cannot apply #[once] on generic fixture."); + } + + #[rstest] + #[case::generics("fn f(){}")] + #[case::const_generics("fn f(){}")] + #[case::lifetimes("fn f<'a>(){}")] + #[case::use_impl_in_answer("fn f() -> impl Iterator{}")] + #[case::use_impl_in_argumets("fn f(it: impl Iterator){}")] + fn generics_once_should_not_return_if_no_once(#[case] f: &str) { + let f: ItemFn = f.ast(); + let info = FixtureInfo::default(); + + let errors = generics_once(&f, &info); + + assert_eq!(0, errors.count()); + } +} diff --git a/tests/fixture/mod.rs b/tests/fixture/mod.rs index 169c7f0..9521f1a 100644 --- a/tests/fixture/mod.rs +++ b/tests/fixture/mod.rs @@ -306,5 +306,35 @@ mod should { ) .unindent() ); + + assert_in!( + output.stderr.str(), + format!( + r#" + error: Cannot apply #[once] on generic fixture. + --> {}/src/lib.rs:43:3 + | + 43 | #[once] + | ^^^^ + "#, + name + ) + .unindent() + ); + + assert_in!( + output.stderr.str(), + format!( + r#" + error: Cannot apply #[once] on generic fixture. + --> {}/src/lib.rs:49:3 + | + 49 | #[once] + | ^^^^ + "#, + name + ) + .unindent() + ); } } diff --git a/tests/resources/fixture/errors.rs b/tests/resources/fixture/errors.rs index d465d9e..d185104 100644 --- a/tests/resources/fixture/errors.rs +++ b/tests/resources/fixture/errors.rs @@ -37,4 +37,16 @@ fn error_inject_a_fixture_more_than_once(f: String) { #[fixture] #[once] async fn error_async_once_fixture() { -} \ No newline at end of file +} + +#[fixture] +#[once] +fn error_generics_once_fixture() -> T { + 42 +} + +#[fixture] +#[once] +fn error_generics_once_fixture() -> impl Iterator { + std::iter::once(42) +}