use syn_canon::{CanonicalForm, SourceContext};
fn view(source: &str) -> CanonicalForm {
let file: syn::File = syn::parse_str(source).unwrap_or_else(|e| panic!("{source}: {e}"));
let context = SourceContext::new(core::iter::once((&[][..], &file)));
let (sig, block) = file
.items
.iter()
.find_map(|item| match item {
syn::Item::Fn(f) if f.sig.ident == "f" => Some((&f.sig, &*f.block)),
syn::Item::Impl(i) => i.items.iter().find_map(|member| match member {
syn::ImplItem::Fn(f) if f.sig.ident == "f" => Some((&f.sig, &f.block)),
_ => None,
}),
_ => None,
})
.expect("a function f");
context
.function(sig, block)
.expect("an indexed f")
.canonicalize()
}
#[test]
fn an_enclosing_attribute_macro_reads_the_method_as_written() {
let source = |attr: &str, body: &str| {
format!("struct S;\n{attr}\nimpl S {{\n fn f() -> u8 {{ {body} }}\n}}")
};
assert_ne!(
view(&source("#[observe]", "return 1;")),
view(&source("#[observe]", "1"))
);
assert_eq!(view(&source("", "return 1;")), view(&source("", "1")));
}
#[test]
fn a_module_binding_of_a_std_derive_name_keeps_the_derive_input() {
let source = |scope: &str, derives: &str| {
format!(
"{scope}\nfn f() -> u8 {{\n #[derive({derives})]\n struct P {{ x: u8 }}\n 1\n}}"
)
};
for scope in [
"use count::Debug;",
"use count::*;",
"#[macro_use]\nextern crate count;",
] {
assert_ne!(
view(&source(scope, "Debug, Clone")),
view(&source(scope, "Clone, Debug")),
"{scope}"
);
}
for scope in ["", "use std::fmt::Debug;", "use count::Other;"] {
assert_eq!(
view(&source(scope, "Debug, Clone")),
view(&source(scope, "Clone, Debug")),
"{scope}"
);
}
}
fn module_view(root: &str, module: &str) -> CanonicalForm {
let parse = |source: &str| -> syn::File {
syn::parse_str(source).unwrap_or_else(|e| panic!("{source}: {e}"))
};
let (root, module) = (parse(&format!("mod m;\n{root}")), parse(module));
let path = [String::from("m")];
let context = SourceContext::new([(&[][..], &root), (&path[..], &module)]);
let f = module
.items
.iter()
.find_map(|item| match item {
syn::Item::Fn(f) if f.sig.ident == "f" => Some(f),
_ => None,
})
.expect("a function f");
context
.function(&f.sig, &f.block)
.expect("an indexed f")
.canonicalize()
}
#[test]
fn format_captures_rename_when_the_module_proves_the_std_macro() {
let capture = |module: &str, name: &str| {
module_view(
"",
&format!("{module}\nfn f({name}: u8) -> String {{ format!(\"{{{name}}}\") }}"),
)
};
for module in [
"#[derive(Debug)]\nstruct S;\n#[cfg(test)]\nmod tests {}",
"use std::fmt::*;",
"use super::*;",
] {
assert_eq!(capture(module, "x"), capture(module, "y"), "{module}");
}
for module in ["use count::format;", "use count::*;"] {
assert_ne!(capture(module, "x"), capture(module, "y"), "{module}");
}
}
#[test]
fn a_super_glob_reads_the_formatting_macros_of_the_parent_module() {
let capture = |root: &str, name: &str| {
module_view(
root,
&format!("use super::*;\nfn f({name}: u8) -> String {{ format!(\"{{{name}}}\") }}"),
)
};
for root in ["use count::format;", "use count::*;"] {
assert_ne!(capture(root, "x"), capture(root, "y"), "{root}");
}
assert_eq!(
capture("use count::Other;", "x"),
capture("use count::Other;", "y")
);
let past_root = |name: &str| {
module_view(
"",
&format!(
"use super::super::*;\nfn f({name}: u8) -> String {{ format!(\"{{{name}}}\") }}"
),
)
};
assert_ne!(past_root("x"), past_root("y"));
}