use proc_macro_error2::emit_error;
use proc_macro2::{Span, TokenStream};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::visit::Visit;
use syn::visit_mut::{self, VisitMut};
use syn::{Error, ExprField, Member, Result, Token, parse_quote};
#[allow(non_camel_case_types)]
mod kw {
syn::custom_keyword!(error_emitted);
syn::custom_keyword!(expanded_result);
}
pub struct UnscopedInput(TokenStream);
impl Parse for UnscopedInput {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(kw::expanded_result) {
let _: kw::expanded_result = input.parse()?;
Ok(UnscopedInput(input.parse()?))
} else if input.peek(kw::error_emitted) {
let _ = input.parse::<kw::error_emitted>()?;
Ok(UnscopedInput(input.parse()?))
} else {
let msg = "usage invalid outside of #[impl_self], impl_scope! or impl_anon! macro with #[widget] attribute";
Err(Error::new(Span::call_site(), msg))
}
}
}
impl UnscopedInput {
pub fn into_token_stream(self) -> TokenStream {
self.0
}
}
pub struct WidgetInput {
pub ident: Member,
}
impl Parse for WidgetInput {
fn parse(input: ParseStream) -> Result<Self> {
let _ = input.parse::<Token![self]>()?;
let _ = input.parse::<Token![.]>()?;
let ident = input.parse()?;
Ok(WidgetInput { ident })
}
}
struct WidgetIndexVisitor<'a, I: Clone + Iterator<Item = (usize, &'a Member)>> {
children: I,
}
impl<'a, I: Clone + Iterator<Item = (usize, &'a Member)>> VisitMut for WidgetIndexVisitor<'a, I> {
fn visit_macro_mut(&mut self, node: &mut syn::Macro) {
if node.path == parse_quote! { widget_index } {
let args = match syn::parse2::<WidgetInput>(node.tokens.clone()) {
Ok(args) => args,
Err(err) => {
emit_error!(node.tokens.span(), "{}", err);
node.tokens = parse_quote! { error_emitted 0 };
return;
}
};
for (i, child) in self.children.clone() {
if args.ident == *child {
node.tokens = parse_quote! { expanded_result #i };
return;
}
}
emit_error!(args.ident.span(), "does not match any child widget");
node.tokens = parse_quote! { error_emitted 0 };
return;
}
visit_mut::visit_macro_mut(self, node);
}
}
pub fn widget_index<'a, I: Clone + Iterator<Item = (usize, &'a Member)>>(
children: I,
impls: &mut [syn::ItemImpl],
) {
let mut obj = WidgetIndexVisitor { children };
for impl_ in impls {
obj.visit_item_impl_mut(impl_);
}
}
struct CoreAccessDetector<'a> {
core: &'a Member,
detected: bool,
}
impl<'a, 'ast> Visit<'ast> for CoreAccessDetector<'a> {
fn visit_expr_field(&mut self, field: &'ast ExprField) {
if field.base == parse_quote! { self } && field.member == *self.core {
self.detected = true;
}
}
}
pub fn is_core_accessed(core: &Member, block: &syn::Block) -> bool {
let mut obj = CoreAccessDetector {
core,
detected: false,
};
obj.visit_block(block);
obj.detected
}