#![deny(unconditional_recursion)]
use proc_macro2::{Span, TokenStream};
use syn::{
    punctuated::{Pair, Punctuated},
    Token,
};
pub trait Spanned {
    fn span(&self) -> Span;
    fn set_span(&mut self, span: Span);
    #[inline]
    fn with_span(mut self, span: Span) -> Self
    where
        Self: Sized,
    {
        self.set_span(span);
        self
    }
}
fn _object_safe(_: &dyn Spanned) {}
impl Spanned for Span {
    #[inline]
    fn span(&self) -> Span {
        *self
    }
    #[inline]
    fn set_span(&mut self, span: Span) {
        *self = span;
    }
}
impl Spanned for TokenStream {
    #[inline]
    fn span(&self) -> Span {
        syn::spanned::Spanned::span(self)
    }
    #[inline]
    fn set_span(&mut self, span: Span) {
        *self = self.clone().with_span(span);
    }
    fn with_span(self, span: Span) -> Self {
        self.into_iter()
            .map(|mut tt| {
                tt.set_span(span);
                tt
            })
            .collect()
    }
}
impl<T: Spanned> Spanned for Option<T> {
    #[inline]
    fn span(&self) -> Span {
        match self {
            Some(t) => t.span(),
            None => Span::call_site(),
        }
    }
    #[inline]
    fn set_span(&mut self, span: Span) {
        if let Some(t) = self {
            t.set_span(span);
        }
    }
}
impl<T: ?Sized + Spanned> Spanned for &T {
    #[inline]
    fn span(&self) -> Span {
        (**self).span()
    }
    #[inline]
    #[track_caller]
    fn set_span(&mut self, _span: Span) {
        unimplemented!("cannot set span of borrowed Spanned: {:?}", std::any::type_name::<&T>())
    }
}
impl<T: Spanned> Spanned for [T] {
    #[inline]
    fn span(&self) -> Span {
        join_spans(self)
    }
    #[inline]
    fn set_span(&mut self, span: Span) {
        set_spans(self, span);
    }
}
impl<T: Spanned, P: Spanned> Spanned for Punctuated<T, P> {
    fn span(&self) -> Span {
        join_spans(self.pairs())
    }
    fn set_span(&mut self, span: Span) {
        set_spans(self.pairs_mut(), span);
    }
}
impl<T: Spanned, P: Spanned> Spanned for Pair<T, P> {
    fn span(&self) -> Span {
        let span = self.value().span();
        self.punct().and_then(|punct| span.join(punct.span())).unwrap_or(span)
    }
    fn set_span(&mut self, span: Span) {
        self.value_mut().set_span(span);
        self.punct_mut().set_span(span);
    }
}
macro_rules! deref_impls {
    ($($(#[$attr:meta])* [$($gen:tt)*] $t:ty),+ $(,)?) => {$(
        $(#[$attr])*
        impl<$($gen)*> Spanned for $t {
            #[inline]
            fn span(&self) -> Span {
                (**self).span()
            }
            #[inline]
            fn set_span(&mut self, span: Span) {
                (**self).set_span(span)
            }
        }
    )+};
}
deref_impls! {
    [T: ?Sized + Spanned] &mut T,
    [T: ?Sized + Spanned] Box<T>,
    [T: Spanned] Vec<T>,
}
macro_rules! inherent_impl {
    ($($t:ty),* $(,)?) => {$(
        impl Spanned for $t {
            #[inline]
            fn span(&self) -> Span {
                self.span()
            }
            #[inline]
            fn set_span(&mut self, span: Span) {
                self.set_span(span);
            }
        }
    )*};
}
inherent_impl!(
    proc_macro2::TokenTree,
    proc_macro2::Group,
    proc_macro2::Punct,
    proc_macro2::Ident,
    proc_macro2::Literal,
    syn::Lifetime,
    syn::Lit,
    syn::LitStr,
    syn::LitByteStr,
    syn::LitByte,
    syn::LitChar,
    syn::LitInt,
    syn::LitFloat,
    syn::LitBool,
);
macro_rules! kw_impl {
    ($([$($t:tt)+])+) => { $(kw_impl!($($t)+);)+ };
    (__more $t:tt) => {
        impl Spanned for Token![$t] {
            #[inline]
            fn span(&self) -> Span {
                self.spans.span()
            }
            #[inline]
            fn set_span(&mut self, span: Span) {
                self.spans.set_span(span);
            }
        }
    };
    ($t:tt) => {
        impl Spanned for Token![$t] {
            #[inline]
            fn span(&self) -> Span {
                self.span
            }
            #[inline]
            fn set_span(&mut self, span: Span) {
                self.span = span;
            }
        }
    };
}
kw_impl! {
    [abstract]
    [as]
    [async]
    [auto]
    [await]
    [become]
    [box]
    [break]
    [const]
    [continue]
    [crate]
    [default]
    [do]
    [dyn]
    [else]
    [enum]
    [extern]
    [final]
    [fn]
    [for]
    [if]
    [impl]
    [in]
    [let]
    [loop]
    [macro]
    [match]
    [mod]
    [move]
    [mut]
    [override]
    [priv]
    [pub]
    [ref]
    [return]
    [Self]
    [self]
    [static]
    [struct]
    [super]
    [trait]
    [try]
    [type]
    [typeof]
    [union]
    [unsafe]
    [unsized]
    [use]
    [virtual]
    [where]
    [while]
    [yield]
    [&]
    [__more &&]
    [__more &=]
    [@]
    [^]
    [__more ^=]
    [:]
    [,]
    [$]
    [.]
    [__more ..]
    [__more ...]
    [__more ..=]
    [=]
    [__more ==]
    [__more =>]
    [__more >=]
    [>]
    [__more <-]
    [__more <=]
    [<]
    [-]
    [__more -=]
    [__more !=]
    [!]
    [|]
    [__more |=]
    [__more ||]
    [__more ::]
    [%]
    [__more %=]
    [+]
    [__more +=]
    [#]
    [?]
    [__more ->]
    [;]
    [__more <<]
    [__more <<=]
    [__more >>]
    [__more >>=]
    [/]
    [__more /=]
    [*]
    [__more *=]
    [~]
    [_]
}
macro_rules! delim_impl {
    ($($t:path),* $(,)?) => {$(
        impl Spanned for $t {
            #[inline]
            fn span(&self) -> Span {
                self.span.join()
            }
            #[inline]
            fn set_span(&mut self, span: Span) {
                *self = $t(span);
            }
            #[inline]
            fn with_span(self, span: Span) -> Self {
                $t(span)
            }
        }
    )*};
}
delim_impl!(syn::token::Brace, syn::token::Bracket, syn::token::Paren);
pub fn join_spans<T: Spanned, I: IntoIterator<Item = T>>(items: I) -> Span {
    items
        .into_iter()
        .map(|t| t.span())
        .reduce(|span, other| span.join(other).unwrap_or(span))
        .unwrap_or_else(Span::call_site)
}
pub fn set_spans<T: Spanned, I: IntoIterator<Item = T>>(items: I, span: Span) {
    for mut item in items {
        item.set_span(span);
    }
}