use crate::visitor;
use insta::assert_snapshot;
use quote::quote;
fn expand(tokens: proc_macro2::TokenStream) -> String {
let item = syn::parse2::<syn::ItemImpl>(tokens).unwrap();
let out = visitor::expand(item).expect("visitor expansion failed");
let file = syn::parse2::<syn::File>(out).unwrap();
prettyplease::unparse(&file)
}
#[test]
fn observer_method_gets_return_type_and_descend_tail() {
let out = expand(quote! {
impl Visit for V {
fn visit_color(&mut self, c: &Color) {
self.seen.push(c);
}
}
});
assert_snapshot!("visitor_observer", out);
}
#[test]
fn explicit_return_type_is_untouched() {
let out = expand(quote! {
impl Visit for V {
fn consider_node(&self, q: VisitNode) -> VisitFlow {
VisitFlow::SKIP_CHILDREN
}
}
});
assert_snapshot!("visitor_explicit_return", out);
}
#[test]
fn bare_return_in_observer_is_rewritten() {
let out = expand(quote! {
impl Visit for V {
fn visit_color(&mut self, c: &Color) {
if c.is_empty() {
return;
}
self.seen.push(c);
}
}
});
assert_snapshot!("visitor_bare_return", out);
}
#[test]
fn return_in_nested_closure_is_not_rewritten() {
let out = expand(quote! {
impl Visit for V {
fn visit_color(&mut self, c: &Color) {
let f = || {
return;
};
f();
}
}
});
assert_snapshot!("visitor_nested_closure", out);
}
#[test]
fn explicit_unit_return_opts_out() {
let out = expand(quote! {
impl VisitMut for V {
fn visit_color(&mut self, c: &Color) -> () {
self.seen.push(c);
}
}
});
assert_snapshot!("visitor_unit_optout", out);
}
#[test]
fn impl_attributes_apply_to_both_generated_impls() {
let out = expand(quote! {
#[cfg(feature = "visitor")]
#[allow(dead_code)]
impl Visit for V {}
});
let file = syn::parse_file(&out).unwrap();
let impls = file.items.iter().filter_map(|item| match item {
syn::Item::Impl(item) => Some(item),
_ => None,
});
let mut count = 0;
for item in impls {
count += 1;
assert!(item.attrs.iter().any(|attr| attr.path().is_ident("cfg")));
assert!(item.attrs.iter().any(|attr| attr.path().is_ident("allow")));
}
assert_eq!(count, 2);
}
#[test]
fn rejects_inherent_impl() {
let item = syn::parse2::<syn::ItemImpl>(quote! {
impl V {
fn visit_color(&mut self, c: &Color) {
self.seen.push(c);
}
}
})
.unwrap();
assert!(visitor::expand(item).is_err());
}