pub fn ensure_no_ink_identifiers<T>(checked: &T) -> Result<(), syn::Error>
where
T: VisitBy,
{
let mut visitor = private::IdentVisitor::default();
checked.visit_by(&mut visitor);
visitor.into_result()
}
pub trait VisitBy: private::Sealed {
fn visit_by(&self, visitor: &mut private::IdentVisitor);
}
mod private {
use super::VisitBy;
use proc_macro2::Ident;
pub trait Sealed {}
impl Sealed for syn::ItemMod {}
impl Sealed for syn::ItemTrait {}
impl Sealed for syn::ItemFn {}
impl VisitBy for syn::ItemMod {
fn visit_by(&self, visitor: &mut IdentVisitor) {
syn::visit::visit_item_mod(visitor, self);
}
}
impl VisitBy for syn::ItemTrait {
fn visit_by(&self, visitor: &mut IdentVisitor) {
syn::visit::visit_item_trait(visitor, self);
}
}
impl VisitBy for syn::ItemFn {
fn visit_by(&self, visitor: &mut IdentVisitor) {
syn::visit::visit_item_fn(visitor, self);
}
}
#[derive(Default)]
pub struct IdentVisitor {
errors: Vec<syn::Error>,
}
impl IdentVisitor {
pub fn into_result(self) -> Result<(), syn::Error> {
match self.errors.split_first() {
None => Ok(()),
Some((first, rest)) => {
let mut combined = first.clone();
for error in rest {
combined.combine(error.clone());
}
Err(combined)
}
}
}
}
impl<'ast> syn::visit::Visit<'ast> for IdentVisitor {
fn visit_ident(&mut self, ident: &'ast Ident) {
if ident.to_string().starts_with("__ink_") {
self.errors.push(format_err!(
ident,
"encountered invalid identifier starting with __ink_",
))
}
}
}
}