1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use std::collections::HashMap;
use proc_macro2::TokenStream;
use syn::spanned::Spanned;
use syn::ItemFn;
use crate::parse::{
fixture::FixtureInfo,
rstest::{RsTestData, RsTestInfo},
};
use crate::refident::MaybeIdent;
use super::utils::fn_args_has_ident;
pub(crate) fn rstest(test: &ItemFn, info: &RsTestInfo) -> TokenStream {
missed_arguments(test, info.data.items.iter())
.chain(duplicate_arguments(info.data.items.iter()))
.chain(invalid_cases(&info.data))
.chain(case_args_without_cases(&info.data))
.map(|e| e.to_compile_error())
.collect()
}
pub(crate) fn fixture(test: &ItemFn, info: &FixtureInfo) -> TokenStream {
missed_arguments(test, info.data.items.iter())
.chain(duplicate_arguments(info.data.items.iter()))
.map(|e| e.to_compile_error())
.collect()
}
type Errors<'a> = Box<dyn Iterator<Item = syn::Error> + 'a>;
fn missed_arguments<'a, I: MaybeIdent + Spanned + 'a>(
test: &'a ItemFn,
args: impl Iterator<Item = &'a I> + 'a,
) -> Errors<'a> {
Box::new(
args.filter_map(|it| it.maybe_ident().map(|ident| (it, ident)))
.filter(move |(_, ident)| !fn_args_has_ident(test, ident))
.map(|(missed, ident)| {
syn::Error::new(
missed.span(),
&format!(
"Missed argument: '{}' should be a test function argument.",
ident
),
)
}),
)
}
fn duplicate_arguments<'a, I: MaybeIdent + Spanned + 'a>(
args: impl Iterator<Item = &'a I> + 'a,
) -> Errors<'a> {
let mut used = HashMap::new();
Box::new(
args.filter_map(|it| it.maybe_ident().map(|ident| (it, ident)))
.filter_map(move |(it, ident)| {
let name = ident.to_string();
let is_duplicate = used.contains_key(&name);
used.insert(name, it);
match is_duplicate {
true => Some((it, ident)),
false => None,
}
})
.map(|(duplicate, ident)| {
syn::Error::new(
duplicate.span(),
&format!("Duplicate argument: '{}' is already defined.", ident),
)
}),
)
}
fn invalid_cases(params: &RsTestData) -> Errors {
let n_args = params.case_args().count();
Box::new(
params
.cases()
.filter(move |case| case.args.len() != n_args)
.map(|case| {
syn::Error::new_spanned(
&case,
"Wrong case signature: should match the given parameters list.",
)
}),
)
}
fn case_args_without_cases(params: &RsTestData) -> Errors {
if !params.has_cases() {
return Box::new(
params
.case_args()
.map(|a| syn::Error::new(a.span(), "No cases for this argument.")),
);
}
Box::new(std::iter::empty())
}