Skip to main content

announce_core/helper/
mod.rs

1//! Helpers for proc-macro crates that use [`announce`](https://docs.rs/announce)
2//! trampolines: `announce::helper`.
3//!
4//! - Receive definitions: [`callback!`] defines a callback that parses its [`Input`].
5//!   [`Input::gather`] asks for more definitions. [`Input::forward`] continues the
6//!   chain.
7//! - Start a chain: [`ContList::invoke`] calls a trampoline from a proc macro.
8//! - Redirect: [`redirect`] forwards a derive or attribute to a renamed trampoline.
9//! - Transform: [`transformer!`] defines a list element that rewrites the newest
10//!   entry.
11//! - Check a definition: [`check`] runs a check: it answers the query and parses its
12//!   [`CheckInput`].
13//!   [`attrs`] has the attributes that a check can own. [`check_type`] and
14//!   [`check_trait`] test for `announce::Announce`.
15//! - Read META: [`Entry::find_meta`] and [`KvArgs`] parse `key = value` lists.
16//!   [`Entry::owned_attrs`] and [`Entry::item_without_owned_attrs`] read owned
17//!   attributes.
18//! - Resolve relative types:
19//!   [`Entry::type_leak`] reads the [`TypeLeak`] table of a definition announced with
20//!   `repeater = ..`. [`TypeLeak::apply`] rewrites an item so that its types resolve
21//!   in any crate.
22//!
23//! The shape of the tokens that a callback receives is described at [`Input`].
24
25pub mod attrs;
26mod kv;
27pub mod leak;
28mod transformer;
29mod typeleak;
30
31pub use attrs::{remove_attributes, CheckCall, OwnedAttrs, Query};
32pub use kv::KvArgs;
33pub use transformer::signature_only;
34/// The version of `type-leak` that [`TypeLeak`] uses. Name it through this crate,
35/// so there is never a second, incompatible copy.
36pub use type_leak;
37pub use typeleak::TypeLeak;
38
39// The macros are `#[macro_export]`ed, so they live at the crate root of
40// `announce-core`; this makes them `announce::helper::callback!` and
41// `announce::helper::transformer!`. Their `$crate` paths point at this module.
42pub use crate::{callback, transformer};
43
44use proc_macro2::{Ident, Span, TokenStream};
45use std::sync::atomic::{AtomicU64, Ordering};
46use syn::ext::IdentExt;
47use syn::parse::{Parse, ParseStream};
48use syn::punctuated::Punctuated;
49use syn::{Attribute, Path, Result, Token};
50use template_quote::{quote, ToTokens};
51
52/// A random identifier `<prefix><16 hex digits>`, unique within the process.
53pub fn gensym(prefix: &str) -> Ident {
54    use std::hash::{BuildHasher, Hasher};
55    static COUNTER: AtomicU64 = AtomicU64::new(0);
56    let mut h = std::collections::hash_map::RandomState::new().build_hasher();
57    h.write_u64(COUNTER.fetch_add(1, Ordering::Relaxed));
58    Ident::new(&format!("{}{:016x}", prefix, h.finish()), Span::call_site())
59}
60
61/// One element of a continuation list (also the `KEY` of an entry).
62#[derive(Clone, Debug)]
63#[allow(clippy::large_enum_variant)]
64pub enum Cont {
65    /// `path::to::mac!`
66    Bang(Path),
67    /// `#[derive(path)] Name` or `#[attr(..)] Name`. `Name` is the dummy enum
68    /// that carries the payload; [`ContList::invoke`] fills it in.
69    Attr {
70        attr: Attribute,
71        name: Option<Ident>,
72    },
73}
74
75impl Cont {
76    /// `path!`. Generic arguments are dropped: `Enum<A>` becomes `Enum!`.
77    pub fn bang(mut path: Path) -> Self {
78        for seg in &mut path.segments {
79            seg.arguments = syn::PathArguments::None;
80        }
81        Cont::Bang(path)
82    }
83
84    /// `#[derive(path)]`
85    pub fn derive(path: Path) -> Self {
86        Cont::Attr {
87            attr: syn::parse_quote!(#[derive(#path)]),
88            name: None,
89        }
90    }
91
92    /// The macro path: `path` of `path!`, the first path of `#[derive(path)]`,
93    /// or the attribute path.
94    pub fn path(&self) -> Path {
95        match self {
96            Cont::Bang(p) => p.clone(),
97            Cont::Attr { attr, .. } => {
98                if attr.path().is_ident("derive") {
99                    let paths =
100                        attr.parse_args_with(Punctuated::<Path, Token![,]>::parse_terminated);
101                    if let Some(p) = paths.ok().and_then(|p| p.into_iter().next()) {
102                        return p;
103                    }
104                }
105                attr.path().clone()
106            }
107        }
108    }
109}
110
111fn parse_macro_path(input: ParseStream) -> Result<Path> {
112    let leading_colon: Option<Token![::]> = input.parse()?;
113    let mut segments = Punctuated::new();
114    loop {
115        let ident = Ident::parse_any(input)?;
116        segments.push_value(syn::PathSegment::from(ident));
117        if input.peek(Token![::]) {
118            segments.push_punct(input.parse()?);
119        } else {
120            break;
121        }
122    }
123    Ok(Path {
124        leading_colon,
125        segments,
126    })
127}
128
129impl Parse for Cont {
130    fn parse(input: ParseStream) -> Result<Self> {
131        if input.peek(Token![#]) {
132            let pound_token = input.parse()?;
133            let content;
134            let bracket_token = syn::bracketed!(content in input);
135            let attr = Attribute {
136                pound_token,
137                style: syn::AttrStyle::Outer,
138                bracket_token,
139                meta: content.parse()?,
140            };
141            let name = if input.peek(syn::Ident) {
142                Some(input.parse()?)
143            } else {
144                None
145            };
146            Ok(Cont::Attr { attr, name })
147        } else {
148            let path = parse_macro_path(input)?;
149            input.parse::<Token![!]>()?;
150            Ok(Cont::Bang(path))
151        }
152    }
153}
154
155impl Cont {
156    /// The comparison key: the tokens without the dummy name of an attribute
157    /// continuation.
158    fn eq_key(&self) -> String {
159        match self {
160            Cont::Bang(_) => self.to_token_stream().to_string(),
161            Cont::Attr { attr, .. } => attr.to_token_stream().to_string(),
162        }
163    }
164}
165
166/// Continuations compare by tokens, ignoring spans and the dummy name.
167impl PartialEq for Cont {
168    fn eq(&self, other: &Self) -> bool {
169        self.eq_key() == other.eq_key()
170    }
171}
172impl Eq for Cont {}
173impl std::hash::Hash for Cont {
174    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
175        self.eq_key().hash(state)
176    }
177}
178
179impl ToTokens for Cont {
180    fn to_tokens(&self, tokens: &mut TokenStream) {
181        match self {
182            Cont::Bang(p) => {
183                // Macro paths cannot carry generic arguments.
184                let mut p = p.clone();
185                for seg in &mut p.segments {
186                    seg.arguments = syn::PathArguments::None;
187                }
188                tokens.extend(quote! { #p ! })
189            }
190            Cont::Attr { attr, name } => tokens.extend(quote! { #attr #name }),
191        }
192    }
193}
194
195/// A continuation list, head first.
196#[derive(Clone, Debug, Default)]
197pub struct ContList(pub Vec<Cont>);
198
199impl Parse for ContList {
200    /// Parses the contents of the brackets: `a!, #[derive(B)], c!`.
201    fn parse(input: ParseStream) -> Result<Self> {
202        let p: Punctuated<Cont, Token![,]> = Punctuated::parse_terminated(input)?;
203        Ok(ContList(p.into_iter().collect()))
204    }
205}
206
207impl ToTokens for ContList {
208    /// Emits `[a!, b!]` including the brackets.
209    fn to_tokens(&self, tokens: &mut TokenStream) {
210        let items = &self.0;
211        tokens.extend(quote! { [ #(#items),* ] });
212    }
213}
214
215impl FromIterator<Cont> for ContList {
216    fn from_iter<I: IntoIterator<Item = Cont>>(iter: I) -> Self {
217        ContList(iter.into_iter().collect())
218    }
219}
220
221/// Whether a global options group (the tokens inside `( .. )` before the
222/// continuation list) puts the chain in expression position. It does when its
223/// **first** token is the identifier `expr`, as in `(expr)` or `(expr, other)`.
224/// The generated trampolines use the same rule, so `(other, expr)` does not select
225/// it in either. Every other option is a namespace for helpers, forwarded
226/// unchanged.
227pub fn is_expression_position(options: &TokenStream) -> bool {
228    matches!(
229        options.clone().into_iter().next(),
230        Some(proc_macro2::TokenTree::Ident(i)) if i == "expr"
231    )
232}
233
234impl ContList {
235    /// Gives every attribute continuation a random dummy name, so two
236    /// trampolines in one module never collide.
237    pub fn name_dummies(&mut self) {
238        for c in &mut self.0 {
239            if let Cont::Attr { name, .. } = c {
240                if name.is_none() {
241                    *name = Some(gensym("__AnnounceT_"));
242                }
243            }
244        }
245    }
246
247    /// Invokes the head with `[self] body`, where `body` is `ENTRY* REST`.
248    pub fn invoke(&self, body: TokenStream) -> TokenStream {
249        self.invoke_with_options(None, body)
250    }
251
252    /// Like [`invoke`](Self::invoke), with a global options group `(options) [..] ..`.
253    /// A derive or attribute head is refused (`compile_error!`) when the group
254    /// starts with `expr` ([`is_expression_position`]).
255    pub fn invoke_with_options(
256        &self,
257        options: Option<&TokenStream>,
258        body: TokenStream,
259    ) -> TokenStream {
260        let opts = options.map(|o| quote! { ( #o ) });
261        let in_expr = options.map_or(false, is_expression_position);
262        let mut list = self.clone();
263        list.name_dummies();
264        match list.0.first() {
265            None => quote! { compile_error! { "announce: empty continuation list" } },
266            Some(Cont::Attr { .. }) if in_expr => quote! {
267                compile_error! { "announce: a derive/attribute continuation emits items, so it cannot be used with `(expr)`" }
268            },
269            Some(Cont::Bang(p)) => quote! { #p ! { #opts #list #body } },
270            Some(Cont::Attr { attr, name }) => quote! {
271                #attr
272                #[allow(dead_code, non_camel_case_types)]
273                enum #name { __AnnouncePayload = (stringify! { #opts #list #body }, 0).1 }
274            },
275        }
276    }
277}
278
279/// One `META` element: `path` or `path(args)`.
280#[derive(Clone, Debug)]
281pub struct Meta {
282    /// `path` of `path(args)`.
283    pub path: Path,
284    /// The tokens inside the parentheses, if any.
285    pub args: Option<TokenStream>,
286}
287
288impl Meta {
289    /// Parses the args with `parser`.
290    pub fn parse_args_with<P: syn::parse::Parser>(&self, parser: P) -> Result<P::Output> {
291        parser.parse2(self.args.clone().unwrap_or_default())
292    }
293
294    /// The args as a [`KvArgs`] list. Absent args give an empty list.
295    pub fn kv(&self) -> Result<KvArgs> {
296        self.parse_args_with(KvArgs::parse)
297    }
298
299    /// The args as `key = value` pairs. Values must be expressions; use
300    /// [`kv`](Self::kv) for types and lists.
301    pub fn name_values(&self) -> Result<Vec<syn::MetaNameValue>> {
302        let p: Punctuated<syn::MetaNameValue, Token![,]> =
303            self.parse_args_with(Punctuated::parse_terminated)?;
304        Ok(p.into_iter().collect())
305    }
306}
307
308impl Parse for Meta {
309    fn parse(input: ParseStream) -> Result<Self> {
310        let path = Path::parse_mod_style(input)?;
311        let args = if input.peek(syn::token::Paren) {
312            let content;
313            syn::parenthesized!(content in input);
314            Some(content.parse()?)
315        } else {
316            None
317        };
318        Ok(Meta { path, args })
319    }
320}
321
322impl ToTokens for Meta {
323    fn to_tokens(&self, tokens: &mut TokenStream) {
324        let p = &self.path;
325        match &self.args {
326            Some(a) => tokens.extend(quote! { #p(#a) }),
327            None => tokens.extend(quote! { #p }),
328        }
329    }
330}
331
332/// `KEY => [META] { ITEM }`
333#[derive(Clone, Debug)]
334pub struct Entry {
335    /// The continuation that produced this entry, as written in the list.
336    pub key: Cont,
337    /// The META elements: checks, `meta =`, `mock(..)`, `type_leak(..)`.
338    pub meta: Vec<Meta>,
339    /// The raw item tokens.
340    pub item: TokenStream,
341}
342
343impl Entry {
344    /// The payload parsed as an item.
345    pub fn item(&self) -> Result<syn::Item> {
346        syn::parse2(self.item.clone())
347    }
348
349    /// The path of the `mock(path)` META element, if there is one: the item stands
350    /// in for the item at that path.
351    pub fn mock(&self) -> Option<Path> {
352        self.meta
353            .iter()
354            .find(|m| m.path.is_ident("mock"))
355            .and_then(|m| syn::parse2(m.args.clone()?).ok())
356    }
357
358    /// The first META element whose last path segment is `name`. Use it to read
359    /// a `meta = lib::info(..)` or a check.
360    pub fn find_meta(&self, name: &str) -> Option<&Meta> {
361        self.meta
362            .iter()
363            .find(|m| m.path.segments.last().map_or(false, |s| s.ident == name))
364    }
365
366    /// The `type_leak(..)` META of an entry announced with `repeater = ..`.
367    /// Unknown keys are ignored.
368    pub fn type_leak(&self) -> Result<Option<TypeLeak>> {
369        let m = match self.find_meta("type_leak") {
370            Some(m) => m,
371            None => return Ok(None),
372        };
373        TypeLeak::from_kv(&mut m.kv()?).map(Some)
374    }
375
376    /// Whether META lists a check whose last path segment is `name`. Only the
377    /// last segment is compared, since the path is as the definer wrote it.
378    pub fn has_check(&self, name: &str) -> bool {
379        self.meta
380            .iter()
381            .any(|m| m.path.segments.last().map_or(false, |s| s.ident == name))
382    }
383
384    /// The attribute names that the definition's checks own: the `attrs(..)` META
385    /// element (empty without one). The definer removed these attributes from the
386    /// item it emitted, but they are still in [`item`](Self::item), where a
387    /// callback can read them ([`OwnedAttrs::find`]).
388    ///
389    /// A callback that emits the item itself must remove them first, because rustc
390    /// rejects an attribute that no macro declares. Use
391    /// [`item_without_owned_attrs`](Self::item_without_owned_attrs).
392    pub fn owned_attrs(&self) -> OwnedAttrs {
393        attrs::owned_from_metas(&self.meta)
394    }
395
396    /// The payload parsed as an item, without the owned attributes: the item as the
397    /// definer emitted it. The same as [`item`](Self::item) when the definition has
398    /// no owned attributes.
399    pub fn item_without_owned_attrs(&self) -> Result<syn::Item> {
400        let mut item = self.item()?;
401        self.owned_attrs().strip(&mut item)?;
402        Ok(item)
403    }
404}
405
406impl Parse for Entry {
407    fn parse(input: ParseStream) -> Result<Self> {
408        let key = input.parse()?;
409        input.parse::<Token![=>]>()?;
410        let meta_content;
411        syn::bracketed!(meta_content in input);
412        let meta: Punctuated<Meta, Token![,]> = Punctuated::parse_terminated(&meta_content)?;
413        let item_content;
414        syn::braced!(item_content in input);
415        Ok(Entry {
416            key,
417            meta: meta.into_iter().collect(),
418            item: item_content.parse()?,
419        })
420    }
421}
422
423impl ToTokens for Entry {
424    fn to_tokens(&self, tokens: &mut TokenStream) {
425        let (key, meta, item) = (&self.key, &self.meta, &self.item);
426        tokens.extend(quote! { #key => [ #(#meta),* ] { #item } });
427    }
428}
429
430/// What a callback receives: `(OPTIONS)? [LIST] ENTRY* REST`.
431///
432/// # The wire format
433///
434/// A callback receives these tokens:
435///
436/// ```text
437/// (OPTIONS)? [LIST] ENTRY* REST
438/// ```
439///
440/// - `OPTIONS`: global options, forwarded unchanged by every hop. Only a leading
441///   `expr` has a meaning: it puts the chain in expression position, where derive
442///   and attribute elements are refused ([`is_expression_position`]). The rest is
443///   free for helpers ([`Input::has_option`]).
444/// - `LIST`: the *continuation list*, the calls still to make, head first. The head
445///   is the macro that is called. An element ([`Cont`]) is `path!`,
446///   `#[derive(path)] Name` or `#[attr] Name`. The last two are the derive and
447///   attribute route: `Name` is a dummy enum that carries the tokens, and a derive or
448///   attribute macro hands them on with [`redirect`].
449/// - `ENTRY`: `KEY => [META] { ITEM }`, one per definition ([`Entry`]). `KEY` is the
450///   list element that produced the entry, as written.
451/// - `REST`: tokens that the protocol never touches. A parser reads entries greedily,
452///   so wrap a `REST` that starts like an entry.
453///
454/// How a chain runs:
455///
456/// - Each macro drops its own element (the head) from the list, adds an entry, and
457///   calls the next element with the rest of the list. The last element is the
458///   callback: it runs once, with one entry per trampoline.
459/// - New entries go in front, so they are newest first.
460///   [`Input::entries_in_order`] gives oldest first.
461/// - `[META]` is always written, even when empty (`[]`).
462/// - A trampoline can get the prefixes `@__announce_self { PATH }` and
463///   `@__announce_dollar { $ }` before this input. Only trampolines and [`redirect`]
464///   use them. Parsing an [`Input`] skips them.
465///
466/// `META` holds these elements, as the definer wrote them:
467///
468/// - `path` or `path(args)`: a check (`#[announce(path(args))]`);
469/// - `path(args)`: a `meta = path(args)` entry, never called ([`Entry::find_meta`]);
470/// - `mock(path)`: the item stands in for the item at `path` ([`Entry::mock`]);
471/// - `attrs(a, b)`: the attribute names that checks own ([`Entry::owned_attrs`]);
472/// - `type_leak(..)`: the type table of a `repeater` ([`Entry::type_leak`]).
473///
474/// Ignore elements you do not know.
475///
476/// ```
477/// # use announce_core as announce;
478/// use announce::helper::Input;
479///
480/// let input: Input = syn::parse_quote! {
481///     [cb!]
482///     a::F! => [my_lib::check, mock(::core::iter::Iterator)] { pub trait F {} }
483///     REST tokens
484/// };
485/// let entry = &input.entries[0];
486/// assert!(entry.has_check("check"));
487/// assert!(entry.mock().is_some());
488/// assert_eq!(input.rest.to_string(), "REST tokens");
489/// ```
490///
491/// # A real example
492///
493/// This is what a callback gets from a real chain, captured by the test
494/// `tests/user/tests/wire.rs`. The definitions, in `tests/defs/src/wire.rs`
495/// (`Meters` is private to the defining crate, so `Length` has a `repeater`):
496///
497/// ```ignore
498/// ::announce::define_repeater!(pub Repeater);
499///
500/// /// A point.
501/// #[announce(announce_test_derive::lib_check, meta = crate::info(1))]
502/// pub struct Point {
503///     /// The x coordinate.
504///     #[my_lib(skip)]
505///     pub x: u8,
506///     pub y: u8,
507/// }
508///
509/// #[announce(repeater = crate::wire::Repeater)]
510/// pub struct Length(pub Meters);
511/// ```
512///
513/// The chain, in another crate. `show!` is the callback; it prints its whole input:
514///
515/// ```ignore
516/// use announce_test_defs::wire as defs;
517///
518/// macro_rules! show {
519///     ($($t:tt)*) => { stringify!($($t)*) };
520/// }
521///
522/// let wire: &str = defs::Point! { (expr) [defs::Point!, defs::Length!, show!] some rest };
523/// ```
524///
525/// `wire` is this text. The tokens are the ones received, in order. Only the
526/// layout is added by hand, and the random number of `id` (a different one for each
527/// build) is replaced by `ID`. The test compares this block with what the chain
528/// gives, so it cannot drift:
529///
530/// <!-- wire-example -->
531/// ```text
532/// (expr)
533/// [show!]
534/// defs::Length! => [
535///     type_leak(repeater = $crate::wire::Repeater, id = ID,
536///               context = 0, referrer = ["type-leak-format-14" (Meters)])
537/// ] {
538///     pub struct Length(pub Meters);
539/// }
540/// defs::Point! => [
541///     announce_test_derive::lib_check,
542///     $crate::info(1),
543///     attrs(my_lib)
544/// ] {
545///     pub struct Point {
546///         #[my_lib(skip)]
547///         pub x: u8,
548///         pub y: u8,
549///     }
550/// }
551/// some rest
552/// ```
553///
554/// How the parts map to the fields of `Input`:
555///
556/// | Tokens | Field | Notes |
557/// |---|---|---|
558/// | `(expr)` | [`options`](Input::options) | From the call, forwarded by both hops. |
559/// | `[show!]` | [`list`](Input::list) | Only the callback is left: `Point!` and `Length!` each dropped themselves. |
560/// | `defs::Length! => [..] { .. }` | `entries[0]` | The newest entry, the last hop. Its `key` is as written in the call. |
561/// | `defs::Point! => [..] { .. }` | `entries[1]` | The oldest entry. |
562/// | `some rest` | [`rest`](Input::rest) | The caller's tokens, untouched. |
563///
564/// What is in the entries:
565///
566/// - `Length`: `type_leak(..)` is the table of the `repeater` ([`Entry::type_leak`]).
567///   The item still says `Meters`, which only the defining crate can resolve;
568///   the table is what a callback uses to write the type from anywhere
569///   ([`TypeLeak::apply`]).
570/// - `Point`: `announce_test_derive::lib_check` is the check, `$crate::info(1)` is
571///   `meta = crate::info(1)` (`crate` became `$crate`, so that the path resolves where
572///   the callback runs), and `attrs(my_lib)` lists the attribute names that the
573///   check owns.
574/// - The doc comments of `Point` are gone from the item, because the payload has
575///   no docs unless `docs = true`. `#[my_lib(skip)]` is still there: the definer
576///   removed it from the item it emitted, but the callback can read it.
577#[derive(Clone, Debug)]
578pub struct Input {
579    /// The global options group `(..)`. [`forward`](Self::forward) passes it on
580    /// unchanged. [`fetch`](Self::fetch) and [`gather`](Self::gather) add
581    /// `__announce_fetched(..)` to it, which records what they fetched (to detect
582    /// a continuation that is not a trampoline).
583    pub options: Option<TokenStream>,
584    /// The continuation list; its head is the callback itself.
585    pub list: ContList,
586    /// Newest first.
587    pub entries: Vec<Entry>,
588    /// REST: tokens after the entries, untouched by the protocol.
589    pub rest: TokenStream,
590}
591
592impl Parse for Input {
593    fn parse(input: ParseStream) -> Result<Self> {
594        // The `@__announce_self { path }` and `@__announce_dollar { $ }`
595        // prefixes are meant for trampolines; skip them if a callback
596        // receives them.
597        while input.peek(Token![@]) {
598            let fork = input.fork();
599            let _: Token![@] = fork.parse()?;
600            match fork.parse::<Ident>() {
601                Ok(i) if i == "__announce_dollar" || i == "__announce_self" => {
602                    let _g: proc_macro2::Group = fork.parse()?;
603                    syn::parse::discouraged::Speculative::advance_to(input, &fork);
604                }
605                _ => break,
606            }
607        }
608        let options = if input.peek(syn::token::Paren) {
609            let o;
610            syn::parenthesized!(o in input);
611            Some(o.parse()?)
612        } else {
613            None
614        };
615        let content;
616        syn::bracketed!(content in input);
617        let list = content.parse()?;
618        let mut entries = Vec::new();
619        loop {
620            let fork = input.fork();
621            match fork.parse::<Entry>() {
622                Ok(e) => {
623                    entries.push(e);
624                    syn::parse::discouraged::Speculative::advance_to(input, &fork);
625                }
626                Err(e) => {
627                    // `KEY =>` means an entry was intended: report it instead
628                    // of silently treating it as REST.
629                    // (`KEY => [` or `KEY => {` only; `foo! => bar` is valid
630                    // REST.)
631                    let probe = input.fork();
632                    if probe.parse::<Cont>().is_ok()
633                        && probe.parse::<Token![=>]>().is_ok()
634                        && (probe.peek(syn::token::Bracket) || probe.peek(syn::token::Brace))
635                    {
636                        return Err(syn::Error::new(
637                            e.span(),
638                            format!("announce: malformed entry: {}", e),
639                        ));
640                    }
641                    break;
642                }
643            }
644        }
645        Ok(Input {
646            options,
647            list,
648            entries,
649            rest: input.parse()?,
650        })
651    }
652}
653
654impl Input {
655    /// Entries in the order their definitions were visited (oldest first).
656    pub fn entries_in_order(&self) -> impl Iterator<Item = &Entry> {
657        self.entries.iter().rev()
658    }
659
660    /// Fetches more definitions, then calls this callback again. It invokes
661    /// `new[0]` with the list `[new.., <this list>]`, so the new entries arrive in
662    /// front of the old ones.
663    ///
664    /// The paths are recorded in the options group as `__announce_fetched(..)`. If
665    /// this callback is called again with a recorded path but no entry for it, that
666    /// path is not an `#[announce]` trampoline. Then `fetch` emits a
667    /// `compile_error!` instead of fetching until the recursion limit. A callback
668    /// that fetches only what is still missing (as [`gather`](Self::gather) does)
669    /// never meets that error for a trampoline.
670    pub fn fetch(&self, new: impl IntoIterator<Item = Cont>) -> TokenStream {
671        self.fetch_conts(new.into_iter().collect())
672    }
673
674    /// The bookkeeping of [`fetch`](Self::fetch) and [`gather`](Self::gather):
675    /// refuses a continuation that was fetched before and left no entry,
676    /// records the new ones in the options group, and invokes `[new.., <list>]`.
677    fn fetch_conts(&self, new: Vec<Cont>) -> TokenStream {
678        // Already fetched once, but no entry came back: not a trampoline. The
679        // bookkeeping travels in the options group.
680        let fetched = self.fetched_paths();
681        if let Some(c) = new
682            .iter()
683            .find(|c| fetched.contains(&path_key(&c.path())) && self.entry_for(c).is_none())
684        {
685            let msg = format!(
686                "announce: `{}` was fetched but did not provide a definition; is it an `#[announce]` trampoline?",
687                c.to_token_stream()
688            );
689            return quote! { compile_error! { #msg } };
690        }
691        let mut options: Vec<TokenStream> = self.option_items();
692        if !new.is_empty() {
693            let paths = new.iter().map(|c| c.path());
694            options.push(quote! { __announce_fetched(#(#paths),*) });
695        }
696        let opts = if options.is_empty() {
697            None
698        } else {
699            Some(quote! { #(#options),* })
700        };
701        let list: ContList = new.into_iter().chain(self.list.0.iter().cloned()).collect();
702        let (entries, rest) = (&self.entries, &self.rest);
703        list.invoke_with_options(opts.as_ref(), quote! { #(#entries)* #rest })
704    }
705
706    /// Whether the global options group contains the ident `name`.
707    pub fn has_option(&self, name: &str) -> bool {
708        self.options.as_ref().map_or(false, |o| {
709            o.clone()
710                .into_iter()
711                .any(|tt| matches!(tt, proc_macro2::TokenTree::Ident(i) if i == name))
712        })
713    }
714
715    /// The comma-separated elements of the options group.
716    pub fn option_items(&self) -> Vec<TokenStream> {
717        let mut items = Vec::new();
718        let mut cur = TokenStream::new();
719        for tt in self.options.clone().unwrap_or_default() {
720            if matches!(&tt, proc_macro2::TokenTree::Punct(p) if p.as_char() == ',') {
721                if !cur.is_empty() {
722                    items.push(std::mem::take(&mut cur));
723                }
724            } else {
725                cur.extend(Some(tt));
726            }
727        }
728        if !cur.is_empty() {
729            items.push(cur);
730        }
731        items
732    }
733
734    fn fetched_paths(&self) -> Vec<String> {
735        let mut out = Vec::new();
736        for item in self.option_items() {
737            let parsed: Result<Meta> = syn::parse2(item);
738            if let Ok(m) = parsed {
739                if m.path.is_ident("__announce_fetched") {
740                    let paths: Result<Punctuated<Path, Token![,]>> =
741                        m.parse_args_with(Punctuated::parse_terminated);
742                    if let Ok(paths) = paths {
743                        out.extend(paths.iter().map(path_key));
744                    }
745                }
746            }
747        }
748        out
749    }
750
751    /// Parses REST as `T`.
752    pub fn rest_as<T: Parse>(&self) -> Result<T> {
753        syn::parse2(self.rest.clone())
754    }
755
756    /// The entry whose key names `cont` (compared by macro path, with generic
757    /// arguments ignored), newest first.
758    pub fn entry_for(&self, cont: &Cont) -> Option<&Entry> {
759        let want = path_key(&cont.path());
760        self.entries
761            .iter()
762            .find(|e| path_key(&e.key.path()) == want)
763    }
764
765    /// Asks for definitions: "give me these, then call me again".
766    ///
767    /// Returns [`Gather::Ready`] with one entry per requested continuation, in
768    /// order, when all are present. Otherwise it returns [`Gather::Fetch`]: tokens
769    /// that fetch the missing ones and call this callback again.
770    ///
771    /// ```ignore
772    /// let entries = match input.gather(needed) {
773    ///     Gather::Ready(e) => e,
774    ///     Gather::Fetch(ts) => return Ok(ts),
775    /// };
776    /// ```
777    pub fn gather(&self, needed: impl IntoIterator<Item = Cont>) -> Gather<'_> {
778        let needed: Vec<Cont> = needed.into_iter().collect();
779        // A callback asking for itself (or for its own head path) would
780        // re-fetch forever.
781        if let Some(head) = self.list.0.first() {
782            if let Some(c) = needed
783                .iter()
784                .find(|c| path_key(&c.path()) == path_key(&head.path()))
785            {
786                let msg = format!(
787                    "announce: `gather` asked for `{}`, which is the callback itself; \
788                     request the definitions' trampolines instead",
789                    c.to_token_stream()
790                );
791                return Gather::Fetch(quote! { compile_error! { #msg } });
792            }
793        }
794        let mut missing: Vec<Cont> = Vec::new();
795        for c in &needed {
796            if self.entry_for(c).is_none()
797                && !missing
798                    .iter()
799                    .any(|m| path_key(&m.path()) == path_key(&c.path()))
800            {
801                missing.push(c.clone());
802            }
803        }
804        if missing.is_empty() {
805            return Gather::Ready(
806                needed
807                    .iter()
808                    .map(|c| self.entry_for(c).expect("checked above"))
809                    .collect(),
810            );
811        }
812        Gather::Fetch(self.fetch_conts(missing))
813    }
814
815    /// Continues the chain: drops the head and invokes the next element. Returns
816    /// `None` if the head was the last element.
817    pub fn forward(&self) -> Option<TokenStream> {
818        if self.list.0.len() < 2 {
819            return None;
820        }
821        let tail: ContList = self.list.0[1..].iter().cloned().collect();
822        let (entries, rest) = (&self.entries, &self.rest);
823        Some(tail.invoke_with_options(self.options.as_ref(), quote! { #(#entries)* #rest }))
824    }
825}
826
827/// Result of [`Input::gather`].
828pub enum Gather<'a> {
829    /// All requested definitions are present, in the requested order.
830    Ready(Vec<&'a Entry>),
831    /// Emit these tokens: they fetch the missing definitions and call back.
832    Fetch(TokenStream),
833}
834
835fn path_key(p: &Path) -> String {
836    let mut p = p.clone();
837    for seg in &mut p.segments {
838        seg.arguments = syn::PathArguments::None;
839    }
840    p.to_token_stream().to_string()
841}
842
843#[doc(hidden)]
844pub mod __private {
845    pub use proc_macro2;
846    pub use syn;
847}
848
849/// Defines a `#[proc_macro]` callback that parses its [`Input`] and turns
850/// `syn::Error`s into compile errors. The body returns
851/// `syn::Result<proc_macro2::TokenStream>`.
852///
853/// ```ignore
854/// announce::helper::callback! {
855///     /// Receives trait definitions.
856///     pub fn my_callback(input) {
857///         let entries = match input.gather(needed(&input)?) {
858///             announce::helper::Gather::Ready(e) => e,
859///             announce::helper::Gather::Fetch(ts) => return Ok(ts),
860///         };
861///         generate(entries)
862///     }
863/// }
864/// ```
865///
866/// The facade crate must re-export the callback (e.g. under `__private`) so
867/// that generated code can name it.
868#[macro_export]
869macro_rules! callback {
870    ($(#[$m:meta])* $vis:vis fn $name:ident ($input:ident) $body:block) => {
871        $(#[$m])*
872        #[proc_macro]
873        $vis fn $name(__announce_ts: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
874            let __announce_run = |__announce_ts: ::proc_macro::TokenStream|
875             -> $crate::helper::__private::syn::Result<$crate::helper::__private::proc_macro2::TokenStream> {
876                let $input: $crate::helper::Input = $crate::helper::__private::syn::parse(__announce_ts)?;
877                $body
878            };
879            match __announce_run(__announce_ts) {
880                ::core::result::Result::Ok(ts) => ts.into(),
881                ::core::result::Result::Err(e) => e.to_compile_error().into(),
882            }
883        }
884    };
885}
886
887/// Runs a check: the body of a `#[proc_macro]` that runs where an item is announced,
888/// and that answers `#[announce]`'s attribute query.
889///
890/// - `input` is the input of the proc macro: either the attribute query
891///   `@__announce_attrs [..] { .. }` or the check's own input, `(ARGS), ITEM`
892///   ([`CheckCall`]).
893/// - `attributes` lists the attribute names that the check owns:
894///   `#[announce(my_lib::my_check)]` then lets the item carry `#[my_lib]`,
895///   `#[my_lib(..)]` and `#[my_lib::..]` attributes. It may be empty, for a check that
896///   owns none: the check still answers the query. See [`attrs`] for the query and
897///   for what a name matches.
898/// - `f` is the check itself. It gets the parsed [`CheckInput`] (`args`, `item` with
899///   the owned attributes, and `owned`, which is `attributes`) and returns the tokens
900///   to emit, or a `syn::Error`. A check usually returns nothing, or an `Err`.
901///
902/// The query is answered with `attributes` ([`Query::answer`]) and `f` is not called.
903/// For the check's own input, the result is what `f` returns. A `syn::Error` (the
904/// input does not parse, a name in `attributes` is not an identifier or is reserved
905/// ([`OwnedAttrs::check_names`]), or `f` fails) becomes a `compile_error!`, like in
906/// [`callback!`]. So the result can be returned from the proc macro as it is.
907///
908/// ```ignore
909/// /// Rejects `#[my_lib(bad)]` and tuple structs.
910/// #[proc_macro]
911/// pub fn my_check(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
912///     announce::helper::check(input.into(), &["my_lib", "other"], |c| {
913///         // `c` is an `announce::helper::CheckInput`.
914///         for attr in c.owned_attributes() {
915///             if attr.path().is_ident("my_lib") && attr.parse_args::<syn::Ident>().map_or(false, |i| i == "bad") {
916///                 return Err(syn::Error::new_spanned(&attr, "`bad` is not allowed"));
917///             }
918///         }
919///         Ok(proc_macro2::TokenStream::new())
920///     })
921///     .into()
922/// }
923/// ```
924///
925/// The function can be tested with `proc_macro2` streams, because it does not use the
926/// `proc_macro` crate:
927///
928/// ```
929/// # use announce_core as announce;
930/// use announce::helper::check;
931/// use proc_macro2::TokenStream;
932///
933/// let run = |input: TokenStream| {
934///     check(input, &["my_lib"], |c| {
935///         match &c.item {
936///             syn::Item::Struct(s) if matches!(s.fields, syn::Fields::Unnamed(_)) => {
937///                 Err(syn::Error::new_spanned(&s.fields, "tuple structs are not supported"))
938///             }
939///             _ => Ok(TokenStream::new()),
940///         }
941///     })
942///     .to_string()
943/// };
944/// // The check itself.
945/// assert_eq!(run(syn::parse_quote! { (), struct S { a: u8 } }), "");
946/// assert!(run(syn::parse_quote! { (), struct S(u8); }).contains("compile_error"));
947/// // The query is answered with the names, and the check is not run.
948/// let out = run(syn::parse_quote! { @__announce_attrs [ me!, next! ] { st } });
949/// assert!(out.contains("next !") && out.contains("attrs (my_lib)"), "{}", out);
950/// ```
951pub fn check(
952    input: TokenStream,
953    attributes: &[&str],
954    f: impl FnOnce(&mut CheckInput) -> Result<TokenStream>,
955) -> TokenStream {
956    let run = || -> Result<TokenStream> {
957        for name in attributes {
958            if !is_attribute_name(name) {
959                return Err(syn::Error::new(
960                    Span::call_site(),
961                    format!(
962                        "announce: `{}` is not an identifier: it cannot be an owned attribute name",
963                        name
964                    ),
965                ));
966            }
967        }
968        let owned = OwnedAttrs::from_strs(attributes);
969        match syn::parse2::<CheckCall>(input)? {
970            // `answer` reports a reserved name where the check answers.
971            CheckCall::Query(q) => Ok(q.answer(&owned)),
972            CheckCall::Run(c) => {
973                owned.check_names()?;
974                f(&mut c.with_owned(owned))
975            }
976        }
977    };
978    run().unwrap_or_else(|e| e.to_compile_error())
979}
980
981/// Whether `name` (or `r#name`) can be turned into an `Ident` without a panic.
982fn is_attribute_name(name: &str) -> bool {
983    let name = name.strip_prefix("r#").unwrap_or(name);
984    let mut chars = name.chars();
985    matches!(chars.next(), Some(c) if c == '_' || c.is_alphabetic())
986        && chars.all(|c| c == '_' || c.is_alphanumeric())
987        && name != "_"
988}
989
990/// Extracts the trampoline payload from the dummy enum that a derive or
991/// attribute continuation receives. Spans, hygiene and `$crate` survive.
992pub fn payload(item: TokenStream) -> Option<TokenStream> {
993    let di: syn::DeriveInput = syn::parse2(item).ok()?;
994    let e = match di.data {
995        syn::Data::Enum(e) => e,
996        _ => return None,
997    };
998    let v = e.variants.first()?;
999    if v.ident != "__AnnouncePayload" || e.variants.len() != 1 {
1000        return None;
1001    }
1002    let f = match &v.discriminant.as_ref()?.1 {
1003        syn::Expr::Field(f) => f,
1004        _ => return None,
1005    };
1006    let t = match &*f.base {
1007        syn::Expr::Tuple(t) => t,
1008        _ => return None,
1009    };
1010    let m = match t.elems.first()? {
1011        syn::Expr::Macro(m) => m,
1012        _ => return None,
1013    };
1014    if !m.mac.path.is_ident("stringify") {
1015        return None;
1016    }
1017    Some(m.mac.tokens.clone())
1018}
1019
1020/// For a derive or attribute whose name clashes with a trampoline: if `item` is a
1021/// trampoline dummy, returns `target! { @__announce_self { target }
1022/// @__announce_dollar { $ } payload }`. Otherwise returns `None`.
1023///
1024/// `target` is the absolute path of the `#[announce(macro_name = ..)]` macro. Pick
1025/// a path that is valid at the user's site. The two prefixes are for the
1026/// trampoline (see [the wire format](Input#the-wire-format)): it needs its own path
1027/// to call itself again, when it holds a large item once, and a real `$` to
1028/// un-escape an item that has `$` in it.
1029///
1030/// ```ignore
1031/// #[proc_macro_derive(Parse)]
1032/// pub fn derive_parse(input: TokenStream) -> TokenStream {
1033///     if let Some(out) = announce::helper::redirect(input.clone().into(), &syn::parse_quote!(::pars::ParseAnnounce)) {
1034///         return out.into();
1035///     }
1036///     // normal derive
1037/// }
1038/// ```
1039pub fn redirect(item: TokenStream, target: &Path) -> Option<TokenStream> {
1040    let body = payload(item)?;
1041    // A trampoline whose payload contains `$` needs a real `$` from its
1042    // caller (`@__announce_dollar { $ }`); the others ignore it. A trampoline
1043    // that holds its item once calls itself again and needs its own path
1044    // (`@__announce_self { path }`); the others ignore that too. The derive route cannot re-invoke the
1045    // trampoline itself, so both prefixes are always added here.
1046    let dollar = proc_macro2::Punct::new('$', proc_macro2::Spacing::Alone);
1047    Some(quote! { #target ! { @__announce_self { #target } @__announce_dollar { #dollar } #body } })
1048}
1049
1050/// What a check receives: `(ARGS), ITEM`.
1051///
1052/// The item has its `#[cfg]` evaluated, and it still has the attributes that the
1053/// checks own (see [`OwnedAttrs`]), so the check can validate them. [`check`] parses
1054/// this and also answers the attribute query. Parsing a `CheckInput` by hand fails
1055/// with an error that says so when `#[announce]` sends the query.
1056pub struct CheckInput {
1057    /// The check's arguments, `(ARGS)`.
1058    pub args: TokenStream,
1059    /// The cfg-evaluated item, with the owned attributes.
1060    pub item: syn::Item,
1061    /// The attribute names that the check declared with `attributes` in
1062    /// [`check`]. Empty when the input was parsed by hand.
1063    pub owned: OwnedAttrs,
1064}
1065
1066impl CheckInput {
1067    /// The same input with the declared attribute names.
1068    pub fn with_owned(mut self, owned: OwnedAttrs) -> Self {
1069        self.owned = owned;
1070        self
1071    }
1072
1073    /// The owned attributes in the item, in source order: what the check
1074    /// validates ([`OwnedAttrs::find`]).
1075    pub fn owned_attributes(&self) -> Vec<Attribute> {
1076        self.owned.find(&self.item)
1077    }
1078}
1079
1080impl Parse for CheckInput {
1081    fn parse(input: ParseStream) -> Result<Self> {
1082        if input.peek(Token![@]) {
1083            return Err(input.error(
1084                "announce: this check received the attribute query `@__announce_attrs` that \
1085                 `#[announce]` sends first, and does not answer it. Run the check with \
1086                 `announce::helper::check`, or parse `announce::helper::CheckCall` and answer \
1087                 `Query` (a `macro_rules!` check needs one more arm); every check must \
1088                 answer. See the `announce::helper::attrs` docs",
1089            ));
1090        }
1091        let content;
1092        syn::parenthesized!(content in input);
1093        let args = content.parse()?;
1094        input.parse::<Token![,]>()?;
1095        Ok(CheckInput {
1096            args,
1097            item: input.parse()?,
1098            owned: OwnedAttrs::new(),
1099        })
1100    }
1101}
1102
1103/// Best-effort check that `ty` is announced (`ty: Announce`). `krate` is the
1104/// consumer's path to the `announce` crate.
1105pub fn check_type(krate: &Path, ty: &syn::Type) -> TokenStream {
1106    quote! {
1107        const _: () = {
1108            fn __announce_chk<__AnnounceT: ?::core::marker::Sized + #krate::Announce>() {}
1109            let _ = __announce_chk::<#ty>;
1110        };
1111    }
1112}
1113
1114/// Best-effort check that the trait at `path` is announced (`dyn path: Announce`).
1115/// Only meaningful for simple dyn-compatible traits.
1116pub fn check_trait(krate: &Path, path: &Path) -> TokenStream {
1117    check_type(krate, &syn::parse_quote!(dyn #path))
1118}
1119
1120#[cfg(test)]
1121mod tests {
1122    use super::*;
1123
1124    fn s(ts: impl ToTokens) -> String {
1125        ts.to_token_stream()
1126            .to_string()
1127            .split_whitespace()
1128            .collect()
1129    }
1130
1131    #[test]
1132    fn bang_drops_generic_arguments() {
1133        assert_eq!(s(Cont::bang(syn::parse_quote!(a::Enum<A, B>))), "a::Enum!");
1134    }
1135
1136    #[test]
1137    fn input_roundtrip_and_meta() {
1138        let input: Input = syn::parse_quote! {
1139            [cb!, #[derive(p::D)] N]
1140            a::F! => [x::check(1), mock(::core::iter::Iterator)] { pub trait F {} }
1141            #[derive(p::D)] M => [] { pub struct S; }
1142            (rest)
1143        };
1144        assert_eq!(input.list.0.len(), 2);
1145        assert_eq!(input.entries.len(), 2);
1146        assert_eq!(s(&input.entries[0].meta[0]), "x::check(1)");
1147        assert_eq!(
1148            s(input.entries[0].find_meta("check").unwrap().args.clone()),
1149            "1"
1150        );
1151        assert!(input.entries[0].has_check("check"));
1152        assert_eq!(
1153            s(input.entries[0].mock().unwrap()),
1154            "::core::iter::Iterator"
1155        );
1156        assert_eq!(s(input.entries[1].key.path()), "p::D");
1157        assert_eq!(s(&input.rest), "(rest)");
1158        let order: Vec<String> = input.entries_in_order().map(|e| s(e.key.path())).collect();
1159        assert_eq!(order, ["p::D", "a::F"]);
1160        let fwd = input.forward().unwrap().to_string();
1161        assert!(fwd.contains("__AnnouncePayload"), "{}", fwd);
1162    }
1163
1164    #[test]
1165    fn entries_know_the_attributes_their_checks_own() {
1166        let input: Input = syn::parse_quote! {
1167            [cb!]
1168            a::F! => [x::check, attrs(my_lib, other), m::info(1), attrs(other, third)] {
1169                #[my_lib(on_item)]
1170                pub struct F { #[other] pub a: u8, #[third(x)] #[doc = "d"] pub b: u8 }
1171            }
1172            a::G! => [] { pub struct G; }
1173        };
1174        let owned = input.entries[0].owned_attrs();
1175        let names: Vec<String> = owned.names().iter().map(|n| n.to_string()).collect();
1176        assert_eq!(names, ["my_lib", "other", "third"]);
1177        assert_eq!(owned.find(&input.entries[0].item().unwrap()).len(), 3);
1178        let stripped = input.entries[0].item_without_owned_attrs().unwrap();
1179        assert_eq!(s(&stripped), "pubstructF{puba:u8,#[doc=\"d\"]pubb:u8}");
1180        // Without `attrs(..)` there is nothing to strip.
1181        assert!(input.entries[1].owned_attrs().is_empty());
1182        assert_eq!(
1183            s(input.entries[1].item_without_owned_attrs().unwrap()),
1184            "pubstructG;"
1185        );
1186        // `attrs` is found by its exact name, not as the last segment of a path.
1187        let input: Input = syn::parse_quote! { [cb!] a::F! => [lib::attrs(x)] { struct F; } };
1188        assert!(input.entries[0].owned_attrs().is_empty());
1189    }
1190
1191    #[test]
1192    fn check_input_reports_the_query_it_does_not_answer() {
1193        let e = syn::parse2::<CheckInput>(template_quote::quote! {
1194            @__announce_attrs [ c!, f! ] { st }
1195        })
1196        .err()
1197        .unwrap()
1198        .to_string();
1199        assert!(
1200            e.contains("does not answer it") && e.contains("announce::helper::check`"),
1201            "{}",
1202            e
1203        );
1204        let ok: CheckInput = syn::parse_quote! { (x, y), struct S; };
1205        assert!(ok.owned.is_empty() && ok.owned_attributes().is_empty());
1206        let ok = ok.with_owned(OwnedAttrs::from_strs(&["a"]));
1207        assert!(ok.owned.contains("a"));
1208    }
1209
1210    #[test]
1211    fn check_answers_the_query_without_running_the_closure() {
1212        let q = || -> TokenStream {
1213            syn::parse_quote! { @__announce_attrs [ a::check!, b::check!, f::finish! ] { st } }
1214        };
1215        let mut ran = false;
1216        let out = check(q(), &["my_lib", "other"], |_| {
1217            ran = true;
1218            Ok(TokenStream::new())
1219        });
1220        assert!(!ran);
1221        assert_eq!(
1222            s(out),
1223            "b::check!{@__announce_attrs[b::check!,f::finish!]{stattrs(my_lib,other)}}"
1224        );
1225        // No names: the state is passed on as it is, and the closure is not run.
1226        let out = check(q(), &[], |_| panic!("the query is not a call"));
1227        assert_eq!(
1228            s(out),
1229            "b::check!{@__announce_attrs[b::check!,f::finish!]{st}}"
1230        );
1231        // Names are written as text: a raw identifier is its plain name.
1232        let out = check(q(), &["r#type", "type"], |_| unreachable!());
1233        assert_eq!(
1234            s(out),
1235            "b::check!{@__announce_attrs[b::check!,f::finish!]{stattrs(type)}}"
1236        );
1237        // The answer of `Query` is kept as is: a list that does not fit is a compile error.
1238        let out = s(check(
1239            syn::parse_quote! { @__announce_attrs [ a::check! ] { st } },
1240            &[],
1241            |_| unreachable!(),
1242        ));
1243        assert!(out.contains("compile_error") && out.contains("nomacrotocallafterthischeck"));
1244    }
1245
1246    #[test]
1247    fn check_runs_the_closure_with_the_owned_names() {
1248        let mut seen = None;
1249        let out = check(
1250            syn::parse_quote! { (x, y), #[my_lib(a)] struct S { #[other] a: u8, #[kept] b: u8 } },
1251            &["my_lib", "other"],
1252            |c| {
1253                seen = Some((
1254                    s(&c.args),
1255                    s(&c.item),
1256                    c.owned
1257                        .names()
1258                        .iter()
1259                        .map(|n| n.to_string())
1260                        .collect::<Vec<_>>(),
1261                    c.owned_attributes().len(),
1262                ));
1263                Ok(quote! { pub const SEEN: u8 = 1; })
1264            },
1265        );
1266        // Whatever the closure returns is the result.
1267        assert_eq!(s(out), "pubconstSEEN:u8=1;");
1268        let (args, item, owned, found) = seen.unwrap();
1269        assert_eq!(args, "x,y");
1270        assert!(
1271            item.starts_with("#[my_lib(a)]structS{#[other]a:u8"),
1272            "{}",
1273            item
1274        );
1275        assert_eq!(owned, ["my_lib", "other"]);
1276        assert_eq!(found, 2);
1277        // The closure can change the input (`&mut`) and has no names to own.
1278        let out = check(syn::parse_quote! { (), struct S; }, &[], |c| {
1279            assert!(c.owned.is_empty());
1280            c.args = quote! { changed };
1281            Ok(c.args.clone())
1282        });
1283        assert_eq!(s(out), "changed");
1284    }
1285
1286    #[test]
1287    fn check_turns_errors_into_compile_errors() {
1288        // An error of the closure.
1289        let out = s(check(
1290            syn::parse_quote! { (), struct S(u8); },
1291            &[],
1292            |c| match &c.item {
1293                syn::Item::Struct(it) => Err(syn::Error::new_spanned(&it.fields, "no tuples")),
1294                _ => Ok(TokenStream::new()),
1295            },
1296        ));
1297        assert_eq!(out, "::core::compile_error!{\"notuples\"}");
1298        // An input that is neither the query nor `(ARGS), ITEM`.
1299        let mut ran = false;
1300        let out = s(check(syn::parse_quote! { not a check call }, &[], |_| {
1301            ran = true;
1302            Ok(TokenStream::new())
1303        }));
1304        assert!(!ran && out.contains("compile_error"), "{}", out);
1305        // A reserved name, on the query and on the call.
1306        for input in [
1307            syn::parse_quote! { @__announce_attrs [ a!, f::finish! ] { st } },
1308            syn::parse_quote! { (), struct S; },
1309        ] {
1310            let out = s(check(input, &["my_lib", "doc"], |_| unreachable!()));
1311            assert!(
1312                out.contains("compile_error") && out.contains("`doc`cannotbeanownedattributename"),
1313                "{}",
1314                out
1315            );
1316        }
1317        // A name that is not an identifier is an error, not a panic.
1318        for bad in ["", "my-lib", "1st", "_", "a b", "r#"] {
1319            let out = s(check(
1320                syn::parse_quote! { (), struct S; },
1321                &[bad],
1322                |_| unreachable!(),
1323            ));
1324            assert!(
1325                out.contains("compile_error") && out.contains("isnotanidentifier"),
1326                "{:?}: {}",
1327                bad,
1328                out
1329            );
1330        }
1331    }
1332
1333    #[test]
1334    fn fetch_prepends_and_returns() {
1335        let input: Input = syn::parse_quote! { [me!, next!] a::F! => [] { struct S; } (rest) };
1336        let out = s(input.fetch([Cont::bang(syn::parse_quote!(g::G))]));
1337        // The fetch is recorded in the options group.
1338        assert_eq!(
1339            out,
1340            "g::G!{(__announce_fetched(g::G))[g::G!,me!,next!]a::F!=>[]{structS;}(rest)}"
1341        );
1342        assert_eq!(Cont::bang(syn::parse_quote!(a::F)), input.entries[0].key);
1343        // Nothing to fetch: nothing to record.
1344        let out = s(input.fetch([]));
1345        assert_eq!(out, "me!{[me!,next!]a::F!=>[]{structS;}(rest)}");
1346    }
1347
1348    #[test]
1349    fn fetch_of_a_path_that_left_no_entry_is_an_error() {
1350        // The bookkeeping of `gather`, shared. `g::G`
1351        // was fetched, the callback is called again and has no entry for it.
1352        // (`g::G` forwarded to `me` without adding an entry.)
1353        let again: Input = syn::parse_quote! {
1354            (__announce_fetched(g::G)) [me!] a::F! => [] { struct S; }
1355        };
1356        let out = s(again.fetch([Cont::bang(syn::parse_quote!(g::G))]));
1357        assert_eq!(
1358            out,
1359            "compile_error!{\"announce:`g::G!`wasfetchedbutdidnotprovideadefinition;isitan`#[announce]`trampoline?\"}"
1360        );
1361        // The same message as `gather`.
1362        let from_gather = match again.gather([Cont::bang(syn::parse_quote!(g::G))]) {
1363            Gather::Fetch(ts) => s(ts),
1364            Gather::Ready(_) => panic!(),
1365        };
1366        assert_eq!(out, from_gather);
1367        // Generic arguments do not hide the path.
1368        let out = s(again.fetch([Cont::bang(syn::parse_quote!(g::G<u8>))]));
1369        assert!(out.contains("wasfetchedbutdidnotprovide"), "{}", out);
1370        // A path that did answer (an entry is there) may be fetched again, and
1371        // other paths are fetched as usual; the record grows.
1372        let answered: Input = syn::parse_quote! {
1373            (mine, __announce_fetched(g::G)) [me!] g::G! => [] { struct G; }
1374        };
1375        let out = s(answered.fetch([Cont::bang(syn::parse_quote!(g::G))]));
1376        assert!(
1377            out.starts_with("g::G!{(mine,__announce_fetched(g::G),__announce_fetched(g::G))"),
1378            "{}",
1379            out
1380        );
1381        let out = s(answered.fetch([Cont::bang(syn::parse_quote!(h::H))]));
1382        assert!(
1383            out.starts_with("h::H!{(mine,__announce_fetched(g::G),__announce_fetched(h::H))"),
1384            "{}",
1385            out
1386        );
1387        // One of several missing paths is enough for the error.
1388        let out = s(again.fetch([
1389            Cont::bang(syn::parse_quote!(h::H)),
1390            Cont::bang(syn::parse_quote!(g::G)),
1391        ]));
1392        assert!(out.starts_with("compile_error!"), "{}", out);
1393    }
1394
1395    #[test]
1396    fn fetch_and_gather_share_the_record() {
1397        let input: Input = syn::parse_quote! { [me!] };
1398        // `gather` fetches `g::G` ...
1399        let first = match input.gather([Cont::bang(syn::parse_quote!(g::G))]) {
1400            Gather::Fetch(ts) => ts,
1401            Gather::Ready(_) => panic!(),
1402        };
1403        assert_eq!(s(&first), "g::G!{(__announce_fetched(g::G))[g::G!,me!]}");
1404        // ... and the callback, called again by a non-trampoline, fetches it
1405        // raw: the error is the same whichever one is used.
1406        let body: Input =
1407            syn::parse2(template_quote::quote! { (__announce_fetched(g::G)) [me!] }).unwrap();
1408        assert!(s(body.fetch([Cont::bang(syn::parse_quote!(g::G))]))
1409            .contains("wasfetchedbutdidnotprovide"));
1410        // And the other way round.
1411        let raw = input.fetch([Cont::bang(syn::parse_quote!(g::G))]);
1412        assert_eq!(s(&raw), "g::G!{(__announce_fetched(g::G))[g::G!,me!]}");
1413        match body.gather([Cont::bang(syn::parse_quote!(g::G))]) {
1414            Gather::Fetch(ts) => assert!(s(ts).contains("wasfetchedbutdidnotprovide")),
1415            Gather::Ready(_) => panic!(),
1416        }
1417    }
1418
1419    #[test]
1420    fn malformed_entry_is_an_error() {
1421        let r = syn::parse2::<Input>(template_quote::quote! { [cb!] a::F! => { struct S; } });
1422        assert!(r.err().unwrap().to_string().contains("malformed entry"));
1423    }
1424
1425    #[test]
1426    fn invoke_names_attribute_dummies() {
1427        let list: ContList = [
1428            Cont::derive(syn::parse_quote!(p::D)),
1429            Cont::bang(syn::parse_quote!(cb)),
1430        ]
1431        .into_iter()
1432        .collect();
1433        let out = s(list.invoke(TokenStream::new()));
1434        assert!(
1435            out.starts_with(
1436                "#[derive(p::D)]#[allow(dead_code,non_camel_case_types)]enum__AnnounceT_"
1437            ),
1438            "{}",
1439            out
1440        );
1441        let body = payload(list.invoke(TokenStream::new())).map(s).unwrap();
1442        assert!(body.starts_with("[#[derive(p::D)]__AnnounceT_"), "{}", body);
1443        assert!(redirect(list.invoke(TokenStream::new()), &syn::parse_quote!(::x::Y)).is_some());
1444    }
1445}
1446
1447#[cfg(test)]
1448mod kv_tests {
1449    use super::*;
1450
1451    #[test]
1452    fn kv_types_lists_flags_and_errors() {
1453        let m: Meta = syn::parse_quote!(lib::info(ty = ::std::collections::HashMap<u8, Vec<u8>>, out = fn() -> u8, xs = [A, b::C], on));
1454        let mut kv = m.kv().unwrap();
1455        let ty: syn::Type = kv.require("ty").unwrap();
1456        assert_eq!(
1457            ty.to_token_stream().to_string().replace(' ', ""),
1458            "::std::collections::HashMap<u8,Vec<u8>>"
1459        );
1460        let out: syn::Type = kv.require("out").unwrap();
1461        assert_eq!(
1462            out.to_token_stream().to_string().replace(' ', ""),
1463            "fn()->u8"
1464        );
1465        let xs: Vec<Path> = kv.list("xs").unwrap().unwrap();
1466        assert_eq!(xs.len(), 2);
1467        assert!(kv.flag("on").unwrap());
1468        assert!(kv.get::<syn::Type>("absent").unwrap().is_none());
1469        kv.finish().unwrap();
1470
1471        let mut kv: KvArgs = syn::parse_quote!(a = 1, b = 2);
1472        let _: syn::LitInt = kv.require("a").unwrap();
1473        assert!(kv
1474            .finish()
1475            .unwrap_err()
1476            .to_string()
1477            .contains("unknown key `b`"));
1478        assert!(syn::parse2::<KvArgs>(template_quote::quote!(a = 1, a = 2)).is_err());
1479        let mut kv: KvArgs = syn::parse_quote!();
1480        assert!(kv
1481            .require::<syn::Type>("x")
1482            .unwrap_err()
1483            .to_string()
1484            .contains("missing `x`"));
1485    }
1486
1487    #[test]
1488    fn stable_referrer_is_deterministic() {
1489        let item: syn::ItemStruct = syn::parse_quote! {
1490            pub struct S { a: Foo, b: Bar, c: Baz, d: Qux, e: Quux, f: Corge }
1491        };
1492        let analyse = || {
1493            let mut l = type_leak::Leaker::new();
1494            let context = l.intern_struct(&item, true).unwrap();
1495            (
1496                crate::helper::leak::StableReferrer::new(l.finish()),
1497                context,
1498            )
1499        };
1500        let order = || {
1501            let (r, _) = analyse();
1502            r.types()
1503                .iter()
1504                .map(|t| t.to_token_stream().to_string())
1505                .collect::<Vec<_>>()
1506        };
1507        let first = order();
1508        assert_eq!(first.len(), 6);
1509        let mut sorted = first.clone();
1510        sorted.sort();
1511        assert_eq!(first, sorted);
1512        for _ in 0..20 {
1513            assert_eq!(order(), first);
1514        }
1515        let (r, context) = analyse();
1516        let mut it: syn::Item = syn::Item::Struct(item.clone());
1517        r.visit_item_mut(context, &mut it, |_, i| {
1518            syn::parse_str(&format!("T{}", i)).unwrap()
1519        });
1520        let s = it.to_token_stream().to_string();
1521        assert!(
1522            s.contains("a : T3") && s.contains("b : T0") && s.contains("f : T2"),
1523            "{}",
1524            s
1525        );
1526        let ty: syn::Type = syn::parse_quote!(Qux);
1527        assert_eq!(r.index_of(&ty), Some(5));
1528        let ty = r.expand(context, ty, |_, i| {
1529            syn::parse_str(&format!("T{}", i)).unwrap()
1530        });
1531        assert_eq!(ty.to_token_stream().to_string(), "T5");
1532    }
1533
1534    #[test]
1535    fn stable_referrer_numbers_required_entries_only() {
1536        // `::other::X` names another crate: an optional entry, left as written.
1537        let item: syn::ItemStruct = syn::parse_quote! {
1538            pub struct S { a: Foo, b: ::other::X, c: Bar, d: ::std::string::String }
1539        };
1540        let mut l = type_leak::Leaker::new();
1541        let context = l.intern_struct(&item, true).unwrap();
1542        let r = crate::helper::leak::StableReferrer::new(l.finish());
1543        let types: Vec<String> = r
1544            .types()
1545            .iter()
1546            .map(|t| t.to_token_stream().to_string())
1547            .collect();
1548        assert_eq!(types, ["Bar", "Foo"]);
1549        let mut it = syn::Item::Struct(item);
1550        r.visit_item_mut(context, &mut it, |_, i| {
1551            syn::parse_str(&format!("T{}", i)).unwrap()
1552        });
1553        let s = it.to_token_stream().to_string();
1554        assert!(
1555            s.contains("a : T1") && s.contains("b : :: other :: X") && s.contains("c : T0"),
1556            "{}",
1557            s
1558        );
1559    }
1560}
1561
1562#[cfg(test)]
1563mod options_tests {
1564    use super::*;
1565
1566    #[test]
1567    fn options_are_parsed_and_forwarded() {
1568        let mut leaker = type_leak::Leaker::new();
1569        let context = leaker
1570            .intern_struct(
1571                &syn::parse_quote!(
1572                    struct F<T> {
1573                        a: Local,
1574                        b: Vec<T>,
1575                    }
1576                ),
1577                false,
1578            )
1579            .unwrap();
1580        let tl = TypeLeak::new(
1581            syn::parse_quote!(::my::A),
1582            syn::parse_quote!(7u64),
1583            leaker.finish(),
1584            context,
1585        );
1586        let meta = tl.meta();
1587        let input: Input = syn::parse_quote! {
1588            (type_leak) [me!, next!]
1589            a::F! => [#meta] { struct F<T>(T); }
1590            (rest)
1591        };
1592        assert!(input.has_option("type_leak"));
1593        let fwd = input.forward().unwrap().to_string().replace(' ', "");
1594        assert!(
1595            fwd.starts_with("next!{(type_leak)[next!]a::F!=>"),
1596            "{}",
1597            fwd
1598        );
1599        let fetch = input
1600            .fetch([Cont::bang(syn::parse_quote!(g::G))])
1601            .to_string()
1602            .replace(' ', "");
1603        assert!(
1604            fetch.starts_with("g::G!{(type_leak,__announce_fetched(g::G))[g::G!,me!,next!]"),
1605            "{}",
1606            fetch
1607        );
1608        let read = input.entries[0].type_leak().unwrap().unwrap();
1609        assert_eq!(read.types().count(), 2);
1610        assert_eq!(read.id.base10_digits(), "7");
1611        assert_eq!(read.referrer, tl.referrer);
1612        let proj = read.projection(1, &syn::parse_quote!((u8,)));
1613        assert_eq!(
1614            proj.to_string().replace(' ', ""),
1615            "<()as::my::A<(u8,),7u64,1usize>>::Type"
1616        );
1617        let plain: Input = syn::parse_quote! { [me!] a::F! => [] { struct F; } };
1618        assert!(!plain.has_option("type_leak"));
1619        assert!(plain.entries[0].type_leak().unwrap().is_none());
1620    }
1621}
1622
1623#[cfg(test)]
1624mod kv_balance_tests {
1625    use super::*;
1626
1627    #[test]
1628    fn unbalanced_angle_is_an_error() {
1629        let e = syn::parse2::<KvArgs>(template_quote::quote!(a = x < y, b = 1)).unwrap_err();
1630        assert!(
1631            e.to_string().contains("unbalanced `<` in the value of `a`"),
1632            "{}",
1633            e
1634        );
1635        assert!(syn::parse2::<KvArgs>(template_quote::quote!(a = 1 << 2, b = 3)).is_err());
1636        let mut kv: KvArgs = syn::parse_quote!(a = (x < y), b = 1, f = fn() -> u8);
1637        assert!(kv.tokens("a").is_some() && kv.tokens("b").is_some() && kv.tokens("f").is_some());
1638        kv.finish().unwrap();
1639    }
1640}
1641
1642#[cfg(test)]
1643mod low_audit_tests {
1644    use super::*;
1645
1646    #[test]
1647    fn cont_equality_ignores_dummy_name_and_generics() {
1648        let a: Cont = syn::parse_quote!(
1649            #[derive(p::D)]
1650            N1
1651        );
1652        let b: Cont = syn::parse_quote!(
1653            #[derive(p::D)]
1654            N2
1655        );
1656        assert_eq!(a, b);
1657        let c = Cont::Bang(syn::parse_quote!(a::E<T>));
1658        assert_eq!(c.to_token_stream().to_string().replace(' ', ""), "a::E!");
1659        let d: Cont = syn::parse_quote!(#[derive(x::A, y::B)]);
1660        assert_eq!(
1661            d.path().to_token_stream().to_string().replace(' ', ""),
1662            "x::A"
1663        );
1664    }
1665
1666    #[test]
1667    fn rest_like_an_entry_without_meta_is_rest() {
1668        let i: Input = syn::parse_quote! { [cb!] foo! => bar };
1669        assert!(i.entries.is_empty());
1670        assert_eq!(i.rest.to_string().replace(' ', ""), "foo!=>bar");
1671    }
1672
1673    #[test]
1674    fn gather_refuses_self_request() {
1675        let i: Input = syn::parse_quote! { [me!] };
1676        match i.gather([Cont::bang(syn::parse_quote!(me))]) {
1677            Gather::Fetch(ts) => assert!(ts.to_string().contains("which is the callback itself")),
1678            Gather::Ready(_) => panic!(),
1679        }
1680    }
1681}
1682
1683#[cfg(test)]
1684mod kv_error_tests {
1685    use super::*;
1686
1687    fn parse(ts: TokenStream) -> Result<KvArgs> {
1688        syn::parse2(ts)
1689    }
1690
1691    #[test]
1692    fn missing_value_after_equals_is_an_error() {
1693        let e = parse(template_quote::quote!(a =)).unwrap_err().to_string();
1694        assert_eq!(e, "missing value for `a`");
1695        let e = parse(template_quote::quote!(a = , b = 1))
1696            .unwrap_err()
1697            .to_string();
1698        assert_eq!(e, "missing value for `a`");
1699        let e = parse(template_quote::quote!(ok = 1, b =))
1700            .unwrap_err()
1701            .to_string();
1702        assert_eq!(e, "missing value for `b`");
1703    }
1704
1705    #[test]
1706    fn get_on_a_flag_key_needs_a_value() {
1707        let mut kv: KvArgs = syn::parse_quote!(f, a = 1);
1708        let e = kv.get::<syn::Type>("f").unwrap_err().to_string();
1709        assert_eq!(e, "`f` needs a value");
1710        // `require` reports the same error, not "missing".
1711        let mut kv: KvArgs = syn::parse_quote!(f);
1712        assert_eq!(
1713            kv.require::<syn::Type>("f").unwrap_err().to_string(),
1714            "`f` needs a value"
1715        );
1716    }
1717
1718    #[test]
1719    fn list_on_a_flag_key_needs_a_bracketed_value() {
1720        let mut kv: KvArgs = syn::parse_quote!(f, a = 1);
1721        let e = kv.list::<syn::Path>("f").unwrap_err().to_string();
1722        assert_eq!(e, "`f` needs a `[..]` value");
1723    }
1724
1725    #[test]
1726    fn flag_on_a_key_with_a_value_takes_no_value() {
1727        let mut kv: KvArgs = syn::parse_quote!(a = 1);
1728        let e = kv.flag("a").unwrap_err().to_string();
1729        assert_eq!(e, "`a` takes no value");
1730        // An absent flag is false and does not consume anything else.
1731        let mut kv: KvArgs = syn::parse_quote!(b = 1);
1732        assert!(!kv.flag("a").unwrap());
1733        assert!(kv.tokens("b").is_some());
1734    }
1735
1736    #[test]
1737    fn list_on_a_non_bracketed_value_is_an_error() {
1738        let mut kv: KvArgs = syn::parse_quote!(a = 1);
1739        let e = kv.list::<syn::LitInt>("a").unwrap_err().to_string();
1740        assert!(e.contains("expected square brackets"), "{}", e);
1741        let mut kv: KvArgs = syn::parse_quote!(a = (x, y));
1742        let e = kv.list::<syn::Path>("a").unwrap_err().to_string();
1743        assert!(e.contains("expected square brackets"), "{}", e);
1744        let mut kv: KvArgs = syn::parse_quote!(a = [1, 2] extra);
1745        assert!(kv.list::<syn::LitInt>("a").is_err());
1746    }
1747
1748    #[test]
1749    fn list_element_and_value_parse_errors_surface() {
1750        let mut kv: KvArgs = syn::parse_quote!(a = [x, 1]);
1751        assert!(kv.list::<syn::Path>("a").is_err());
1752        let mut kv: KvArgs = syn::parse_quote!(a = 1);
1753        assert!(kv.get::<syn::Path>("a").is_err());
1754    }
1755
1756    #[test]
1757    fn list_accepts_empty_and_trailing_comma() {
1758        let mut kv: KvArgs = syn::parse_quote!(a = [], b = [x::Y, z,]);
1759        assert!(kv.list::<Path>("a").unwrap().unwrap().is_empty());
1760        assert_eq!(kv.list::<Path>("b").unwrap().unwrap().len(), 2);
1761        kv.finish().unwrap();
1762    }
1763
1764    #[test]
1765    fn taken_keys_are_gone_and_finish_names_the_first_leftover() {
1766        let mut kv: KvArgs = syn::parse_quote!(a = 1, b, c = 2);
1767        assert!(kv.tokens("a").is_some());
1768        assert!(kv.tokens("a").is_none());
1769        assert_eq!(kv.finish().unwrap_err().to_string(), "unknown key `b`");
1770    }
1771
1772    #[test]
1773    fn duplicate_keys_and_keywords() {
1774        let e = parse(template_quote::quote!(a, a)).unwrap_err().to_string();
1775        assert_eq!(e, "duplicate key `a`");
1776        // Keywords are fine as keys.
1777        let mut kv: KvArgs = syn::parse_quote!(type = u8, crate = a::b, fn);
1778        assert!(kv.get::<syn::Type>("type").unwrap().is_some());
1779        assert!(kv.get::<Path>("crate").unwrap().is_some());
1780        assert!(kv.flag("fn").unwrap());
1781        kv.finish().unwrap();
1782    }
1783
1784    #[test]
1785    fn values_run_to_the_next_top_level_comma() {
1786        // `1 b = 2` is all one value; two flags need a comma between them.
1787        let mut kv = parse(template_quote::quote!(a = 1 b = 2)).unwrap();
1788        assert_eq!(kv.tokens("a").unwrap().to_string().replace(' ', ""), "1b=2");
1789        assert!(parse(template_quote::quote!(a b)).is_err());
1790    }
1791
1792    #[test]
1793    fn equality_comparison_is_a_flag_followed_by_junk() {
1794        // `a == b` is not `a = (= b)`: `==` ends neither value nor key.
1795        assert!(parse(template_quote::quote!(a == b)).is_err());
1796    }
1797}
1798
1799#[cfg(test)]
1800mod gap_tests {
1801    use super::*;
1802    use proc_macro2::{Punct, Spacing};
1803
1804    fn s(ts: impl ToTokens) -> String {
1805        ts.to_token_stream()
1806            .to_string()
1807            .split_whitespace()
1808            .collect()
1809    }
1810
1811    fn mk_dollar() -> Punct {
1812        Punct::new('$', Spacing::Alone)
1813    }
1814
1815    #[test]
1816    fn check_trait_asserts_the_dyn_trait_is_announced() {
1817        let out = s(check_trait(
1818            &syn::parse_quote!(::announce),
1819            &syn::parse_quote!(my::Tr),
1820        ));
1821        assert!(out.contains("dynmy::Tr"), "{}", out);
1822        assert!(out.contains("::announce::Announce"), "{}", out);
1823        assert!(out.contains("?::core::marker::Sized"), "{}", out);
1824        assert!(out.starts_with("const_:()="), "{}", out);
1825        // `check_trait` is `check_type` of the `dyn` type.
1826        let direct = s(check_type(
1827            &syn::parse_quote!(::announce),
1828            &syn::parse_quote!(dyn my::Tr),
1829        ));
1830        assert_eq!(out, direct);
1831    }
1832
1833    #[test]
1834    fn check_type_uses_the_given_krate_path() {
1835        let out = s(check_type(
1836            &syn::parse_quote!(::renamed),
1837            &syn::parse_quote!(Vec<u8>),
1838        ));
1839        assert!(out.contains("::renamed::Announce"), "{}", out);
1840        assert!(out.contains("__announce_chk::<Vec<u8>>"), "{}", out);
1841    }
1842
1843    #[test]
1844    fn meta_name_values_parse_expression_values() {
1845        let m: Meta = syn::parse_quote!(lib::info(a = 1, b = "x", c = 1 + 2));
1846        let nv = m.name_values().unwrap();
1847        assert_eq!(nv.len(), 3);
1848        assert_eq!(s(&nv[0].path), "a");
1849        assert_eq!(s(&nv[1].value), "\"x\"");
1850        assert_eq!(s(&nv[2].value), "1+2");
1851        // No args: empty list.
1852        let m: Meta = syn::parse_quote!(lib::info);
1853        assert!(m.name_values().unwrap().is_empty());
1854        // A flag, or a non-expression value, is an error; `kv` accepts them.
1855        let m: Meta = syn::parse_quote!(lib::info(a = 1, on));
1856        assert!(m.name_values().is_err());
1857        let m: Meta = syn::parse_quote!(lib::info(a = 1, on));
1858        assert!(m.kv().is_ok());
1859        // Array expressions are expressions; types are not.
1860        let m: Meta = syn::parse_quote!(lib::info(xs = [A, B]));
1861        assert_eq!(m.name_values().unwrap().len(), 1);
1862        let m: Meta = syn::parse_quote!(lib::info(ty = Vec<u8, A>));
1863        assert!(m.name_values().is_err());
1864        assert!(m.kv().is_ok());
1865    }
1866
1867    #[test]
1868    fn meta_args_default_to_empty() {
1869        let m: Meta = syn::parse_quote!(lib::info);
1870        assert!(m.args.is_none());
1871        assert!(m.kv().unwrap().finish().is_ok());
1872        assert_eq!(s(&m), "lib::info");
1873        let m: Meta = syn::parse_quote!(lib::info(1, 2));
1874        assert_eq!(s(&m), "lib::info(1,2)");
1875    }
1876
1877    fn hash_of<T: std::hash::Hash>(t: &T) -> u64 {
1878        use std::hash::Hasher;
1879        let mut h = std::collections::hash_map::DefaultHasher::new();
1880        t.hash(&mut h);
1881        h.finish()
1882    }
1883
1884    #[test]
1885    fn cont_hash_agrees_with_eq() {
1886        let a: Cont = syn::parse_quote!(
1887            #[derive(p::D)]
1888            N1
1889        );
1890        let b: Cont = syn::parse_quote!(
1891            #[derive(p::D)]
1892            N2
1893        );
1894        let c: Cont = syn::parse_quote!(#[derive(p::D)]);
1895        assert_eq!(a, b);
1896        assert_eq!(hash_of(&a), hash_of(&b));
1897        assert_eq!(hash_of(&a), hash_of(&c));
1898        let bang = Cont::bang(syn::parse_quote!(p::D));
1899        let bang_generic = Cont::Bang(syn::parse_quote!(p::D<T>));
1900        assert_eq!(bang, bang_generic);
1901        assert_eq!(hash_of(&bang), hash_of(&bang_generic));
1902        // Different continuations differ, and a derive is not the bang of the same path.
1903        let other: Cont = syn::parse_quote!(
1904            #[derive(p::E)]
1905            N1
1906        );
1907        assert_ne!(a, other);
1908        assert_ne!(a, bang);
1909        let set: std::collections::HashSet<Cont> = [a.clone(), b, c, other].into_iter().collect();
1910        assert_eq!(set.len(), 2);
1911        assert!(set.contains(&a));
1912    }
1913
1914    #[test]
1915    fn cont_path_of_attribute_and_derive() {
1916        let c: Cont = syn::parse_quote!(
1917            #[my::attr(x, y)]
1918            N
1919        );
1920        assert_eq!(s(c.path()), "my::attr");
1921        let d = Cont::derive(syn::parse_quote!(::p::D));
1922        assert_eq!(s(d.path()), "::p::D");
1923        assert_eq!(s(&d), "#[derive(::p::D)]");
1924        // A `derive` without a usable path falls back to the attribute path.
1925        let e: Cont = syn::parse_quote!(
1926            #[derive()]
1927            N
1928        );
1929        assert_eq!(s(e.path()), "derive");
1930    }
1931
1932    #[test]
1933    fn input_skips_a_leading_dollar_prefix() {
1934        let dollar = mk_dollar();
1935        let ts = template_quote::quote! {
1936            @ __announce_dollar { #dollar }
1937            (opt) [cb!, n!]
1938            a::F! => [x::check(1)] { struct S; }
1939            (rest)
1940        };
1941        let input: Input = syn::parse2(ts).unwrap();
1942        assert_eq!(s(input.options.as_ref().unwrap()), "opt");
1943        assert!(input.has_option("opt"));
1944        assert_eq!(input.list.0.len(), 2);
1945        assert_eq!(s(&input.list), "[cb!,n!]");
1946        assert_eq!(input.entries.len(), 1);
1947        assert_eq!(s(input.entries[0].key.path()), "a::F");
1948        assert_eq!(s(&input.entries[0].meta[0]), "x::check(1)");
1949        assert_eq!(s(&input.rest), "(rest)");
1950        // The prefix is dropped on `forward` and `fetch`.
1951        let out = s(input.forward().unwrap());
1952        assert!(!out.contains("__announce_dollar"), "{}", out);
1953    }
1954
1955    #[test]
1956    fn input_dollar_prefix_without_options() {
1957        let dollar = mk_dollar();
1958        let ts = template_quote::quote! { @ __announce_dollar { #dollar } [cb!] };
1959        let input: Input = syn::parse2(ts).unwrap();
1960        assert!(input.options.is_none());
1961        assert_eq!(input.list.0.len(), 1);
1962        assert!(input.entries.is_empty());
1963        assert!(input.rest.is_empty());
1964    }
1965
1966    #[test]
1967    fn input_skips_the_self_and_dollar_prefixes() {
1968        let dollar = mk_dollar();
1969        let ts = template_quote::quote! {
1970            @ __announce_self { ::a::F }
1971            @ __announce_dollar { #dollar }
1972            [cb!, n!] REST
1973        };
1974        let input: Input = syn::parse2(ts).unwrap();
1975        assert!(input.options.is_none());
1976        assert_eq!(s(&input.list), "[cb!,n!]");
1977        assert_eq!(s(&input.rest), "REST");
1978        // Either one alone, and the self prefix before the options group.
1979        let ts = template_quote::quote! { @ __announce_self { F } (o) [cb!] };
1980        let input: Input = syn::parse2(ts).unwrap();
1981        assert!(input.has_option("o"));
1982        assert_eq!(input.list.0.len(), 1);
1983        // The prefixes are dropped on `forward`.
1984        let ts = template_quote::quote! { @ __announce_self { F } [cb!, n!] };
1985        let out = s(syn::parse2::<Input>(ts).unwrap().forward().unwrap());
1986        assert!(!out.contains("__announce_self"), "{}", out);
1987    }
1988
1989    #[test]
1990    fn input_rejects_other_prefixes() {
1991        assert!(syn::parse2::<Input>(template_quote::quote! { @ other { x } [cb!] }).is_err());
1992        assert!(syn::parse2::<Input>(template_quote::quote! { @ [cb!] }).is_err());
1993        // The prefix alone is not an input.
1994        let dollar = mk_dollar();
1995        let ts = template_quote::quote! { @ __announce_dollar { #dollar } };
1996        assert!(syn::parse2::<Input>(ts).is_err());
1997    }
1998
1999    #[test]
2000    fn invoke_with_an_empty_list_is_a_compile_error() {
2001        let out = s(ContList::default().invoke(template_quote::quote!(x y)));
2002        assert_eq!(out, "compile_error!{\"announce:emptycontinuationlist\"}");
2003        let opts = template_quote::quote!(expr);
2004        let out = s(ContList::default().invoke_with_options(Some(&opts), TokenStream::new()));
2005        assert!(out.contains("emptycontinuationlist"), "{}", out);
2006    }
2007
2008    #[test]
2009    fn invoke_in_expression_position_refuses_the_derive_route() {
2010        let list: ContList = [
2011            Cont::derive(syn::parse_quote!(p::D)),
2012            Cont::bang(syn::parse_quote!(cb)),
2013        ]
2014        .into_iter()
2015        .collect();
2016        let opts = template_quote::quote!(expr);
2017        let out = s(list.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2018        assert!(out.starts_with("compile_error!{"), "{}", out);
2019        assert!(out.contains("cannotbeusedwith`(expr)`"), "{}", out);
2020        assert!(!out.contains("__AnnouncePayload"), "{}", out);
2021        // The same with `expr` leading other options.
2022        let opts = template_quote::quote!(expr, type_leak);
2023        let out = s(list.invoke_with_options(Some(&opts), TokenStream::new()));
2024        assert!(out.contains("cannotbeusedwith`(expr)`"), "{}", out);
2025    }
2026
2027    #[test]
2028    fn only_a_leading_expr_selects_expression_position() {
2029        // The rule of the trampolines, shared.
2030        let yes = |ts: TokenStream| is_expression_position(&ts);
2031        assert!(yes(template_quote::quote!(expr)));
2032        assert!(yes(template_quote::quote!(expr, type_leak)));
2033        assert!(yes(template_quote::quote!(expr __announce_fetched(a))));
2034        assert!(!yes(template_quote::quote!(type_leak, expr)));
2035        assert!(!yes(template_quote::quote!(__announce_fetched(expr))));
2036        assert!(!yes(template_quote::quote!((expr))));
2037        assert!(!yes(template_quote::quote!(expression)));
2038        assert!(!yes(TokenStream::new()));
2039        // A derive continuation is refused only for a leading `expr`; with `expr`
2040        // later the options are a namespace, and the dummy enum is emitted.
2041        let list: ContList = [
2042            Cont::derive(syn::parse_quote!(p::D)),
2043            Cont::bang(syn::parse_quote!(cb)),
2044        ]
2045        .into_iter()
2046        .collect();
2047        let opts = template_quote::quote!(type_leak, expr);
2048        let out = s(list.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2049        assert!(out.contains("__AnnouncePayload"), "{}", out);
2050        assert!(!out.contains("cannotbeusedwith"), "{}", out);
2051        let opts = template_quote::quote!(__announce_fetched(expr));
2052        let out = s(list.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2053        assert!(out.contains("__AnnouncePayload"), "{}", out);
2054        // A bang head forwards the group as it is, whatever it holds.
2055        let bang: ContList = [Cont::bang(syn::parse_quote!(p::cb))].into_iter().collect();
2056        let opts = template_quote::quote!(type_leak, expr);
2057        let out = s(bang.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2058        assert_eq!(out, "p::cb!{(type_leak,expr)[p::cb!]body}");
2059    }
2060
2061    #[test]
2062    fn expression_option_is_fine_for_a_bang_head_and_other_options_for_derives() {
2063        let bang: ContList = [Cont::bang(syn::parse_quote!(p::cb))].into_iter().collect();
2064        let opts = template_quote::quote!(expr);
2065        let out = s(bang.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2066        assert_eq!(out, "p::cb!{(expr)[p::cb!]body}");
2067        let derive: ContList = [Cont::derive(syn::parse_quote!(p::D))]
2068            .into_iter()
2069            .collect();
2070        let opts = template_quote::quote!(type_leak);
2071        let out = s(derive.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2072        assert!(out.contains("__AnnouncePayload"), "{}", out);
2073        assert!(
2074            out.contains("stringify!{(type_leak)[#[derive(p::D)]__AnnounceT_"),
2075            "{}",
2076            out
2077        );
2078    }
2079
2080    #[test]
2081    fn name_dummies_keeps_existing_names_and_gives_distinct_new_ones() {
2082        let mut list: ContList = [
2083            Cont::derive(syn::parse_quote!(p::A)),
2084            Cont::derive(syn::parse_quote!(p::B)),
2085            syn::parse_quote!(
2086                #[derive(p::C)]
2087                Fixed
2088            ),
2089            Cont::bang(syn::parse_quote!(cb)),
2090        ]
2091        .into_iter()
2092        .collect();
2093        list.name_dummies();
2094        let names: Vec<String> = list
2095            .0
2096            .iter()
2097            .filter_map(|c| match c {
2098                Cont::Attr { name: Some(n), .. } => Some(n.to_string()),
2099                _ => None,
2100            })
2101            .collect();
2102        assert_eq!(names.len(), 3);
2103        assert_eq!(names[2], "Fixed");
2104        assert!(names[0].starts_with("__AnnounceT_") && names[1].starts_with("__AnnounceT_"));
2105        assert_ne!(names[0], names[1]);
2106        // `name_dummies` is idempotent.
2107        let before = s(&list);
2108        list.name_dummies();
2109        assert_eq!(s(&list), before);
2110    }
2111
2112    #[test]
2113    fn redirect_ignores_ordinary_items() {
2114        let target: Path = syn::parse_quote!(::x::Y);
2115        for item in [
2116            template_quote::quote! { struct S; },
2117            template_quote::quote! { enum E { A, B } },
2118            template_quote::quote! { enum E { __AnnouncePayload } },
2119            template_quote::quote! { enum E { __AnnouncePayload = 1 } },
2120            template_quote::quote! { enum E { __AnnouncePayload = (1, 0).1 } },
2121            template_quote::quote! { enum E { __AnnouncePayload = (other!{ x }, 0).1 } },
2122            template_quote::quote! { enum E { __AnnouncePayload = (stringify!{ x }, 0).1, B } },
2123            template_quote::quote! { enum E { Other = (stringify!{ x }, 0).1 } },
2124            template_quote::quote! { fn f() {} },
2125        ] {
2126            assert!(payload(item.clone()).is_none(), "{}", item);
2127            assert!(redirect(item.clone(), &target).is_none(), "{}", item);
2128        }
2129    }
2130
2131    #[test]
2132    fn redirect_wraps_the_payload_with_the_dollar_prefix() {
2133        let list: ContList = [
2134            Cont::derive(syn::parse_quote!(p::D)),
2135            Cont::bang(syn::parse_quote!(cb)),
2136        ]
2137        .into_iter()
2138        .collect();
2139        let dummy = list.invoke(template_quote::quote!(body));
2140        let out = redirect(dummy.clone(), &syn::parse_quote!(::x::Y)).unwrap();
2141        let out_s = s(&out);
2142        assert!(
2143            out_s.starts_with(
2144                "::x::Y!{@__announce_self{::x::Y}@__announce_dollar{$}[#[derive(p::D)]__AnnounceT_"
2145            ),
2146            "{}",
2147            out_s
2148        );
2149        assert!(out_s.ends_with("cb!]body}"), "{}", out_s);
2150        // The `$` is a real punct, not a string.
2151        let has_dollar = out.into_iter().any(|tt| match tt {
2152            proc_macro2::TokenTree::Group(g) => g
2153                .stream()
2154                .into_iter()
2155                .any(|t| matches!(t, proc_macro2::TokenTree::Group(g2) if g2.stream().into_iter().any(|x| matches!(x, proc_macro2::TokenTree::Punct(p) if p.as_char() == '$')))),
2156            _ => false,
2157        });
2158        assert!(has_dollar);
2159        // What `redirect` produces parses as an `Input` (the prefix is skipped).
2160        let input: Input = syn::parse2(
2161            match redirect(dummy, &syn::parse_quote!(::x::Y))
2162                .unwrap()
2163                .into_iter()
2164                .last()
2165            {
2166                Some(proc_macro2::TokenTree::Group(g)) => g.stream(),
2167                other => panic!("{:?}", other),
2168            },
2169        )
2170        .unwrap();
2171        assert_eq!(input.list.0.len(), 2);
2172        assert_eq!(s(input.rest), "body");
2173    }
2174}