1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#![allow(nonstandard_style, unused_imports)]

//! Internal crate, do not use it directly.

extern crate proc_macro;

use ::core::{mem, ops::Not as _};
use ::proc_macro::TokenStream;
use ::proc_macro2::{
    Span,
    TokenStream as TokenStream2,
};
use ::quote::{
    format_ident,
    quote,
    quote_spanned,
    ToTokens,
};
use ::syn::{*,
    parse::{Parse, Parser, ParseStream},
    punctuated::Punctuated,
    spanned::Spanned,
    visit_mut::VisitMut,
    Result,
};

use self::{
    append_captures_hack_to_impl_occurrences::append_captures_hack_to_impl_occurrences,
    collect_lifetime_params::collect_lifetime_params,
    manually_unsugar_async::manually_unsugar_async,
    params::Params,
};

mod append_captures_hack_to_impl_occurrences;

mod collect_lifetime_params;

mod manually_unsugar_async;

mod params;

#[cfg(feature = "showme")]
mod showme;

/// <span class="stab portability" title="This is supported on crate feature `proc-macros` only"><code>feature = "proc-macros"</code></span>See [the crate docs](https://docs.rs/fix_hidden_lifetime_bug) for more info.
#[proc_macro_attribute] pub
fn fix_hidden_lifetime_bug (
    attrs: TokenStream,
    input: TokenStream,
) -> TokenStream
{
    let Params {
        showme,
    } = parse_macro_input!(attrs);
    let _ = showme;
    match parse_macro_input!(input) {
        | Item::Fn(ItemFn {
            attrs, vis, sig, block,
        }) => {
            let fun = ImplItemMethod {
                attrs, vis, sig, block: *block,
                defaultness: None,
            };
            fix_fn(fun, None).map(ToTokens::into_token_stream)
        },
        | Item::Impl(impl_) => fix_impl(impl_).map(ToTokens::into_token_stream),
        | _ => Err(Error::new(
            Span::call_site(),
            "expected `fn`, or `impl` block",
        )),
    }
    .map(|output| {
        #[cfg(feature = "showme")] {
            if showme.is_some() {
                showme::pretty_print_tokenstream(&output);
                eprintln!("{}", showme::BANNER);
            }
        }
        output
    })
    .unwrap_or_else(|err| err.to_compile_error())
    .into()
}

fn fix_fn (
    mut fun: ImplItemMethod,
    outer_scope: Option<&'_ mut ItemImpl>,
) -> Result<ImplItemMethod>
{
    let ref lifetimes = collect_lifetime_params(&mut fun.sig, outer_scope);
    if fun.sig.asyncness.is_some() {
        fun = manually_unsugar_async(fun);
    }
    append_captures_hack_to_impl_occurrences(
        lifetimes,
        &mut fun.sig.output,
    );
    Ok(fun)
}

fn fix_impl (mut impl_: ItemImpl)
  -> Result<TokenStream2>
{
    if let Some((_, ref trait_, _)) = impl_.trait_ {
        return Err(Error::new_spanned(
            trait_,
            "`#[fix_hidden_lifetime_bug]` does not support traits yet.",
        ));
    }
    let items = mem::replace(&mut impl_.items, vec![]);
    impl_.items = items.into_iter().map(|it| Ok(match it {
        | ImplItem::Method(mut fun) => {
            let mut process_current = false;
            fun.attrs.retain(|attr| (
                attr.path.segments.last().unwrap().ident
                !=
                "fix_hidden_lifetime_bug"
                || {
                    process_current = true;
                    false
                }
            ));
            if process_current {
                fun = fix_fn(fun, Some(&mut impl_))?;
            }
            ImplItem::Method(fun)
        },
        | _ => it,
    })).collect::<Result<_>>()?;
    Ok(impl_.into_token_stream())
}

/* Uncomment to debug panics on parse_quote calls */
// macro_rules! __parse_quote__ {(
//     $($code:tt)*
// ) => (
//     (|| {
//         fn type_of_some<T> (_: Option<T>)
//           -> &'static str
//         {
//             ::core::any::type_name::<T>()
//         }
//         let target_ty = None; if false { return target_ty.unwrap(); }
//         let code = ::quote::quote!( $($code)* );
//         eprintln!(
//             "[{}:{}:{}:parse_quote!]\n  - ty: `{ty}`\n  - code: `{code}`",
//             file!(), line!(), column!(),
//             code = code,
//             ty = type_of_some(target_ty),
//         );
//         ::syn::parse_quote!( #code )
//     })()
// )} use __parse_quote__ as parse_quote;