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()`] runs 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()`] runs 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//! - Detect announce: every call that announce makes starts with the prefix
24//!   `@announce_format "1"` ([`format_prefix`], [`ANNOUNCE_FORMAT`], [`Input::format`]).
25//!
26//! The shape of the tokens that a callback receives is described at [`Input`].
27//!
28//! As `announce::helper`, this module needs the feature `proc-macro-api` of
29//! `announce`, which is on by default (`default-features = false` leaves it out, for
30//! `no_std` definitions).
31
32pub mod attrs;
33mod kv;
34pub mod leak;
35mod transformer;
36mod typeleak;
37
38pub use attrs::{remove_attributes, CheckCall, OwnedAttrs, Query};
39pub use kv::KvArgs;
40pub use transformer::{signature_only, transformer};
41/// The version of `type-leak` that [`TypeLeak`] uses. Name it through this crate,
42/// so there is never a second, incompatible copy.
43pub use type_leak;
44pub use typeleak::TypeLeak;
45
46// The macros `callback!` and `transformer!` (kept for compatibility; the functions of
47// the same names are the API) are `#[macro_export]`ed, so they live at the crate root
48// of `announce-core`; this makes them `announce::helper::callback!` and
49// `announce::helper::transformer!`. A macro and a function of one name do not clash:
50// they are in different namespaces. The macros are thin wrappers that call the
51// functions through their `$crate` paths, which point at this module.
52#[doc(hidden)]
53pub use crate::{callback, transformer};
54
55use proc_macro2::{Ident, Span, TokenStream};
56use std::sync::atomic::{AtomicU64, Ordering};
57use syn::ext::IdentExt;
58use syn::parse::{Parse, ParseStream};
59use syn::punctuated::Punctuated;
60use syn::{Attribute, Path, Result, Token};
61use template_quote::{quote, ToTokens};
62
63/// The version of the wire format, as written in the prefix `@announce_format "1"`
64/// that starts every call announce makes ([`format_prefix`]).
65///
66/// Bump it when the wire format changes in a way that a parser of the old version
67/// cannot read ([the wire format](Input#the-wire-format)). A parser of this crate
68/// accepts a call without the prefix (written by hand), and a call with this version.
69/// Any other version is an error that names both versions.
70pub const ANNOUNCE_FORMAT: &str = "1";
71
72/// The word after the `@` of the prefix: `@announce_format "1"`.
73const FORMAT_TAG: &str = "announce_format";
74
75/// The start of every error about a version that is not [`ANNOUNCE_FORMAT`].
76const FORMAT_ERROR: &str = "announce: incompatible wire format";
77
78/// The prefix `@announce_format "1"`, with the version [`ANNOUNCE_FORMAT`].
79///
80/// Every call that announce makes to the next element of a chain starts with these
81/// tokens, before everything else (`@__announce_self { .. }`, `(OPTIONS)`, `[LIST]`).
82/// A macro of your own can match them to know that it was called by announce
83/// ([the wire format](Input#the-wire-format)). A proc macro that builds such a call
84/// by hand starts it with this.
85pub fn format_prefix() -> TokenStream {
86    quote! { @announce_format #ANNOUNCE_FORMAT }
87}
88
89/// The error for a call with `@announce_format "found"`.
90fn format_error(span: Span, found: &str) -> syn::Error {
91    syn::Error::new(
92        span,
93        format!(
94            "{}: found `@announce_format {:?}`, expected `@announce_format {:?}`. The item was \
95             announced, or the macro that reads it was built, with another version of \
96             `announce`, whose wire format this version cannot read: use one version of \
97             `announce` in the whole chain",
98            FORMAT_ERROR, found, ANNOUNCE_FORMAT
99        ),
100    )
101}
102
103/// Whether `e` is the error of [`format_error`].
104pub(crate) fn is_format_error(e: &syn::Error) -> bool {
105    e.to_string().starts_with(FORMAT_ERROR)
106}
107
108/// Reads the prefix `@announce_format "VERSION"` if the input starts with it, and
109/// returns the version. A version other than [`ANNOUNCE_FORMAT`] is an error. Nothing
110/// is consumed when the input starts with anything else (a call written by hand).
111pub(crate) fn parse_format(input: ParseStream) -> Result<Option<String>> {
112    if !input.peek(Token![@]) {
113        return Ok(None);
114    }
115    let fork = input.fork();
116    let _: Token![@] = fork.parse()?;
117    match fork.parse::<Ident>() {
118        Ok(i) if i == FORMAT_TAG => {}
119        _ => return Ok(None),
120    }
121    let lit: syn::LitStr = fork.parse().map_err(|e| {
122        syn::Error::new(
123            e.span(),
124            "announce: `@announce_format` must be followed by a string literal, like \
125             `@announce_format \"1\"`",
126        )
127    })?;
128    syn::parse::discouraged::Speculative::advance_to(input, &fork);
129    let found = lit.value();
130    if found != ANNOUNCE_FORMAT {
131        return Err(format_error(lit.span(), &found));
132    }
133    Ok(Some(found))
134}
135
136/// Splits the prefix `@announce_format "VERSION"` off the start of `ts`, whatever the
137/// version is. The prefix is `None` when `ts` does not start with it.
138fn split_format(ts: TokenStream) -> (Option<TokenStream>, TokenStream) {
139    use proc_macro2::TokenTree;
140    let mut it = ts.into_iter();
141    let first: Vec<TokenTree> = it.by_ref().take(3).collect();
142    let is_prefix = matches!(
143        first.as_slice(),
144        [TokenTree::Punct(p), TokenTree::Ident(i), TokenTree::Literal(_)]
145            if p.as_char() == '@' && i == FORMAT_TAG
146    );
147    if is_prefix {
148        (Some(first.into_iter().collect()), it.collect())
149    } else {
150        (None, first.into_iter().chain(it).collect())
151    }
152}
153
154/// A random identifier `<prefix><16 hex digits>`, unique within the process.
155pub fn gensym(prefix: &str) -> Ident {
156    use std::hash::{BuildHasher, Hasher};
157    static COUNTER: AtomicU64 = AtomicU64::new(0);
158    let mut h = std::collections::hash_map::RandomState::new().build_hasher();
159    h.write_u64(COUNTER.fetch_add(1, Ordering::Relaxed));
160    Ident::new(&format!("{}{:016x}", prefix, h.finish()), Span::call_site())
161}
162
163/// One element of a continuation list (also the `KEY` of an entry).
164#[derive(Clone, Debug)]
165#[allow(clippy::large_enum_variant)]
166pub enum Cont {
167    /// `path::to::mac!`
168    Bang(Path),
169    /// `#[derive(path)] Name` or `#[attr(..)] Name`. `Name` is the dummy enum
170    /// that carries the payload; [`ContList::invoke`] fills it in.
171    Attr {
172        attr: Attribute,
173        name: Option<Ident>,
174    },
175}
176
177impl Cont {
178    /// `path!`. Generic arguments are dropped: `Enum<A>` becomes `Enum!`.
179    pub fn bang(mut path: Path) -> Self {
180        for seg in &mut path.segments {
181            seg.arguments = syn::PathArguments::None;
182        }
183        Cont::Bang(path)
184    }
185
186    /// `#[derive(path)]`
187    pub fn derive(path: Path) -> Self {
188        Cont::Attr {
189            attr: syn::parse_quote!(#[derive(#path)]),
190            name: None,
191        }
192    }
193
194    /// The macro path: `path` of `path!`, the first path of `#[derive(path)]`,
195    /// or the attribute path.
196    pub fn path(&self) -> Path {
197        match self {
198            Cont::Bang(p) => p.clone(),
199            Cont::Attr { attr, .. } => {
200                if attr.path().is_ident("derive") {
201                    let paths =
202                        attr.parse_args_with(Punctuated::<Path, Token![,]>::parse_terminated);
203                    if let Some(p) = paths.ok().and_then(|p| p.into_iter().next()) {
204                        return p;
205                    }
206                }
207                attr.path().clone()
208            }
209        }
210    }
211}
212
213fn parse_macro_path(input: ParseStream) -> Result<Path> {
214    let leading_colon: Option<Token![::]> = input.parse()?;
215    let mut segments = Punctuated::new();
216    loop {
217        let ident = Ident::parse_any(input)?;
218        segments.push_value(syn::PathSegment::from(ident));
219        if input.peek(Token![::]) {
220            segments.push_punct(input.parse()?);
221        } else {
222            break;
223        }
224    }
225    Ok(Path {
226        leading_colon,
227        segments,
228    })
229}
230
231impl Parse for Cont {
232    fn parse(input: ParseStream) -> Result<Self> {
233        if input.peek(Token![#]) {
234            let pound_token = input.parse()?;
235            let content;
236            let bracket_token = syn::bracketed!(content in input);
237            let attr = Attribute {
238                pound_token,
239                style: syn::AttrStyle::Outer,
240                bracket_token,
241                meta: content.parse()?,
242            };
243            let name = if input.peek(syn::Ident) {
244                Some(input.parse()?)
245            } else {
246                None
247            };
248            Ok(Cont::Attr { attr, name })
249        } else {
250            let path = parse_macro_path(input)?;
251            input.parse::<Token![!]>()?;
252            Ok(Cont::Bang(path))
253        }
254    }
255}
256
257impl Cont {
258    /// The comparison key: the tokens without the dummy name of an attribute
259    /// continuation.
260    fn eq_key(&self) -> String {
261        match self {
262            Cont::Bang(_) => self.to_token_stream().to_string(),
263            Cont::Attr { attr, .. } => attr.to_token_stream().to_string(),
264        }
265    }
266}
267
268/// Continuations compare by tokens, ignoring spans and the dummy name.
269impl PartialEq for Cont {
270    fn eq(&self, other: &Self) -> bool {
271        self.eq_key() == other.eq_key()
272    }
273}
274impl Eq for Cont {}
275impl std::hash::Hash for Cont {
276    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
277        self.eq_key().hash(state)
278    }
279}
280
281impl ToTokens for Cont {
282    fn to_tokens(&self, tokens: &mut TokenStream) {
283        match self {
284            Cont::Bang(p) => {
285                // Macro paths cannot carry generic arguments.
286                let mut p = p.clone();
287                for seg in &mut p.segments {
288                    seg.arguments = syn::PathArguments::None;
289                }
290                tokens.extend(quote! { #p ! })
291            }
292            Cont::Attr { attr, name } => tokens.extend(quote! { #attr #name }),
293        }
294    }
295}
296
297/// A continuation list, head first.
298#[derive(Clone, Debug, Default)]
299pub struct ContList(pub Vec<Cont>);
300
301impl Parse for ContList {
302    /// Parses the contents of the brackets: `a!, #[derive(B)], c!`.
303    fn parse(input: ParseStream) -> Result<Self> {
304        let p: Punctuated<Cont, Token![,]> = Punctuated::parse_terminated(input)?;
305        Ok(ContList(p.into_iter().collect()))
306    }
307}
308
309impl ToTokens for ContList {
310    /// Emits `[a!, b!]` including the brackets.
311    fn to_tokens(&self, tokens: &mut TokenStream) {
312        let items = &self.0;
313        tokens.extend(quote! { [ #(#items),* ] });
314    }
315}
316
317impl FromIterator<Cont> for ContList {
318    fn from_iter<I: IntoIterator<Item = Cont>>(iter: I) -> Self {
319        ContList(iter.into_iter().collect())
320    }
321}
322
323/// Whether a global options group (the tokens inside `( .. )` before the
324/// continuation list) puts the chain in expression position. It does when its
325/// **first** token is the identifier `expr`, as in `(expr)` or `(expr, other)`.
326/// The generated trampolines use the same rule, so `(other, expr)` does not select
327/// it in either. Every other option is a namespace for helpers, forwarded
328/// unchanged.
329pub fn is_expression_position(options: &TokenStream) -> bool {
330    matches!(
331        options.clone().into_iter().next(),
332        Some(proc_macro2::TokenTree::Ident(i)) if i == "expr"
333    )
334}
335
336impl ContList {
337    /// Gives every attribute continuation a random dummy name, so two
338    /// trampolines in one module never collide.
339    pub fn name_dummies(&mut self) {
340        for c in &mut self.0 {
341            if let Cont::Attr { name, .. } = c {
342                if name.is_none() {
343                    *name = Some(gensym("__AnnounceT_"));
344                }
345            }
346        }
347    }
348
349    /// Invokes the head with `@announce_format "1" [self] body`, where `body` is
350    /// `ENTRY* REST` (a derive or attribute head gets the same tokens in the dummy
351    /// enum).
352    pub fn invoke(&self, body: TokenStream) -> TokenStream {
353        self.invoke_with_options(None, body)
354    }
355
356    /// Like [`invoke`](Self::invoke), with a global options group
357    /// `@announce_format "1" (options) [..] ..`.
358    /// A derive or attribute head is refused (`compile_error!`) when the group
359    /// starts with `expr` ([`is_expression_position`]).
360    pub fn invoke_with_options(
361        &self,
362        options: Option<&TokenStream>,
363        body: TokenStream,
364    ) -> TokenStream {
365        let opts = options.map(|o| quote! { ( #o ) });
366        let in_expr = options.map_or(false, is_expression_position);
367        let mut list = self.clone();
368        list.name_dummies();
369        let format = format_prefix();
370        match list.0.first() {
371            None => quote! { compile_error! { "announce: empty continuation list" } },
372            Some(Cont::Attr { .. }) if in_expr => quote! {
373                compile_error! { "announce: a derive/attribute continuation emits items, so it cannot be used with `(expr)`" }
374            },
375            Some(Cont::Bang(p)) => quote! { #p ! { #format #opts #list #body } },
376            Some(Cont::Attr { attr, name }) => quote! {
377                #attr
378                #[allow(dead_code, non_camel_case_types)]
379                enum #name { __AnnouncePayload = (stringify! { #format #opts #list #body }, 0).1 }
380            },
381        }
382    }
383}
384
385/// One `META` element: `path` or `path(args)`.
386#[derive(Clone, Debug)]
387pub struct Meta {
388    /// `path` of `path(args)`.
389    pub path: Path,
390    /// The tokens inside the parentheses, if any.
391    pub args: Option<TokenStream>,
392}
393
394impl Meta {
395    /// Parses the args with `parser`.
396    pub fn parse_args_with<P: syn::parse::Parser>(&self, parser: P) -> Result<P::Output> {
397        parser.parse2(self.args.clone().unwrap_or_default())
398    }
399
400    /// The args as a [`KvArgs`] list. Absent args give an empty list.
401    pub fn kv(&self) -> Result<KvArgs> {
402        self.parse_args_with(KvArgs::parse)
403    }
404
405    /// The args as `key = value` pairs. Values must be expressions; use
406    /// [`kv`](Self::kv) for types and lists.
407    pub fn name_values(&self) -> Result<Vec<syn::MetaNameValue>> {
408        let p: Punctuated<syn::MetaNameValue, Token![,]> =
409            self.parse_args_with(Punctuated::parse_terminated)?;
410        Ok(p.into_iter().collect())
411    }
412}
413
414impl Parse for Meta {
415    fn parse(input: ParseStream) -> Result<Self> {
416        let path = Path::parse_mod_style(input)?;
417        let args = if input.peek(syn::token::Paren) {
418            let content;
419            syn::parenthesized!(content in input);
420            Some(content.parse()?)
421        } else {
422            None
423        };
424        Ok(Meta { path, args })
425    }
426}
427
428impl ToTokens for Meta {
429    fn to_tokens(&self, tokens: &mut TokenStream) {
430        let p = &self.path;
431        match &self.args {
432            Some(a) => tokens.extend(quote! { #p(#a) }),
433            None => tokens.extend(quote! { #p }),
434        }
435    }
436}
437
438/// `KEY => [META] { ITEM }`
439#[derive(Clone, Debug)]
440pub struct Entry {
441    /// The continuation that produced this entry, as written in the list.
442    pub key: Cont,
443    /// The META elements: checks, `meta =`, `mock(..)`, `type_leak(..)`.
444    pub meta: Vec<Meta>,
445    /// The raw item tokens.
446    pub item: TokenStream,
447}
448
449impl Entry {
450    /// The payload parsed as an item.
451    pub fn item(&self) -> Result<syn::Item> {
452        syn::parse2(self.item.clone())
453    }
454
455    /// The path of the `mock(path)` META element, if there is one: the item stands
456    /// in for the item at that path.
457    pub fn mock(&self) -> Option<Path> {
458        self.meta
459            .iter()
460            .find(|m| m.path.is_ident("mock"))
461            .and_then(|m| syn::parse2(m.args.clone()?).ok())
462    }
463
464    /// The first META element whose last path segment is `name`. Use it to read
465    /// a `meta = lib::info(..)` or a check.
466    pub fn find_meta(&self, name: &str) -> Option<&Meta> {
467        self.meta
468            .iter()
469            .find(|m| m.path.segments.last().map_or(false, |s| s.ident == name))
470    }
471
472    /// The `type_leak(..)` META of an entry announced with `repeater = ..`.
473    /// Unknown keys are ignored.
474    pub fn type_leak(&self) -> Result<Option<TypeLeak>> {
475        let m = match self.find_meta("type_leak") {
476            Some(m) => m,
477            None => return Ok(None),
478        };
479        TypeLeak::from_kv(&mut m.kv()?).map(Some)
480    }
481
482    /// Whether META lists a check whose last path segment is `name`. Only the
483    /// last segment is compared, since the path is as the definer wrote it.
484    pub fn has_check(&self, name: &str) -> bool {
485        self.meta
486            .iter()
487            .any(|m| m.path.segments.last().map_or(false, |s| s.ident == name))
488    }
489
490    /// The attribute names that the definition's checks own: the `attrs(..)` META
491    /// element (empty without one). The definer removed these attributes from the
492    /// item it emitted, but they are still in [`item`](Self::item), where a
493    /// callback can read them ([`OwnedAttrs::find`]).
494    ///
495    /// A callback that emits the item itself must remove them first, because rustc
496    /// rejects an attribute that no macro declares. Use
497    /// [`item_without_owned_attrs`](Self::item_without_owned_attrs).
498    pub fn owned_attrs(&self) -> OwnedAttrs {
499        attrs::owned_from_metas(&self.meta)
500    }
501
502    /// The payload parsed as an item, without the owned attributes: the item as the
503    /// definer emitted it. The same as [`item`](Self::item) when the definition has
504    /// no owned attributes.
505    pub fn item_without_owned_attrs(&self) -> Result<syn::Item> {
506        let mut item = self.item()?;
507        self.owned_attrs().strip(&mut item)?;
508        Ok(item)
509    }
510}
511
512impl Parse for Entry {
513    fn parse(input: ParseStream) -> Result<Self> {
514        let key = input.parse()?;
515        input.parse::<Token![=>]>()?;
516        let meta_content;
517        syn::bracketed!(meta_content in input);
518        let meta: Punctuated<Meta, Token![,]> = Punctuated::parse_terminated(&meta_content)?;
519        let item_content;
520        syn::braced!(item_content in input);
521        Ok(Entry {
522            key,
523            meta: meta.into_iter().collect(),
524            item: item_content.parse()?,
525        })
526    }
527}
528
529impl ToTokens for Entry {
530    fn to_tokens(&self, tokens: &mut TokenStream) {
531        let (key, meta, item) = (&self.key, &self.meta, &self.item);
532        tokens.extend(quote! { #key => [ #(#meta),* ] { #item } });
533    }
534}
535
536/// What a callback receives: `@announce_format "1" (OPTIONS)? [LIST] ENTRY* REST`.
537///
538/// # The wire format
539///
540/// A callback receives these tokens:
541///
542/// ```text
543/// @announce_format "1" (OPTIONS)? [LIST] ENTRY* REST
544/// ```
545///
546/// - `@announce_format "1"`: the *format prefix*. Every call that announce makes to
547///   the next element of a chain starts with it, whatever the element is: a
548///   trampoline, a transformer, a callback, a check. `"1"` is the version of the wire
549///   format ([`ANNOUNCE_FORMAT`]); it changes when this format changes in a way an old
550///   reader cannot read. A call written by hand does not need the prefix, and every
551///   parser of announce reads a call with or without it ([`Input::format`] is the
552///   version found). A call with another version is one compile error that names both
553///   versions, because the item and the macro that reads it were built with
554///   incompatible versions of announce. [How a macro of your own detects the
555///   prefix](#detecting-a-call-from-announce).
556///   It starts every call that announce makes, also the call of the sentinel of the runner,
557///   `NAME! { @announce_format "1" @__announce_exists }`.
558///   (The *runner* is the hidden attribute that `#[announce]` puts on an item that has other
559///   attributes. Its arguments have their own marker, `__announce_format_version = "1"`: it
560///   versions the encoding of the arguments of an attribute, which the runners of two
561///   versions of announce compare when they meet on one item. The runner is not a call of a
562///   chain, and has no `@announce_format`; the calls of a chain are not attributes, and have
563///   no `__announce_format_version`.)
564/// - `OPTIONS`: global options, forwarded unchanged by every hop. Only a leading
565///   `expr` has a meaning: it puts the chain in expression position, where derive
566///   and attribute elements are refused ([`is_expression_position`]). The rest is
567///   free for helpers ([`Input::has_option`]).
568/// - `LIST`: the *continuation list*, the calls still to make, head first. The head
569///   is the macro that is called. An element ([`Cont`]) is `path!`,
570///   `#[derive(path)] Name` or `#[attr] Name`. The last two are the derive and
571///   attribute route: `Name` is a dummy enum that carries the tokens, and a derive or
572///   attribute macro hands them on with [`redirect`].
573/// - `ENTRY`: `KEY => [META] { ITEM }`, one per definition ([`Entry`]). `KEY` is the
574///   list element that produced the entry, as written.
575/// - `REST`: tokens that the protocol never touches. A parser reads entries greedily,
576///   so wrap a `REST` that starts like an entry.
577///
578/// How a chain runs:
579///
580/// - Each macro drops its own element (the head) from the list, adds an entry, and
581///   calls the next element with the rest of the list. The last element is the
582///   callback: it runs once, with one entry per trampoline.
583/// - New entries go in front, so they are newest first.
584///   [`Input::entries_in_order`] gives oldest first.
585/// - `[META]` is always written, even when empty (`[]`).
586/// - A trampoline can get the prefixes `@__announce_self { PATH }` and
587///   `@__announce_dollar { $ }` after the format prefix, before the rest of this input:
588///   `@announce_format "1" @__announce_self { PATH } @__announce_dollar { $ } ..`. Only
589///   trampolines and [`redirect`] use them. Parsing an [`Input`] skips them.
590/// - The calls that announce makes to a check have the prefix too: the query
591///   `@announce_format "1" @__announce_attrs [..] { STATE }` and the check itself,
592///   `@announce_format "1" (ARGS), ITEM` ([`attrs`]).
593///
594/// `META` holds these elements, as the definer wrote them:
595///
596/// - `path` or `path(args)`: a check (`#[announce(path(args))]`);
597/// - `path(args)`: a `meta = path(args)` entry, never called ([`Entry::find_meta`]);
598/// - `mock(path)`: the item stands in for the item at `path` ([`Entry::mock`]);
599/// - `attrs(a, b)`: the attribute names that checks own ([`Entry::owned_attrs`]);
600/// - `type_leak(..)`: the type table of a `repeater` ([`Entry::type_leak`]).
601///
602/// Ignore elements you do not know.
603///
604/// ```
605/// # use announce_core as announce;
606/// use announce::helper::Input;
607///
608/// let input: Input = syn::parse_quote! {
609///     @announce_format "1"
610///     [cb!]
611///     a::F! => [my_lib::check, mock(::core::iter::Iterator)] { pub trait F {} }
612///     REST tokens
613/// };
614/// assert_eq!(input.format.as_deref(), Some("1"));
615/// let entry = &input.entries[0];
616/// assert!(entry.has_check("check"));
617/// assert!(entry.mock().is_some());
618/// assert_eq!(input.rest.to_string(), "REST tokens");
619///
620/// // A call by hand has no prefix, and parses as well.
621/// let by_hand: Input = syn::parse_quote! { [cb!] a::F! => [] { pub trait F {} } };
622/// assert_eq!(by_hand.format, None);
623///
624/// // Another version is one error, which names the version found and the one read.
625/// let err = syn::parse2::<Input>(syn::parse_quote! { @announce_format "2" [cb!] }).unwrap_err();
626/// assert!(err.to_string().contains("found `@announce_format \"2\"`"));
627/// assert!(err.to_string().contains("expected `@announce_format \"1\"`"));
628/// ```
629///
630/// # Detecting a call from announce
631///
632/// A macro of your own, a `macro_rules!` callback, check or transformer, can match
633/// the prefix to know that announce called it, and which version of the wire format
634/// the call has. Put the arm for the prefix first. The arm for the call by hand
635/// (without it) stays, if you want one:
636///
637/// ```
638/// macro_rules! who_calls {
639///     // Called by announce: the prefix, then `[LIST] ENTRY* REST`.
640///     (@announce_format "1" [$($list:tt)*] $($rest:tt)*) => {
641///         const CALLER: &str = "announce";
642///     };
643///     // Called by hand, or with a version that this macro does not know.
644///     ($($rest:tt)*) => {
645///         const CALLER: &str = "not announce, or another format";
646///     };
647/// }
648///
649/// who_calls! { @announce_format "1" [who_calls!] }
650/// assert_eq!(CALLER, "announce");
651/// ```
652///
653/// A macro that forwards the call to the next element starts the forwarded call with
654/// the prefix again; [`Input::forward`] does it, and [`format_prefix`] gives the
655/// tokens to a proc macro that builds the call itself. A `macro_rules!` callback
656/// that is written for one version only says so in its pattern, as above: a call
657/// with another version matches no arm, and rustc reports the call.
658///
659/// # A real example
660///
661/// This is what a callback gets from a real chain, captured by the test
662/// `tests/user/tests/wire.rs`. The definitions, in `tests/defs/src/wire.rs`
663/// (`Meters` is private to the defining crate, so `Length` has a `repeater`):
664///
665/// ```ignore
666/// ::announce::define_repeater!(pub Repeater);
667///
668/// /// A point.
669/// #[announce(announce_test_derive::lib_check, meta = crate::info(1))]
670/// pub struct Point {
671///     /// The x coordinate.
672///     #[my_lib(skip)]
673///     pub x: u8,
674///     pub y: u8,
675/// }
676///
677/// #[announce(repeater = crate::wire::Repeater)]
678/// pub struct Length(pub Meters);
679/// ```
680///
681/// The chain, in another crate. `show!` is the callback; it prints its whole input:
682///
683/// ```ignore
684/// use announce_test_defs::wire as defs;
685///
686/// macro_rules! show {
687///     ($($t:tt)*) => { stringify!($($t)*) };
688/// }
689///
690/// let wire: &str = defs::Point! { (expr) [defs::Point!, defs::Length!, show!] some rest };
691/// ```
692///
693/// `wire` is this text. The tokens are the ones received, in order. Only the
694/// layout is added by hand, and the random number of `id` (a different one for each
695/// build) is replaced by `ID`. The test compares this block with what the chain
696/// gives, so it cannot drift:
697///
698/// <!-- wire-example -->
699/// ```text
700/// @announce_format "1"
701/// (expr)
702/// [show!]
703/// defs::Length! => [
704///     type_leak(repeater = $crate::wire::Repeater, id = ID,
705///               context = 0, referrer = ["type-leak-format-14" (Meters)])
706/// ] {
707///     pub struct Length(pub Meters);
708/// }
709/// defs::Point! => [
710///     announce_test_derive::lib_check,
711///     $crate::info(1),
712///     attrs(my_lib)
713/// ] {
714///     pub struct Point {
715///         #[my_lib(skip)]
716///         pub x: u8,
717///         pub y: u8,
718///     }
719/// }
720/// some rest
721/// ```
722///
723/// How the parts map to the fields of `Input`:
724///
725/// | Tokens | Field | Notes |
726/// |---|---|---|
727/// | `@announce_format "1"` | [`format`](Input::format) | The prefix, written by both hops. |
728/// | `(expr)` | [`options`](Input::options) | From the call, forwarded by both hops. |
729/// | `[show!]` | [`list`](Input::list) | Only the callback is left: `Point!` and `Length!` each dropped themselves. |
730/// | `defs::Length! => [..] { .. }` | `entries[0]` | The newest entry, the last hop. Its `key` is as written in the call. |
731/// | `defs::Point! => [..] { .. }` | `entries[1]` | The oldest entry. |
732/// | `some rest` | [`rest`](Input::rest) | The caller's tokens, untouched. |
733///
734/// What is in the entries:
735///
736/// - `Length`: `type_leak(..)` is the table of the `repeater` ([`Entry::type_leak`]).
737///   The item still says `Meters`, which only the defining crate can resolve;
738///   the table is what a callback uses to write the type from anywhere
739///   ([`TypeLeak::apply`]).
740/// - `Point`: `announce_test_derive::lib_check` is the check, `$crate::info(1)` is
741///   `meta = crate::info(1)` (`crate` became `$crate`, so that the path resolves where
742///   the callback runs), and `attrs(my_lib)` lists the attribute names that the
743///   check owns.
744/// - The doc comments of `Point` are gone from the item, because the payload has
745///   no docs unless `docs = true`. `#[my_lib(skip)]` is still there: the definer
746///   removed it from the item it emitted, but the callback can read it.
747///
748/// # `self` and hygiene
749///
750/// `self` is hygienic. The `self` in the receiver of a method that the callback
751/// receives (`&self`) belongs to the trampoline's expansion; a `self` that the
752/// callback writes itself, in its own `quote!`, is another one to rustc. A callback
753/// that mixes the received tokens with its own output, for example the received
754/// signature with a body it writes, must use the `self` token of the payload in that
755/// body. Otherwise rustc reports `E0424` ("cannot find value `self` in this
756/// scope"). Take the token from the receiver of the parsed signature:
757///
758/// ```
759/// # use announce_core as announce;
760/// use announce::helper::Input;
761/// use template_quote::quote;
762///
763/// // For `trait Get { fn get(&self) -> u8; }`: an impl that forwards `get` to a field.
764/// fn forward(input: &Input) -> syn::Result<proc_macro2::TokenStream> {
765///     let item = match input.entries[0].item()? {
766///         syn::Item::Trait(t) => t,
767///         _ => return Err(syn::Error::new(proc_macro2::Span::call_site(), "expected a trait")),
768///     };
769///     let sig = match &item.items[0] {
770///         syn::TraitItem::Fn(f) => &f.sig,
771///         _ => return Err(syn::Error::new(proc_macro2::Span::call_site(), "expected a method")),
772///     };
773///     let (trait_name, name) = (&item.ident, &sig.ident);
774///     // The `self` of the payload: a `quote! { self.inner }` here would not match `#sig`.
775///     let this = &sig.receiver().expect("a method with a receiver").self_token;
776///     Ok(quote! {
777///         impl #trait_name for Wrapper { #sig { #this.inner.#name() } }
778///     })
779/// }
780///
781/// let input: Input = syn::parse_quote! { [cb!] a::Get! => [] { trait Get { fn get(&self) -> u8; } } };
782/// let out = forward(&input).unwrap().to_string();
783/// assert!(out.contains("fn get (& self) -> u8 { self . inner . get () }"), "{}", out);
784/// ```
785#[derive(Clone, Debug)]
786pub struct Input {
787    /// The version of the prefix `@announce_format "VERSION"` that the call started
788    /// with ([`ANNOUNCE_FORMAT`] when there is one), or `None` for a call without it
789    /// (written by hand). A call with another version does not parse.
790    ///
791    /// [`forward`](Self::forward), [`fetch`](Self::fetch) and [`gather`](Self::gather)
792    /// always write the prefix with the version of this crate, whatever this field is.
793    pub format: Option<String>,
794    /// The global options group `(..)`. [`forward`](Self::forward) passes it on
795    /// unchanged. [`fetch`](Self::fetch) and [`gather`](Self::gather) add
796    /// `__announce_fetched(..)` to it, which records what they fetched (to detect
797    /// a continuation that is not a trampoline).
798    pub options: Option<TokenStream>,
799    /// The continuation list; its head is the callback itself.
800    pub list: ContList,
801    /// Newest first.
802    pub entries: Vec<Entry>,
803    /// REST: tokens after the entries, untouched by the protocol.
804    pub rest: TokenStream,
805}
806
807impl Parse for Input {
808    fn parse(input: ParseStream) -> Result<Self> {
809        // `@announce_format "1"` comes first, before everything else.
810        let format = parse_format(input)?;
811        // The `@__announce_self { path }` and `@__announce_dollar { $ }`
812        // prefixes are meant for trampolines; skip them if a callback
813        // receives them.
814        while input.peek(Token![@]) {
815            let fork = input.fork();
816            let _: Token![@] = fork.parse()?;
817            match fork.parse::<Ident>() {
818                Ok(i) if i == "__announce_dollar" || i == "__announce_self" => {
819                    let _g: proc_macro2::Group = fork.parse()?;
820                    syn::parse::discouraged::Speculative::advance_to(input, &fork);
821                }
822                _ => break,
823            }
824        }
825        let options = if input.peek(syn::token::Paren) {
826            let o;
827            syn::parenthesized!(o in input);
828            Some(o.parse()?)
829        } else {
830            None
831        };
832        let content;
833        syn::bracketed!(content in input);
834        let list = content.parse()?;
835        let mut entries = Vec::new();
836        loop {
837            let fork = input.fork();
838            match fork.parse::<Entry>() {
839                Ok(e) => {
840                    entries.push(e);
841                    syn::parse::discouraged::Speculative::advance_to(input, &fork);
842                }
843                Err(e) => {
844                    // `KEY =>` means an entry was intended: report it instead
845                    // of silently treating it as REST.
846                    // (`KEY => [` or `KEY => {` only; `foo! => bar` is valid
847                    // REST.)
848                    let probe = input.fork();
849                    if probe.parse::<Cont>().is_ok()
850                        && probe.parse::<Token![=>]>().is_ok()
851                        && (probe.peek(syn::token::Bracket) || probe.peek(syn::token::Brace))
852                    {
853                        return Err(syn::Error::new(
854                            e.span(),
855                            format!("announce: malformed entry: {}", e),
856                        ));
857                    }
858                    break;
859                }
860            }
861        }
862        Ok(Input {
863            format,
864            options,
865            list,
866            entries,
867            rest: input.parse()?,
868        })
869    }
870}
871
872impl Input {
873    /// Entries in the order their definitions were visited (oldest first).
874    pub fn entries_in_order(&self) -> impl Iterator<Item = &Entry> {
875        self.entries.iter().rev()
876    }
877
878    /// Fetches more definitions, then calls this callback again. It invokes
879    /// `new[0]` with the list `[new.., <this list>]`, so the new entries arrive in
880    /// front of the old ones.
881    ///
882    /// The paths are recorded in the options group as `__announce_fetched(..)`. If
883    /// this callback is called again with a recorded path but no entry for it, that
884    /// path is not an `#[announce]` trampoline. Then `fetch` emits a
885    /// `compile_error!` instead of fetching until the recursion limit. A callback
886    /// that fetches only what is still missing (as [`gather`](Self::gather) does)
887    /// never meets that error for a trampoline.
888    pub fn fetch(&self, new: impl IntoIterator<Item = Cont>) -> TokenStream {
889        self.fetch_conts(new.into_iter().collect())
890    }
891
892    /// The bookkeeping of [`fetch`](Self::fetch) and [`gather`](Self::gather):
893    /// refuses a continuation that was fetched before and left no entry,
894    /// records the new ones in the options group, and invokes `[new.., <list>]`.
895    fn fetch_conts(&self, new: Vec<Cont>) -> TokenStream {
896        // Already fetched once, but no entry came back: not a trampoline. The
897        // bookkeeping travels in the options group.
898        let fetched = self.fetched_paths();
899        if let Some(c) = new
900            .iter()
901            .find(|c| fetched.contains(&path_key(&c.path())) && self.entry_for(c).is_none())
902        {
903            let msg = format!(
904                "announce: `{}` was fetched but did not provide a definition; is it an `#[announce]` trampoline?",
905                c.to_token_stream()
906            );
907            return quote! { compile_error! { #msg } };
908        }
909        let mut options: Vec<TokenStream> = self.option_items();
910        if !new.is_empty() {
911            let paths = new.iter().map(|c| c.path());
912            options.push(quote! { __announce_fetched(#(#paths),*) });
913        }
914        let opts = if options.is_empty() {
915            None
916        } else {
917            Some(quote! { #(#options),* })
918        };
919        let list: ContList = new.into_iter().chain(self.list.0.iter().cloned()).collect();
920        let (entries, rest) = (&self.entries, &self.rest);
921        list.invoke_with_options(opts.as_ref(), quote! { #(#entries)* #rest })
922    }
923
924    /// Whether the global options group contains the ident `name`.
925    pub fn has_option(&self, name: &str) -> bool {
926        self.options.as_ref().map_or(false, |o| {
927            o.clone()
928                .into_iter()
929                .any(|tt| matches!(tt, proc_macro2::TokenTree::Ident(i) if i == name))
930        })
931    }
932
933    /// The comma-separated elements of the options group.
934    pub fn option_items(&self) -> Vec<TokenStream> {
935        let mut items = Vec::new();
936        let mut cur = TokenStream::new();
937        for tt in self.options.clone().unwrap_or_default() {
938            if matches!(&tt, proc_macro2::TokenTree::Punct(p) if p.as_char() == ',') {
939                if !cur.is_empty() {
940                    items.push(std::mem::take(&mut cur));
941                }
942            } else {
943                cur.extend(Some(tt));
944            }
945        }
946        if !cur.is_empty() {
947            items.push(cur);
948        }
949        items
950    }
951
952    fn fetched_paths(&self) -> Vec<String> {
953        let mut out = Vec::new();
954        for item in self.option_items() {
955            let parsed: Result<Meta> = syn::parse2(item);
956            if let Ok(m) = parsed {
957                if m.path.is_ident("__announce_fetched") {
958                    let paths: Result<Punctuated<Path, Token![,]>> =
959                        m.parse_args_with(Punctuated::parse_terminated);
960                    if let Ok(paths) = paths {
961                        out.extend(paths.iter().map(path_key));
962                    }
963                }
964            }
965        }
966        out
967    }
968
969    /// Parses REST as `T`.
970    pub fn rest_as<T: Parse>(&self) -> Result<T> {
971        syn::parse2(self.rest.clone())
972    }
973
974    /// The entry whose key names `cont` (compared by macro path, with generic
975    /// arguments ignored), newest first.
976    pub fn entry_for(&self, cont: &Cont) -> Option<&Entry> {
977        let want = path_key(&cont.path());
978        self.entries
979            .iter()
980            .find(|e| path_key(&e.key.path()) == want)
981    }
982
983    /// Asks for definitions: "give me these, then call me again".
984    ///
985    /// Returns [`Gather::Ready`] with one entry per requested continuation, in
986    /// order, when all are present. Otherwise it returns [`Gather::Fetch`]: tokens
987    /// that fetch the missing ones and call this callback again.
988    ///
989    /// ```ignore
990    /// let entries = match input.gather(needed) {
991    ///     Gather::Ready(e) => e,
992    ///     Gather::Fetch(ts) => return Ok(ts),
993    /// };
994    /// ```
995    pub fn gather(&self, needed: impl IntoIterator<Item = Cont>) -> Gather<'_> {
996        let needed: Vec<Cont> = needed.into_iter().collect();
997        // A callback asking for itself (or for its own head path) would
998        // re-fetch forever.
999        if let Some(head) = self.list.0.first() {
1000            if let Some(c) = needed
1001                .iter()
1002                .find(|c| path_key(&c.path()) == path_key(&head.path()))
1003            {
1004                let msg = format!(
1005                    "announce: `gather` asked for `{}`, which is the callback itself; \
1006                     request the definitions' trampolines instead",
1007                    c.to_token_stream()
1008                );
1009                return Gather::Fetch(quote! { compile_error! { #msg } });
1010            }
1011        }
1012        let mut missing: Vec<Cont> = Vec::new();
1013        for c in &needed {
1014            if self.entry_for(c).is_none()
1015                && !missing
1016                    .iter()
1017                    .any(|m| path_key(&m.path()) == path_key(&c.path()))
1018            {
1019                missing.push(c.clone());
1020            }
1021        }
1022        if missing.is_empty() {
1023            return Gather::Ready(
1024                needed
1025                    .iter()
1026                    .map(|c| self.entry_for(c).expect("checked above"))
1027                    .collect(),
1028            );
1029        }
1030        Gather::Fetch(self.fetch_conts(missing))
1031    }
1032
1033    /// Continues the chain: drops the head and invokes the next element. Returns
1034    /// `None` if the head was the last element.
1035    pub fn forward(&self) -> Option<TokenStream> {
1036        if self.list.0.len() < 2 {
1037            return None;
1038        }
1039        let tail: ContList = self.list.0[1..].iter().cloned().collect();
1040        let (entries, rest) = (&self.entries, &self.rest);
1041        Some(tail.invoke_with_options(self.options.as_ref(), quote! { #(#entries)* #rest }))
1042    }
1043}
1044
1045/// Result of [`Input::gather`].
1046pub enum Gather<'a> {
1047    /// All requested definitions are present, in the requested order.
1048    Ready(Vec<&'a Entry>),
1049    /// Emit these tokens: they fetch the missing definitions and call back.
1050    Fetch(TokenStream),
1051}
1052
1053fn path_key(p: &Path) -> String {
1054    let mut p = p.clone();
1055    for seg in &mut p.segments {
1056        seg.arguments = syn::PathArguments::None;
1057    }
1058    p.to_token_stream().to_string()
1059}
1060
1061#[doc(hidden)]
1062pub mod __private {
1063    pub use proc_macro2;
1064    pub use syn;
1065}
1066
1067/// Runs a callback: the body of a `#[proc_macro]` that receives definitions from
1068/// trampolines.
1069///
1070/// - `input` is the input of the proc macro, the tokens a trampoline sends. It is
1071///   parsed as an [`Input`]; see there for the shape.
1072/// - `f` is the callback itself. It gets the parsed [`Input`] by value and returns the
1073///   tokens to emit, or a `syn::Error`.
1074///
1075/// A `syn::Error` (the input does not parse as an [`Input`], or `f` fails) becomes a
1076/// `compile_error!` in the result, so the result can be returned from the proc macro
1077/// as it is.
1078///
1079/// In a proc-macro crate this is a `#[proc_macro]` function. The example is run as a
1080/// doctest, which cannot define a proc macro: it has no attribute, and `proc_macro`
1081/// stands for `proc_macro2`.
1082///
1083/// ```
1084/// # use announce_core as announce;
1085/// # use announce::helper::{Cont, Entry, Input};
1086/// # use proc_macro2 as proc_macro;
1087/// # fn needed(_: &Input) -> syn::Result<Vec<Cont>> {
1088/// #     Ok(vec![Cont::bang(syn::parse_quote!(a::Get))])
1089/// # }
1090/// # fn generate(entries: Vec<&Entry>) -> syn::Result<proc_macro::TokenStream> {
1091/// #     let n = entries.len();
1092/// #     Ok(template_quote::quote!(const COUNT: usize = #n;))
1093/// # }
1094/// /// Receives trait definitions.
1095/// pub fn my_callback(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1096///     announce::helper::callback(input.into(), |input| {
1097///         // `input` is an `announce::helper::Input`.
1098///         let entries = match input.gather(needed(&input)?) {
1099///             announce::helper::Gather::Ready(e) => e,
1100///             announce::helper::Gather::Fetch(ts) => return Ok(ts),
1101///         };
1102///         generate(entries)
1103///     })
1104///     .into()
1105/// }
1106/// #
1107/// # // `a::Get!` is there: `generate` runs.
1108/// # let out = my_callback(syn::parse_quote! { [cb!] a::Get! => [] { trait Get {} } });
1109/// # assert_eq!(out.to_string(), "const COUNT : usize = 1usize ;");
1110/// # // It is missing: the callback fetches it and is called again.
1111/// # let out = my_callback(syn::parse_quote! { [cb!] }).to_string();
1112/// # assert!(out.starts_with("a :: Get !") && out.contains("cb !"), "{}", out);
1113/// ```
1114///
1115/// The function can be tested with `proc_macro2` streams, because it does not use the
1116/// `proc_macro` crate:
1117///
1118/// ```
1119/// # use announce_core as announce;
1120/// use announce::helper::callback;
1121/// use proc_macro2::TokenStream;
1122/// use template_quote::quote;
1123///
1124/// let run = |input: TokenStream| {
1125///     callback(input, |input| {
1126///         let name: syn::Ident = input.rest_as()?;
1127///         let n = input.entries.len();
1128///         Ok(quote!(const #name: usize = #n;))
1129///     })
1130///     .to_string()
1131/// };
1132/// // One definition, then the caller's tokens.
1133/// let out = run(syn::parse_quote! { [cb!] a::S! => [] { struct S; } COUNT });
1134/// assert_eq!(out, "const COUNT : usize = 1usize ;");
1135/// // Input that is not a callback input, and an error of the callback.
1136/// assert!(run(syn::parse_quote! { not a callback input }).contains("compile_error"));
1137/// assert!(run(syn::parse_quote! { [cb!] 1 + 1 }).contains("compile_error"));
1138/// ```
1139///
1140/// The macro `callback!` (`announce::helper::callback! { pub fn name(input) { .. } }`)
1141/// does the same and defines the `#[proc_macro]` function too. It is kept for
1142/// compatibility; write the function above instead.
1143pub fn callback(input: TokenStream, f: impl FnOnce(Input) -> Result<TokenStream>) -> TokenStream {
1144    syn::parse2::<Input>(input)
1145        .and_then(f)
1146        .unwrap_or_else(|e| e.to_compile_error())
1147}
1148
1149/// Defines a `#[proc_macro]` callback: the same as the function [`callback()`], in
1150/// the form of a macro.
1151///
1152/// **Kept for compatibility; prefer the function [`callback()`]**, called from a
1153/// `#[proc_macro]` function you write yourself. This macro is a thin wrapper that
1154/// defines that function: the body returns `syn::Result<proc_macro2::TokenStream>`
1155/// and gets the [`Input`] by value, as `input`.
1156///
1157/// ```ignore
1158/// announce::helper::callback! {
1159///     /// Receives trait definitions.
1160///     pub fn my_callback(input) {
1161///         let entries = match input.gather(needed(&input)?) {
1162///             announce::helper::Gather::Ready(e) => e,
1163///             announce::helper::Gather::Fetch(ts) => return Ok(ts),
1164///         };
1165///         generate(entries)
1166///     }
1167/// }
1168/// ```
1169///
1170/// is the same as
1171///
1172/// ```
1173/// # use announce_core as announce;
1174/// # use announce::helper::{Cont, Entry, Input};
1175/// # use proc_macro2 as proc_macro; // a doctest cannot define a `#[proc_macro]`
1176/// # fn needed(_: &Input) -> syn::Result<Vec<Cont>> {
1177/// #     Ok(vec![Cont::bang(syn::parse_quote!(a::Get))])
1178/// # }
1179/// # fn generate(entries: Vec<&Entry>) -> syn::Result<proc_macro::TokenStream> {
1180/// #     let n = entries.len();
1181/// #     Ok(template_quote::quote!(const COUNT: usize = #n;))
1182/// # }
1183/// /// Receives trait definitions.
1184/// pub fn my_callback(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
1185///     announce::helper::callback(ts.into(), |input| {
1186///         let entries = match input.gather(needed(&input)?) {
1187///             announce::helper::Gather::Ready(e) => e,
1188///             announce::helper::Gather::Fetch(ts) => return Ok(ts),
1189///         };
1190///         generate(entries)
1191///     })
1192///     .into()
1193/// }
1194/// #
1195/// # let out = my_callback(syn::parse_quote! { [cb!] a::Get! => [] { trait Get {} } });
1196/// # assert_eq!(out.to_string(), "const COUNT : usize = 1usize ;");
1197/// ```
1198///
1199/// The facade crate must re-export the callback (e.g. under `__private`) so
1200/// that generated code can name it.
1201#[doc(hidden)]
1202#[macro_export]
1203macro_rules! callback {
1204    ($(#[$m:meta])* $vis:vis fn $name:ident ($input:ident) $body:block) => {
1205        $(#[$m])*
1206        #[proc_macro]
1207        $vis fn $name(__announce_ts: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
1208            ::core::convert::Into::into($crate::helper::callback(
1209                ::core::convert::Into::into(__announce_ts),
1210                |$input: $crate::helper::Input|
1211                 -> $crate::helper::__private::syn::Result<$crate::helper::__private::proc_macro2::TokenStream> {
1212                    $body
1213                },
1214            ))
1215        }
1216    };
1217}
1218
1219/// Runs a check: the body of a `#[proc_macro]` that runs where an item is announced,
1220/// and that answers `#[announce]`'s attribute query.
1221///
1222/// - `input` is the input of the proc macro: either the attribute query
1223///   `@__announce_attrs [..] { .. }` or the check's own input, `(ARGS), ITEM`
1224///   ([`CheckCall`]), each after the prefix `@announce_format "1"` that announce writes.
1225/// - `attributes` lists the attribute names that the check owns:
1226///   `#[announce(my_lib::my_check)]` then lets the item carry `#[my_lib]`,
1227///   `#[my_lib(..)]` and `#[my_lib::..]` attributes. It may be empty, for a check that
1228///   owns none: the check still answers the query. See [`attrs`] for the query and
1229///   for what a name matches.
1230/// - `f` is the check itself. It gets the parsed [`CheckInput`] (`args`, `item` with
1231///   the owned attributes, and `owned`, which is `attributes`) and returns the tokens
1232///   to emit, or a `syn::Error`. A check usually returns nothing, or an `Err`.
1233///
1234/// The query is answered with `attributes` ([`Query::answer`]) and `f` is not called.
1235/// For the check's own input, the result is what `f` returns. A `syn::Error` (the
1236/// input does not parse, a name in `attributes` is not an identifier or is reserved
1237/// ([`OwnedAttrs::check_names`]), or `f` fails) becomes a `compile_error!`, like in
1238/// [`callback()`]. So the result can be returned from the proc macro as it is.
1239///
1240/// ```ignore
1241/// /// Rejects `#[my_lib(bad)]` and tuple structs.
1242/// #[proc_macro]
1243/// pub fn my_check(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1244///     announce::helper::check(input.into(), &["my_lib", "other"], |c| {
1245///         // `c` is an `announce::helper::CheckInput`.
1246///         for attr in c.owned_attributes() {
1247///             if attr.path().is_ident("my_lib") && attr.parse_args::<syn::Ident>().map_or(false, |i| i == "bad") {
1248///                 return Err(syn::Error::new_spanned(&attr, "`bad` is not allowed"));
1249///             }
1250///         }
1251///         Ok(proc_macro2::TokenStream::new())
1252///     })
1253///     .into()
1254/// }
1255/// ```
1256///
1257/// The function can be tested with `proc_macro2` streams, because it does not use the
1258/// `proc_macro` crate:
1259///
1260/// ```
1261/// # use announce_core as announce;
1262/// use announce::helper::check;
1263/// use proc_macro2::TokenStream;
1264///
1265/// let run = |input: TokenStream| {
1266///     check(input, &["my_lib"], |c| {
1267///         match &c.item {
1268///             syn::Item::Struct(s) if matches!(s.fields, syn::Fields::Unnamed(_)) => {
1269///                 Err(syn::Error::new_spanned(&s.fields, "tuple structs are not supported"))
1270///             }
1271///             _ => Ok(TokenStream::new()),
1272///         }
1273///     })
1274///     .to_string()
1275/// };
1276/// // The check itself.
1277/// assert_eq!(run(syn::parse_quote! { (), struct S { a: u8 } }), "");
1278/// assert!(run(syn::parse_quote! { (), struct S(u8); }).contains("compile_error"));
1279/// // The query is answered with the names, and the check is not run.
1280/// let out = run(syn::parse_quote! { @__announce_attrs [ me!, next! ] { st } });
1281/// assert!(out.contains("next !") && out.contains("attrs (my_lib)"), "{}", out);
1282/// ```
1283pub fn check(
1284    input: TokenStream,
1285    attributes: &[&str],
1286    f: impl FnOnce(&mut CheckInput) -> Result<TokenStream>,
1287) -> TokenStream {
1288    let run = || -> Result<TokenStream> {
1289        for name in attributes {
1290            if !is_attribute_name(name) {
1291                return Err(syn::Error::new(
1292                    Span::call_site(),
1293                    format!(
1294                        "announce: `{}` is not an identifier: it cannot be an owned attribute name",
1295                        name
1296                    ),
1297                ));
1298            }
1299        }
1300        let owned = OwnedAttrs::from_strs(attributes);
1301        match syn::parse2::<CheckCall>(input)? {
1302            // `answer` reports a reserved name where the check answers.
1303            CheckCall::Query(q) => Ok(q.answer(&owned)),
1304            CheckCall::Run(c) => {
1305                owned.check_names()?;
1306                f(&mut c.with_owned(owned))
1307            }
1308        }
1309    };
1310    run().unwrap_or_else(|e| e.to_compile_error())
1311}
1312
1313/// Whether `name` (or `r#name`) can be turned into an `Ident` without a panic.
1314fn is_attribute_name(name: &str) -> bool {
1315    let name = name.strip_prefix("r#").unwrap_or(name);
1316    let mut chars = name.chars();
1317    matches!(chars.next(), Some(c) if c == '_' || c.is_alphabetic())
1318        && chars.all(|c| c == '_' || c.is_alphanumeric())
1319        && name != "_"
1320}
1321
1322/// Extracts the trampoline payload from the dummy enum that a derive or
1323/// attribute continuation receives. Spans, hygiene and `$crate` survive.
1324pub fn payload(item: TokenStream) -> Option<TokenStream> {
1325    let di: syn::DeriveInput = syn::parse2(item).ok()?;
1326    let e = match di.data {
1327        syn::Data::Enum(e) => e,
1328        _ => return None,
1329    };
1330    let v = e.variants.first()?;
1331    if v.ident != "__AnnouncePayload" || e.variants.len() != 1 {
1332        return None;
1333    }
1334    let f = match &v.discriminant.as_ref()?.1 {
1335        syn::Expr::Field(f) => f,
1336        _ => return None,
1337    };
1338    let t = match &*f.base {
1339        syn::Expr::Tuple(t) => t,
1340        _ => return None,
1341    };
1342    let m = match t.elems.first()? {
1343        syn::Expr::Macro(m) => m,
1344        _ => return None,
1345    };
1346    if !m.mac.path.is_ident("stringify") {
1347        return None;
1348    }
1349    Some(m.mac.tokens.clone())
1350}
1351
1352/// For a derive or attribute whose name clashes with a trampoline: if `item` is a
1353/// trampoline dummy, returns `target! { @announce_format "1" @__announce_self {
1354/// target } @__announce_dollar { $ } payload }`. Otherwise returns `None`.
1355///
1356/// `target` is the absolute path of the `#[announce(macro_name = ..)]` macro. Pick
1357/// a path that is valid at the user's site. The prefixes are for the trampoline (see
1358/// [the wire format](Input#the-wire-format)). `@announce_format "1"` is the version of
1359/// the format; the payload of the dummy starts with it, and it is moved to the front
1360/// of the call. The trampoline needs its own path to call itself again, when it holds
1361/// a large item once, and a real `$` to un-escape an item that has `$` in it.
1362///
1363/// ```ignore
1364/// #[proc_macro_derive(Parse)]
1365/// pub fn derive_parse(input: TokenStream) -> TokenStream {
1366///     if let Some(out) = announce::helper::redirect(input.clone().into(), &syn::parse_quote!(::pars::ParseAnnounce)) {
1367///         return out.into();
1368///     }
1369///     // normal derive
1370/// }
1371/// ```
1372pub fn redirect(item: TokenStream, target: &Path) -> Option<TokenStream> {
1373    // The payload starts with `@announce_format "VERSION"`, written by whoever
1374    // made the dummy. A call starts with it, before the two prefixes below, and
1375    // keeps the version it was made with, so that a trampoline of another version
1376    // reports the mismatch. A payload without it (a hand-made dummy) gets the
1377    // version of this crate.
1378    let (format, body) = split_format(payload(item)?);
1379    let format = format.unwrap_or_else(format_prefix);
1380    // A trampoline whose payload contains `$` needs a real `$` from its
1381    // caller (`@__announce_dollar { $ }`); the others ignore it. A trampoline
1382    // that holds its item once calls itself again and needs its own path
1383    // (`@__announce_self { path }`); the others ignore that too. The derive route cannot re-invoke the
1384    // trampoline itself, so both prefixes are always added here.
1385    let dollar = proc_macro2::Punct::new('$', proc_macro2::Spacing::Alone);
1386    Some(quote! {
1387        #target ! { #format @__announce_self { #target } @__announce_dollar { #dollar } #body }
1388    })
1389}
1390
1391/// What a check receives: `(ARGS), ITEM`, after the prefix `@announce_format "1"` when
1392/// announce calls the check (a call by hand has no prefix, and parses as well).
1393///
1394/// The item has its `#[cfg]` evaluated, and it still has the attributes that the
1395/// checks own (see [`OwnedAttrs`]), so the check can validate them. [`check`] parses
1396/// this and also answers the attribute query. Parsing a `CheckInput` by hand fails
1397/// with an error that says so when `#[announce]` sends the query.
1398pub struct CheckInput {
1399    /// The check's arguments, `(ARGS)`.
1400    pub args: TokenStream,
1401    /// The cfg-evaluated item, with the owned attributes.
1402    pub item: syn::Item,
1403    /// The attribute names that the check declared with `attributes` in
1404    /// [`check`]. Empty when the input was parsed by hand.
1405    pub owned: OwnedAttrs,
1406}
1407
1408impl CheckInput {
1409    /// The same input with the declared attribute names.
1410    pub fn with_owned(mut self, owned: OwnedAttrs) -> Self {
1411        self.owned = owned;
1412        self
1413    }
1414
1415    /// The owned attributes in the item, in source order: what the check
1416    /// validates ([`OwnedAttrs::find`]).
1417    pub fn owned_attributes(&self) -> Vec<Attribute> {
1418        self.owned.find(&self.item)
1419    }
1420}
1421
1422impl Parse for CheckInput {
1423    fn parse(input: ParseStream) -> Result<Self> {
1424        // The call starts with `@announce_format "1"`, unless it was written by hand.
1425        parse_format(input)?;
1426        if input.peek(Token![@]) {
1427            return Err(input.error(
1428                "announce: this check received the attribute query `@__announce_attrs` that \
1429                 `#[announce]` sends first, and does not answer it. Run the check with \
1430                 `announce::helper::check`, or parse `announce::helper::CheckCall` and answer \
1431                 `Query` (a `macro_rules!` check needs one more arm); every check must \
1432                 answer. See the `announce::helper::attrs` docs",
1433            ));
1434        }
1435        let content;
1436        syn::parenthesized!(content in input);
1437        let args = content.parse()?;
1438        input.parse::<Token![,]>()?;
1439        Ok(CheckInput {
1440            args,
1441            item: input.parse()?,
1442            owned: OwnedAttrs::new(),
1443        })
1444    }
1445}
1446
1447/// Best-effort check that `ty` is announced (`ty: Announce`). `krate` is the
1448/// consumer's path to the `announce` crate.
1449pub fn check_type(krate: &Path, ty: &syn::Type) -> TokenStream {
1450    quote! {
1451        const _: () = {
1452            fn __announce_chk<__AnnounceT: ?::core::marker::Sized + #krate::Announce>() {}
1453            let _ = __announce_chk::<#ty>;
1454        };
1455    }
1456}
1457
1458/// Best-effort check that the trait at `path` is announced (`dyn path: Announce`).
1459/// Only meaningful for simple dyn-compatible traits.
1460pub fn check_trait(krate: &Path, path: &Path) -> TokenStream {
1461    check_type(krate, &syn::parse_quote!(dyn #path))
1462}
1463
1464#[cfg(test)]
1465mod tests {
1466    use super::*;
1467
1468    fn s(ts: impl ToTokens) -> String {
1469        ts.to_token_stream()
1470            .to_string()
1471            .split_whitespace()
1472            .collect()
1473    }
1474
1475    #[test]
1476    fn bang_drops_generic_arguments() {
1477        assert_eq!(s(Cont::bang(syn::parse_quote!(a::Enum<A, B>))), "a::Enum!");
1478    }
1479
1480    #[test]
1481    fn input_roundtrip_and_meta() {
1482        let input: Input = syn::parse_quote! {
1483            [cb!, #[derive(p::D)] N]
1484            a::F! => [x::check(1), mock(::core::iter::Iterator)] { pub trait F {} }
1485            #[derive(p::D)] M => [] { pub struct S; }
1486            (rest)
1487        };
1488        assert_eq!(input.list.0.len(), 2);
1489        assert_eq!(input.entries.len(), 2);
1490        assert_eq!(s(&input.entries[0].meta[0]), "x::check(1)");
1491        assert_eq!(
1492            s(input.entries[0].find_meta("check").unwrap().args.clone()),
1493            "1"
1494        );
1495        assert!(input.entries[0].has_check("check"));
1496        assert_eq!(
1497            s(input.entries[0].mock().unwrap()),
1498            "::core::iter::Iterator"
1499        );
1500        assert_eq!(s(input.entries[1].key.path()), "p::D");
1501        assert_eq!(s(&input.rest), "(rest)");
1502        let order: Vec<String> = input.entries_in_order().map(|e| s(e.key.path())).collect();
1503        assert_eq!(order, ["p::D", "a::F"]);
1504        let fwd = input.forward().unwrap().to_string();
1505        assert!(fwd.contains("__AnnouncePayload"), "{}", fwd);
1506    }
1507
1508    #[test]
1509    fn entries_know_the_attributes_their_checks_own() {
1510        let input: Input = syn::parse_quote! {
1511            [cb!]
1512            a::F! => [x::check, attrs(my_lib, other), m::info(1), attrs(other, third)] {
1513                #[my_lib(on_item)]
1514                pub struct F { #[other] pub a: u8, #[third(x)] #[doc = "d"] pub b: u8 }
1515            }
1516            a::G! => [] { pub struct G; }
1517        };
1518        let owned = input.entries[0].owned_attrs();
1519        let names: Vec<String> = owned.names().iter().map(|n| n.to_string()).collect();
1520        assert_eq!(names, ["my_lib", "other", "third"]);
1521        assert_eq!(owned.find(&input.entries[0].item().unwrap()).len(), 3);
1522        let stripped = input.entries[0].item_without_owned_attrs().unwrap();
1523        assert_eq!(s(&stripped), "pubstructF{puba:u8,#[doc=\"d\"]pubb:u8}");
1524        // Without `attrs(..)` there is nothing to strip.
1525        assert!(input.entries[1].owned_attrs().is_empty());
1526        assert_eq!(
1527            s(input.entries[1].item_without_owned_attrs().unwrap()),
1528            "pubstructG;"
1529        );
1530        // `attrs` is found by its exact name, not as the last segment of a path.
1531        let input: Input = syn::parse_quote! { [cb!] a::F! => [lib::attrs(x)] { struct F; } };
1532        assert!(input.entries[0].owned_attrs().is_empty());
1533    }
1534
1535    #[test]
1536    fn check_input_reports_the_query_it_does_not_answer() {
1537        let e = syn::parse2::<CheckInput>(template_quote::quote! {
1538            @__announce_attrs [ c!, f! ] { st }
1539        })
1540        .err()
1541        .unwrap()
1542        .to_string();
1543        assert!(
1544            e.contains("does not answer it") && e.contains("announce::helper::check`"),
1545            "{}",
1546            e
1547        );
1548        let ok: CheckInput = syn::parse_quote! { (x, y), struct S; };
1549        assert!(ok.owned.is_empty() && ok.owned_attributes().is_empty());
1550        let ok = ok.with_owned(OwnedAttrs::from_strs(&["a"]));
1551        assert!(ok.owned.contains("a"));
1552    }
1553
1554    #[test]
1555    fn check_answers_the_query_without_running_the_closure() {
1556        let q = || -> TokenStream {
1557            syn::parse_quote! { @__announce_attrs [ a::check!, b::check!, f::finish! ] { st } }
1558        };
1559        let mut ran = false;
1560        let out = check(q(), &["my_lib", "other"], |_| {
1561            ran = true;
1562            Ok(TokenStream::new())
1563        });
1564        assert!(!ran);
1565        assert_eq!(
1566            s(out),
1567            "b::check!{@announce_format\"1\"@__announce_attrs[b::check!,f::finish!]{stattrs(my_lib,other)}}"
1568        );
1569        // No names: the state is passed on as it is, and the closure is not run.
1570        let out = check(q(), &[], |_| panic!("the query is not a call"));
1571        assert_eq!(
1572            s(out),
1573            "b::check!{@announce_format\"1\"@__announce_attrs[b::check!,f::finish!]{st}}"
1574        );
1575        // Names are written as text: a raw identifier is its plain name.
1576        let out = check(q(), &["r#type", "type"], |_| unreachable!());
1577        assert_eq!(
1578            s(out),
1579            "b::check!{@announce_format\"1\"@__announce_attrs[b::check!,f::finish!]{stattrs(type)}}"
1580        );
1581        // The answer of `Query` is kept as is: a list that does not fit is a compile error.
1582        let out = s(check(
1583            syn::parse_quote! { @__announce_attrs [ a::check! ] { st } },
1584            &[],
1585            |_| unreachable!(),
1586        ));
1587        assert!(out.contains("compile_error") && out.contains("nomacrotocallafterthischeck"));
1588    }
1589
1590    #[test]
1591    fn check_runs_the_closure_with_the_owned_names() {
1592        let mut seen = None;
1593        let out = check(
1594            syn::parse_quote! { (x, y), #[my_lib(a)] struct S { #[other] a: u8, #[kept] b: u8 } },
1595            &["my_lib", "other"],
1596            |c| {
1597                seen = Some((
1598                    s(&c.args),
1599                    s(&c.item),
1600                    c.owned
1601                        .names()
1602                        .iter()
1603                        .map(|n| n.to_string())
1604                        .collect::<Vec<_>>(),
1605                    c.owned_attributes().len(),
1606                ));
1607                Ok(quote! { pub const SEEN: u8 = 1; })
1608            },
1609        );
1610        // Whatever the closure returns is the result.
1611        assert_eq!(s(out), "pubconstSEEN:u8=1;");
1612        let (args, item, owned, found) = seen.unwrap();
1613        assert_eq!(args, "x,y");
1614        assert!(
1615            item.starts_with("#[my_lib(a)]structS{#[other]a:u8"),
1616            "{}",
1617            item
1618        );
1619        assert_eq!(owned, ["my_lib", "other"]);
1620        assert_eq!(found, 2);
1621        // The closure can change the input (`&mut`) and has no names to own.
1622        let out = check(syn::parse_quote! { (), struct S; }, &[], |c| {
1623            assert!(c.owned.is_empty());
1624            c.args = quote! { changed };
1625            Ok(c.args.clone())
1626        });
1627        assert_eq!(s(out), "changed");
1628    }
1629
1630    #[test]
1631    fn check_turns_errors_into_compile_errors() {
1632        // An error of the closure.
1633        let out = s(check(
1634            syn::parse_quote! { (), struct S(u8); },
1635            &[],
1636            |c| match &c.item {
1637                syn::Item::Struct(it) => Err(syn::Error::new_spanned(&it.fields, "no tuples")),
1638                _ => Ok(TokenStream::new()),
1639            },
1640        ));
1641        assert_eq!(out, "::core::compile_error!{\"notuples\"}");
1642        // An input that is neither the query nor `(ARGS), ITEM`.
1643        let mut ran = false;
1644        let out = s(check(syn::parse_quote! { not a check call }, &[], |_| {
1645            ran = true;
1646            Ok(TokenStream::new())
1647        }));
1648        assert!(!ran && out.contains("compile_error"), "{}", out);
1649        // A reserved name, on the query and on the call.
1650        for input in [
1651            syn::parse_quote! { @__announce_attrs [ a!, f::finish! ] { st } },
1652            syn::parse_quote! { (), struct S; },
1653        ] {
1654            let out = s(check(input, &["my_lib", "doc"], |_| unreachable!()));
1655            assert!(
1656                out.contains("compile_error") && out.contains("`doc`cannotbeanownedattributename"),
1657                "{}",
1658                out
1659            );
1660        }
1661        // A name that is not an identifier is an error, not a panic.
1662        for bad in ["", "my-lib", "1st", "_", "a b", "r#"] {
1663            let out = s(check(
1664                syn::parse_quote! { (), struct S; },
1665                &[bad],
1666                |_| unreachable!(),
1667            ));
1668            assert!(
1669                out.contains("compile_error") && out.contains("isnotanidentifier"),
1670                "{:?}: {}",
1671                bad,
1672                out
1673            );
1674        }
1675    }
1676
1677    #[test]
1678    fn fetch_prepends_and_returns() {
1679        let input: Input = syn::parse_quote! { [me!, next!] a::F! => [] { struct S; } (rest) };
1680        let out = s(input.fetch([Cont::bang(syn::parse_quote!(g::G))]));
1681        // The fetch is recorded in the options group.
1682        assert_eq!(
1683            out,
1684            "g::G!{@announce_format\"1\"(__announce_fetched(g::G))[g::G!,me!,next!]a::F!=>[]{structS;}(rest)}"
1685        );
1686        assert_eq!(Cont::bang(syn::parse_quote!(a::F)), input.entries[0].key);
1687        // Nothing to fetch: nothing to record.
1688        let out = s(input.fetch([]));
1689        assert_eq!(
1690            out,
1691            "me!{@announce_format\"1\"[me!,next!]a::F!=>[]{structS;}(rest)}"
1692        );
1693    }
1694
1695    #[test]
1696    fn fetch_of_a_path_that_left_no_entry_is_an_error() {
1697        // The bookkeeping of `gather`, shared. `g::G`
1698        // was fetched, the callback is called again and has no entry for it.
1699        // (`g::G` forwarded to `me` without adding an entry.)
1700        let again: Input = syn::parse_quote! {
1701            (__announce_fetched(g::G)) [me!] a::F! => [] { struct S; }
1702        };
1703        let out = s(again.fetch([Cont::bang(syn::parse_quote!(g::G))]));
1704        assert_eq!(
1705            out,
1706            "compile_error!{\"announce:`g::G!`wasfetchedbutdidnotprovideadefinition;isitan`#[announce]`trampoline?\"}"
1707        );
1708        // The same message as `gather`.
1709        let from_gather = match again.gather([Cont::bang(syn::parse_quote!(g::G))]) {
1710            Gather::Fetch(ts) => s(ts),
1711            Gather::Ready(_) => panic!(),
1712        };
1713        assert_eq!(out, from_gather);
1714        // Generic arguments do not hide the path.
1715        let out = s(again.fetch([Cont::bang(syn::parse_quote!(g::G<u8>))]));
1716        assert!(out.contains("wasfetchedbutdidnotprovide"), "{}", out);
1717        // A path that did answer (an entry is there) may be fetched again, and
1718        // other paths are fetched as usual; the record grows.
1719        let answered: Input = syn::parse_quote! {
1720            (mine, __announce_fetched(g::G)) [me!] g::G! => [] { struct G; }
1721        };
1722        let out = s(answered.fetch([Cont::bang(syn::parse_quote!(g::G))]));
1723        assert!(
1724            out.starts_with("g::G!{@announce_format\"1\"(mine,__announce_fetched(g::G),__announce_fetched(g::G))"),
1725            "{}",
1726            out
1727        );
1728        let out = s(answered.fetch([Cont::bang(syn::parse_quote!(h::H))]));
1729        assert!(
1730            out.starts_with("h::H!{@announce_format\"1\"(mine,__announce_fetched(g::G),__announce_fetched(h::H))"),
1731            "{}",
1732            out
1733        );
1734        // One of several missing paths is enough for the error.
1735        let out = s(again.fetch([
1736            Cont::bang(syn::parse_quote!(h::H)),
1737            Cont::bang(syn::parse_quote!(g::G)),
1738        ]));
1739        assert!(out.starts_with("compile_error!"), "{}", out);
1740    }
1741
1742    #[test]
1743    fn fetch_and_gather_share_the_record() {
1744        let input: Input = syn::parse_quote! { [me!] };
1745        // `gather` fetches `g::G` ...
1746        let first = match input.gather([Cont::bang(syn::parse_quote!(g::G))]) {
1747            Gather::Fetch(ts) => ts,
1748            Gather::Ready(_) => panic!(),
1749        };
1750        assert_eq!(
1751            s(&first),
1752            "g::G!{@announce_format\"1\"(__announce_fetched(g::G))[g::G!,me!]}"
1753        );
1754        // ... and the callback, called again by a non-trampoline, fetches it
1755        // raw: the error is the same whichever one is used.
1756        let body: Input =
1757            syn::parse2(template_quote::quote! { (__announce_fetched(g::G)) [me!] }).unwrap();
1758        assert!(s(body.fetch([Cont::bang(syn::parse_quote!(g::G))]))
1759            .contains("wasfetchedbutdidnotprovide"));
1760        // And the other way round.
1761        let raw = input.fetch([Cont::bang(syn::parse_quote!(g::G))]);
1762        assert_eq!(
1763            s(&raw),
1764            "g::G!{@announce_format\"1\"(__announce_fetched(g::G))[g::G!,me!]}"
1765        );
1766        match body.gather([Cont::bang(syn::parse_quote!(g::G))]) {
1767            Gather::Fetch(ts) => assert!(s(ts).contains("wasfetchedbutdidnotprovide")),
1768            Gather::Ready(_) => panic!(),
1769        }
1770    }
1771
1772    #[test]
1773    fn malformed_entry_is_an_error() {
1774        let r = syn::parse2::<Input>(template_quote::quote! { [cb!] a::F! => { struct S; } });
1775        assert!(r.err().unwrap().to_string().contains("malformed entry"));
1776    }
1777
1778    #[test]
1779    fn invoke_names_attribute_dummies() {
1780        let list: ContList = [
1781            Cont::derive(syn::parse_quote!(p::D)),
1782            Cont::bang(syn::parse_quote!(cb)),
1783        ]
1784        .into_iter()
1785        .collect();
1786        let out = s(list.invoke(TokenStream::new()));
1787        assert!(
1788            out.starts_with(
1789                "#[derive(p::D)]#[allow(dead_code,non_camel_case_types)]enum__AnnounceT_"
1790            ),
1791            "{}",
1792            out
1793        );
1794        let body = payload(list.invoke(TokenStream::new())).map(s).unwrap();
1795        assert!(
1796            body.starts_with("@announce_format\"1\"[#[derive(p::D)]__AnnounceT_"),
1797            "{}",
1798            body
1799        );
1800        assert!(redirect(list.invoke(TokenStream::new()), &syn::parse_quote!(::x::Y)).is_some());
1801    }
1802}
1803
1804#[cfg(test)]
1805mod kv_tests {
1806    use super::*;
1807
1808    #[test]
1809    fn kv_types_lists_flags_and_errors() {
1810        let m: Meta = syn::parse_quote!(lib::info(ty = ::std::collections::HashMap<u8, Vec<u8>>, out = fn() -> u8, xs = [A, b::C], on));
1811        let mut kv = m.kv().unwrap();
1812        let ty: syn::Type = kv.require("ty").unwrap();
1813        assert_eq!(
1814            ty.to_token_stream().to_string().replace(' ', ""),
1815            "::std::collections::HashMap<u8,Vec<u8>>"
1816        );
1817        let out: syn::Type = kv.require("out").unwrap();
1818        assert_eq!(
1819            out.to_token_stream().to_string().replace(' ', ""),
1820            "fn()->u8"
1821        );
1822        let xs: Vec<Path> = kv.list("xs").unwrap().unwrap();
1823        assert_eq!(xs.len(), 2);
1824        assert!(kv.flag("on").unwrap());
1825        assert!(kv.get::<syn::Type>("absent").unwrap().is_none());
1826        kv.finish().unwrap();
1827
1828        let mut kv: KvArgs = syn::parse_quote!(a = 1, b = 2);
1829        let _: syn::LitInt = kv.require("a").unwrap();
1830        assert!(kv
1831            .finish()
1832            .unwrap_err()
1833            .to_string()
1834            .contains("unknown key `b`"));
1835        assert!(syn::parse2::<KvArgs>(template_quote::quote!(a = 1, a = 2)).is_err());
1836        let mut kv: KvArgs = syn::parse_quote!();
1837        assert!(kv
1838            .require::<syn::Type>("x")
1839            .unwrap_err()
1840            .to_string()
1841            .contains("missing `x`"));
1842    }
1843
1844    #[test]
1845    fn stable_referrer_is_deterministic() {
1846        let item: syn::ItemStruct = syn::parse_quote! {
1847            pub struct S { a: Foo, b: Bar, c: Baz, d: Qux, e: Quux, f: Corge }
1848        };
1849        let analyse = || {
1850            let mut l = type_leak::Leaker::new();
1851            let context = l.intern_struct(&item, true).unwrap();
1852            (
1853                crate::helper::leak::StableReferrer::new(l.finish()),
1854                context,
1855            )
1856        };
1857        let order = || {
1858            let (r, _) = analyse();
1859            r.types()
1860                .iter()
1861                .map(|t| t.to_token_stream().to_string())
1862                .collect::<Vec<_>>()
1863        };
1864        let first = order();
1865        assert_eq!(first.len(), 6);
1866        let mut sorted = first.clone();
1867        sorted.sort();
1868        assert_eq!(first, sorted);
1869        for _ in 0..20 {
1870            assert_eq!(order(), first);
1871        }
1872        let (r, context) = analyse();
1873        let mut it: syn::Item = syn::Item::Struct(item.clone());
1874        r.visit_item_mut(context, &mut it, |_, i| {
1875            syn::parse_str(&format!("T{}", i)).unwrap()
1876        });
1877        let s = it.to_token_stream().to_string();
1878        assert!(
1879            s.contains("a : T3") && s.contains("b : T0") && s.contains("f : T2"),
1880            "{}",
1881            s
1882        );
1883        let ty: syn::Type = syn::parse_quote!(Qux);
1884        assert_eq!(r.index_of(&ty), Some(5));
1885        let ty = r.expand(context, ty, |_, i| {
1886            syn::parse_str(&format!("T{}", i)).unwrap()
1887        });
1888        assert_eq!(ty.to_token_stream().to_string(), "T5");
1889    }
1890
1891    #[test]
1892    fn stable_referrer_numbers_required_entries_only() {
1893        // `::other::X` names another crate: an optional entry, left as written.
1894        let item: syn::ItemStruct = syn::parse_quote! {
1895            pub struct S { a: Foo, b: ::other::X, c: Bar, d: ::std::string::String }
1896        };
1897        let mut l = type_leak::Leaker::new();
1898        let context = l.intern_struct(&item, true).unwrap();
1899        let r = crate::helper::leak::StableReferrer::new(l.finish());
1900        let types: Vec<String> = r
1901            .types()
1902            .iter()
1903            .map(|t| t.to_token_stream().to_string())
1904            .collect();
1905        assert_eq!(types, ["Bar", "Foo"]);
1906        let mut it = syn::Item::Struct(item);
1907        r.visit_item_mut(context, &mut it, |_, i| {
1908            syn::parse_str(&format!("T{}", i)).unwrap()
1909        });
1910        let s = it.to_token_stream().to_string();
1911        assert!(
1912            s.contains("a : T1") && s.contains("b : :: other :: X") && s.contains("c : T0"),
1913            "{}",
1914            s
1915        );
1916    }
1917}
1918
1919#[cfg(test)]
1920mod options_tests {
1921    use super::*;
1922
1923    #[test]
1924    fn options_are_parsed_and_forwarded() {
1925        let mut leaker = type_leak::Leaker::new();
1926        let context = leaker
1927            .intern_struct(
1928                &syn::parse_quote!(
1929                    struct F<T> {
1930                        a: Local,
1931                        b: Vec<T>,
1932                    }
1933                ),
1934                false,
1935            )
1936            .unwrap();
1937        let tl = TypeLeak::new(
1938            syn::parse_quote!(::my::A),
1939            syn::parse_quote!(7u64),
1940            leaker.finish(),
1941            context,
1942        );
1943        let meta = tl.meta();
1944        let input: Input = syn::parse_quote! {
1945            (type_leak) [me!, next!]
1946            a::F! => [#meta] { struct F<T>(T); }
1947            (rest)
1948        };
1949        assert!(input.has_option("type_leak"));
1950        let fwd = input.forward().unwrap().to_string().replace(' ', "");
1951        assert!(
1952            fwd.starts_with("next!{@announce_format\"1\"(type_leak)[next!]a::F!=>"),
1953            "{}",
1954            fwd
1955        );
1956        let fetch = input
1957            .fetch([Cont::bang(syn::parse_quote!(g::G))])
1958            .to_string()
1959            .replace(' ', "");
1960        assert!(
1961            fetch.starts_with(
1962                "g::G!{@announce_format\"1\"(type_leak,__announce_fetched(g::G))[g::G!,me!,next!]"
1963            ),
1964            "{}",
1965            fetch
1966        );
1967        let read = input.entries[0].type_leak().unwrap().unwrap();
1968        assert_eq!(read.types().count(), 2);
1969        assert_eq!(read.id.base10_digits(), "7");
1970        assert_eq!(read.referrer, tl.referrer);
1971        let proj = read.projection(1, &syn::parse_quote!((u8,)));
1972        assert_eq!(
1973            proj.to_string().replace(' ', ""),
1974            "<()as::my::A<(u8,),7u64,1usize>>::Type"
1975        );
1976        let plain: Input = syn::parse_quote! { [me!] a::F! => [] { struct F; } };
1977        assert!(!plain.has_option("type_leak"));
1978        assert!(plain.entries[0].type_leak().unwrap().is_none());
1979    }
1980}
1981
1982#[cfg(test)]
1983mod kv_balance_tests {
1984    use super::*;
1985
1986    #[test]
1987    fn unbalanced_angle_is_an_error() {
1988        let e = syn::parse2::<KvArgs>(template_quote::quote!(a = x < y, b = 1)).unwrap_err();
1989        assert!(
1990            e.to_string().contains("unbalanced `<` in the value of `a`"),
1991            "{}",
1992            e
1993        );
1994        assert!(syn::parse2::<KvArgs>(template_quote::quote!(a = 1 << 2, b = 3)).is_err());
1995        let mut kv: KvArgs = syn::parse_quote!(a = (x < y), b = 1, f = fn() -> u8);
1996        assert!(kv.tokens("a").is_some() && kv.tokens("b").is_some() && kv.tokens("f").is_some());
1997        kv.finish().unwrap();
1998    }
1999}
2000
2001#[cfg(test)]
2002mod low_audit_tests {
2003    use super::*;
2004
2005    #[test]
2006    fn cont_equality_ignores_dummy_name_and_generics() {
2007        let a: Cont = syn::parse_quote!(
2008            #[derive(p::D)]
2009            N1
2010        );
2011        let b: Cont = syn::parse_quote!(
2012            #[derive(p::D)]
2013            N2
2014        );
2015        assert_eq!(a, b);
2016        let c = Cont::Bang(syn::parse_quote!(a::E<T>));
2017        assert_eq!(c.to_token_stream().to_string().replace(' ', ""), "a::E!");
2018        let d: Cont = syn::parse_quote!(#[derive(x::A, y::B)]);
2019        assert_eq!(
2020            d.path().to_token_stream().to_string().replace(' ', ""),
2021            "x::A"
2022        );
2023    }
2024
2025    #[test]
2026    fn rest_like_an_entry_without_meta_is_rest() {
2027        let i: Input = syn::parse_quote! { [cb!] foo! => bar };
2028        assert!(i.entries.is_empty());
2029        assert_eq!(i.rest.to_string().replace(' ', ""), "foo!=>bar");
2030    }
2031
2032    #[test]
2033    fn gather_refuses_self_request() {
2034        let i: Input = syn::parse_quote! { [me!] };
2035        match i.gather([Cont::bang(syn::parse_quote!(me))]) {
2036            Gather::Fetch(ts) => assert!(ts.to_string().contains("which is the callback itself")),
2037            Gather::Ready(_) => panic!(),
2038        }
2039    }
2040}
2041
2042#[cfg(test)]
2043mod kv_error_tests {
2044    use super::*;
2045
2046    fn parse(ts: TokenStream) -> Result<KvArgs> {
2047        syn::parse2(ts)
2048    }
2049
2050    #[test]
2051    fn missing_value_after_equals_is_an_error() {
2052        let e = parse(template_quote::quote!(a =)).unwrap_err().to_string();
2053        assert_eq!(e, "missing value for `a`");
2054        let e = parse(template_quote::quote!(a = , b = 1))
2055            .unwrap_err()
2056            .to_string();
2057        assert_eq!(e, "missing value for `a`");
2058        let e = parse(template_quote::quote!(ok = 1, b =))
2059            .unwrap_err()
2060            .to_string();
2061        assert_eq!(e, "missing value for `b`");
2062    }
2063
2064    #[test]
2065    fn get_on_a_flag_key_needs_a_value() {
2066        let mut kv: KvArgs = syn::parse_quote!(f, a = 1);
2067        let e = kv.get::<syn::Type>("f").unwrap_err().to_string();
2068        assert_eq!(e, "`f` needs a value");
2069        // `require` reports the same error, not "missing".
2070        let mut kv: KvArgs = syn::parse_quote!(f);
2071        assert_eq!(
2072            kv.require::<syn::Type>("f").unwrap_err().to_string(),
2073            "`f` needs a value"
2074        );
2075    }
2076
2077    #[test]
2078    fn list_on_a_flag_key_needs_a_bracketed_value() {
2079        let mut kv: KvArgs = syn::parse_quote!(f, a = 1);
2080        let e = kv.list::<syn::Path>("f").unwrap_err().to_string();
2081        assert_eq!(e, "`f` needs a `[..]` value");
2082    }
2083
2084    #[test]
2085    fn flag_on_a_key_with_a_value_takes_no_value() {
2086        let mut kv: KvArgs = syn::parse_quote!(a = 1);
2087        let e = kv.flag("a").unwrap_err().to_string();
2088        assert_eq!(e, "`a` takes no value");
2089        // An absent flag is false and does not consume anything else.
2090        let mut kv: KvArgs = syn::parse_quote!(b = 1);
2091        assert!(!kv.flag("a").unwrap());
2092        assert!(kv.tokens("b").is_some());
2093    }
2094
2095    #[test]
2096    fn list_on_a_non_bracketed_value_is_an_error() {
2097        let mut kv: KvArgs = syn::parse_quote!(a = 1);
2098        let e = kv.list::<syn::LitInt>("a").unwrap_err().to_string();
2099        assert!(e.contains("expected square brackets"), "{}", e);
2100        let mut kv: KvArgs = syn::parse_quote!(a = (x, y));
2101        let e = kv.list::<syn::Path>("a").unwrap_err().to_string();
2102        assert!(e.contains("expected square brackets"), "{}", e);
2103        let mut kv: KvArgs = syn::parse_quote!(a = [1, 2] extra);
2104        assert!(kv.list::<syn::LitInt>("a").is_err());
2105    }
2106
2107    #[test]
2108    fn list_element_and_value_parse_errors_surface() {
2109        let mut kv: KvArgs = syn::parse_quote!(a = [x, 1]);
2110        assert!(kv.list::<syn::Path>("a").is_err());
2111        let mut kv: KvArgs = syn::parse_quote!(a = 1);
2112        assert!(kv.get::<syn::Path>("a").is_err());
2113    }
2114
2115    #[test]
2116    fn list_accepts_empty_and_trailing_comma() {
2117        let mut kv: KvArgs = syn::parse_quote!(a = [], b = [x::Y, z,]);
2118        assert!(kv.list::<Path>("a").unwrap().unwrap().is_empty());
2119        assert_eq!(kv.list::<Path>("b").unwrap().unwrap().len(), 2);
2120        kv.finish().unwrap();
2121    }
2122
2123    #[test]
2124    fn taken_keys_are_gone_and_finish_names_the_first_leftover() {
2125        let mut kv: KvArgs = syn::parse_quote!(a = 1, b, c = 2);
2126        assert!(kv.tokens("a").is_some());
2127        assert!(kv.tokens("a").is_none());
2128        assert_eq!(kv.finish().unwrap_err().to_string(), "unknown key `b`");
2129    }
2130
2131    #[test]
2132    fn duplicate_keys_and_keywords() {
2133        let e = parse(template_quote::quote!(a, a)).unwrap_err().to_string();
2134        assert_eq!(e, "duplicate key `a`");
2135        // Keywords are fine as keys.
2136        let mut kv: KvArgs = syn::parse_quote!(type = u8, crate = a::b, fn);
2137        assert!(kv.get::<syn::Type>("type").unwrap().is_some());
2138        assert!(kv.get::<Path>("crate").unwrap().is_some());
2139        assert!(kv.flag("fn").unwrap());
2140        kv.finish().unwrap();
2141    }
2142
2143    #[test]
2144    fn values_run_to_the_next_top_level_comma() {
2145        // `1 b = 2` is all one value; two flags need a comma between them.
2146        let mut kv = parse(template_quote::quote!(a = 1 b = 2)).unwrap();
2147        assert_eq!(kv.tokens("a").unwrap().to_string().replace(' ', ""), "1b=2");
2148        assert!(parse(template_quote::quote!(a b)).is_err());
2149    }
2150
2151    #[test]
2152    fn equality_comparison_is_a_flag_followed_by_junk() {
2153        // `a == b` is not `a = (= b)`: `==` ends neither value nor key.
2154        assert!(parse(template_quote::quote!(a == b)).is_err());
2155    }
2156}
2157
2158#[cfg(test)]
2159mod gap_tests {
2160    use super::*;
2161    use proc_macro2::{Punct, Spacing};
2162
2163    fn s(ts: impl ToTokens) -> String {
2164        ts.to_token_stream()
2165            .to_string()
2166            .split_whitespace()
2167            .collect()
2168    }
2169
2170    fn mk_dollar() -> Punct {
2171        Punct::new('$', Spacing::Alone)
2172    }
2173
2174    #[test]
2175    fn check_trait_asserts_the_dyn_trait_is_announced() {
2176        let out = s(check_trait(
2177            &syn::parse_quote!(::announce),
2178            &syn::parse_quote!(my::Tr),
2179        ));
2180        assert!(out.contains("dynmy::Tr"), "{}", out);
2181        assert!(out.contains("::announce::Announce"), "{}", out);
2182        assert!(out.contains("?::core::marker::Sized"), "{}", out);
2183        assert!(out.starts_with("const_:()="), "{}", out);
2184        // `check_trait` is `check_type` of the `dyn` type.
2185        let direct = s(check_type(
2186            &syn::parse_quote!(::announce),
2187            &syn::parse_quote!(dyn my::Tr),
2188        ));
2189        assert_eq!(out, direct);
2190    }
2191
2192    #[test]
2193    fn check_type_uses_the_given_krate_path() {
2194        let out = s(check_type(
2195            &syn::parse_quote!(::renamed),
2196            &syn::parse_quote!(Vec<u8>),
2197        ));
2198        assert!(out.contains("::renamed::Announce"), "{}", out);
2199        assert!(out.contains("__announce_chk::<Vec<u8>>"), "{}", out);
2200    }
2201
2202    #[test]
2203    fn meta_name_values_parse_expression_values() {
2204        let m: Meta = syn::parse_quote!(lib::info(a = 1, b = "x", c = 1 + 2));
2205        let nv = m.name_values().unwrap();
2206        assert_eq!(nv.len(), 3);
2207        assert_eq!(s(&nv[0].path), "a");
2208        assert_eq!(s(&nv[1].value), "\"x\"");
2209        assert_eq!(s(&nv[2].value), "1+2");
2210        // No args: empty list.
2211        let m: Meta = syn::parse_quote!(lib::info);
2212        assert!(m.name_values().unwrap().is_empty());
2213        // A flag, or a non-expression value, is an error; `kv` accepts them.
2214        let m: Meta = syn::parse_quote!(lib::info(a = 1, on));
2215        assert!(m.name_values().is_err());
2216        let m: Meta = syn::parse_quote!(lib::info(a = 1, on));
2217        assert!(m.kv().is_ok());
2218        // Array expressions are expressions; types are not.
2219        let m: Meta = syn::parse_quote!(lib::info(xs = [A, B]));
2220        assert_eq!(m.name_values().unwrap().len(), 1);
2221        let m: Meta = syn::parse_quote!(lib::info(ty = Vec<u8, A>));
2222        assert!(m.name_values().is_err());
2223        assert!(m.kv().is_ok());
2224    }
2225
2226    #[test]
2227    fn meta_args_default_to_empty() {
2228        let m: Meta = syn::parse_quote!(lib::info);
2229        assert!(m.args.is_none());
2230        assert!(m.kv().unwrap().finish().is_ok());
2231        assert_eq!(s(&m), "lib::info");
2232        let m: Meta = syn::parse_quote!(lib::info(1, 2));
2233        assert_eq!(s(&m), "lib::info(1,2)");
2234    }
2235
2236    fn hash_of<T: std::hash::Hash>(t: &T) -> u64 {
2237        use std::hash::Hasher;
2238        let mut h = std::collections::hash_map::DefaultHasher::new();
2239        t.hash(&mut h);
2240        h.finish()
2241    }
2242
2243    #[test]
2244    fn cont_hash_agrees_with_eq() {
2245        let a: Cont = syn::parse_quote!(
2246            #[derive(p::D)]
2247            N1
2248        );
2249        let b: Cont = syn::parse_quote!(
2250            #[derive(p::D)]
2251            N2
2252        );
2253        let c: Cont = syn::parse_quote!(#[derive(p::D)]);
2254        assert_eq!(a, b);
2255        assert_eq!(hash_of(&a), hash_of(&b));
2256        assert_eq!(hash_of(&a), hash_of(&c));
2257        let bang = Cont::bang(syn::parse_quote!(p::D));
2258        let bang_generic = Cont::Bang(syn::parse_quote!(p::D<T>));
2259        assert_eq!(bang, bang_generic);
2260        assert_eq!(hash_of(&bang), hash_of(&bang_generic));
2261        // Different continuations differ, and a derive is not the bang of the same path.
2262        let other: Cont = syn::parse_quote!(
2263            #[derive(p::E)]
2264            N1
2265        );
2266        assert_ne!(a, other);
2267        assert_ne!(a, bang);
2268        let set: std::collections::HashSet<Cont> = [a.clone(), b, c, other].into_iter().collect();
2269        assert_eq!(set.len(), 2);
2270        assert!(set.contains(&a));
2271    }
2272
2273    #[test]
2274    fn cont_path_of_attribute_and_derive() {
2275        let c: Cont = syn::parse_quote!(
2276            #[my::attr(x, y)]
2277            N
2278        );
2279        assert_eq!(s(c.path()), "my::attr");
2280        let d = Cont::derive(syn::parse_quote!(::p::D));
2281        assert_eq!(s(d.path()), "::p::D");
2282        assert_eq!(s(&d), "#[derive(::p::D)]");
2283        // A `derive` without a usable path falls back to the attribute path.
2284        let e: Cont = syn::parse_quote!(
2285            #[derive()]
2286            N
2287        );
2288        assert_eq!(s(e.path()), "derive");
2289    }
2290
2291    #[test]
2292    fn input_skips_a_leading_dollar_prefix() {
2293        let dollar = mk_dollar();
2294        let ts = template_quote::quote! {
2295            @ __announce_dollar { #dollar }
2296            (opt) [cb!, n!]
2297            a::F! => [x::check(1)] { struct S; }
2298            (rest)
2299        };
2300        let input: Input = syn::parse2(ts).unwrap();
2301        assert_eq!(s(input.options.as_ref().unwrap()), "opt");
2302        assert!(input.has_option("opt"));
2303        assert_eq!(input.list.0.len(), 2);
2304        assert_eq!(s(&input.list), "[cb!,n!]");
2305        assert_eq!(input.entries.len(), 1);
2306        assert_eq!(s(input.entries[0].key.path()), "a::F");
2307        assert_eq!(s(&input.entries[0].meta[0]), "x::check(1)");
2308        assert_eq!(s(&input.rest), "(rest)");
2309        // The prefix is dropped on `forward` and `fetch`.
2310        let out = s(input.forward().unwrap());
2311        assert!(!out.contains("__announce_dollar"), "{}", out);
2312    }
2313
2314    #[test]
2315    fn input_dollar_prefix_without_options() {
2316        let dollar = mk_dollar();
2317        let ts = template_quote::quote! { @ __announce_dollar { #dollar } [cb!] };
2318        let input: Input = syn::parse2(ts).unwrap();
2319        assert!(input.options.is_none());
2320        assert_eq!(input.list.0.len(), 1);
2321        assert!(input.entries.is_empty());
2322        assert!(input.rest.is_empty());
2323    }
2324
2325    #[test]
2326    fn input_skips_the_self_and_dollar_prefixes() {
2327        let dollar = mk_dollar();
2328        let ts = template_quote::quote! {
2329            @ __announce_self { ::a::F }
2330            @ __announce_dollar { #dollar }
2331            [cb!, n!] REST
2332        };
2333        let input: Input = syn::parse2(ts).unwrap();
2334        assert!(input.options.is_none());
2335        assert_eq!(s(&input.list), "[cb!,n!]");
2336        assert_eq!(s(&input.rest), "REST");
2337        // Either one alone, and the self prefix before the options group.
2338        let ts = template_quote::quote! { @ __announce_self { F } (o) [cb!] };
2339        let input: Input = syn::parse2(ts).unwrap();
2340        assert!(input.has_option("o"));
2341        assert_eq!(input.list.0.len(), 1);
2342        // The prefixes are dropped on `forward`.
2343        let ts = template_quote::quote! { @ __announce_self { F } [cb!, n!] };
2344        let out = s(syn::parse2::<Input>(ts).unwrap().forward().unwrap());
2345        assert!(!out.contains("__announce_self"), "{}", out);
2346    }
2347
2348    #[test]
2349    fn input_rejects_other_prefixes() {
2350        assert!(syn::parse2::<Input>(template_quote::quote! { @ other { x } [cb!] }).is_err());
2351        assert!(syn::parse2::<Input>(template_quote::quote! { @ [cb!] }).is_err());
2352        // The prefix alone is not an input.
2353        let dollar = mk_dollar();
2354        let ts = template_quote::quote! { @ __announce_dollar { #dollar } };
2355        assert!(syn::parse2::<Input>(ts).is_err());
2356    }
2357
2358    #[test]
2359    fn invoke_with_an_empty_list_is_a_compile_error() {
2360        let out = s(ContList::default().invoke(template_quote::quote!(x y)));
2361        assert_eq!(out, "compile_error!{\"announce:emptycontinuationlist\"}");
2362        let opts = template_quote::quote!(expr);
2363        let out = s(ContList::default().invoke_with_options(Some(&opts), TokenStream::new()));
2364        assert!(out.contains("emptycontinuationlist"), "{}", out);
2365    }
2366
2367    #[test]
2368    fn invoke_in_expression_position_refuses_the_derive_route() {
2369        let list: ContList = [
2370            Cont::derive(syn::parse_quote!(p::D)),
2371            Cont::bang(syn::parse_quote!(cb)),
2372        ]
2373        .into_iter()
2374        .collect();
2375        let opts = template_quote::quote!(expr);
2376        let out = s(list.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2377        assert!(out.starts_with("compile_error!{"), "{}", out);
2378        assert!(out.contains("cannotbeusedwith`(expr)`"), "{}", out);
2379        assert!(!out.contains("__AnnouncePayload"), "{}", out);
2380        // The same with `expr` leading other options.
2381        let opts = template_quote::quote!(expr, type_leak);
2382        let out = s(list.invoke_with_options(Some(&opts), TokenStream::new()));
2383        assert!(out.contains("cannotbeusedwith`(expr)`"), "{}", out);
2384    }
2385
2386    #[test]
2387    fn only_a_leading_expr_selects_expression_position() {
2388        // The rule of the trampolines, shared.
2389        let yes = |ts: TokenStream| is_expression_position(&ts);
2390        assert!(yes(template_quote::quote!(expr)));
2391        assert!(yes(template_quote::quote!(expr, type_leak)));
2392        assert!(yes(template_quote::quote!(expr __announce_fetched(a))));
2393        assert!(!yes(template_quote::quote!(type_leak, expr)));
2394        assert!(!yes(template_quote::quote!(__announce_fetched(expr))));
2395        assert!(!yes(template_quote::quote!((expr))));
2396        assert!(!yes(template_quote::quote!(expression)));
2397        assert!(!yes(TokenStream::new()));
2398        // A derive continuation is refused only for a leading `expr`; with `expr`
2399        // later the options are a namespace, and the dummy enum is emitted.
2400        let list: ContList = [
2401            Cont::derive(syn::parse_quote!(p::D)),
2402            Cont::bang(syn::parse_quote!(cb)),
2403        ]
2404        .into_iter()
2405        .collect();
2406        let opts = template_quote::quote!(type_leak, expr);
2407        let out = s(list.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2408        assert!(out.contains("__AnnouncePayload"), "{}", out);
2409        assert!(!out.contains("cannotbeusedwith"), "{}", out);
2410        let opts = template_quote::quote!(__announce_fetched(expr));
2411        let out = s(list.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2412        assert!(out.contains("__AnnouncePayload"), "{}", out);
2413        // A bang head forwards the group as it is, whatever it holds.
2414        let bang: ContList = [Cont::bang(syn::parse_quote!(p::cb))].into_iter().collect();
2415        let opts = template_quote::quote!(type_leak, expr);
2416        let out = s(bang.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2417        assert_eq!(
2418            out,
2419            "p::cb!{@announce_format\"1\"(type_leak,expr)[p::cb!]body}"
2420        );
2421    }
2422
2423    #[test]
2424    fn expression_option_is_fine_for_a_bang_head_and_other_options_for_derives() {
2425        let bang: ContList = [Cont::bang(syn::parse_quote!(p::cb))].into_iter().collect();
2426        let opts = template_quote::quote!(expr);
2427        let out = s(bang.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2428        assert_eq!(out, "p::cb!{@announce_format\"1\"(expr)[p::cb!]body}");
2429        let derive: ContList = [Cont::derive(syn::parse_quote!(p::D))]
2430            .into_iter()
2431            .collect();
2432        let opts = template_quote::quote!(type_leak);
2433        let out = s(derive.invoke_with_options(Some(&opts), template_quote::quote!(body)));
2434        assert!(out.contains("__AnnouncePayload"), "{}", out);
2435        assert!(
2436            out.contains("stringify!{@announce_format\"1\"(type_leak)[#[derive(p::D)]__AnnounceT_"),
2437            "{}",
2438            out
2439        );
2440    }
2441
2442    #[test]
2443    fn name_dummies_keeps_existing_names_and_gives_distinct_new_ones() {
2444        let mut list: ContList = [
2445            Cont::derive(syn::parse_quote!(p::A)),
2446            Cont::derive(syn::parse_quote!(p::B)),
2447            syn::parse_quote!(
2448                #[derive(p::C)]
2449                Fixed
2450            ),
2451            Cont::bang(syn::parse_quote!(cb)),
2452        ]
2453        .into_iter()
2454        .collect();
2455        list.name_dummies();
2456        let names: Vec<String> = list
2457            .0
2458            .iter()
2459            .filter_map(|c| match c {
2460                Cont::Attr { name: Some(n), .. } => Some(n.to_string()),
2461                _ => None,
2462            })
2463            .collect();
2464        assert_eq!(names.len(), 3);
2465        assert_eq!(names[2], "Fixed");
2466        assert!(names[0].starts_with("__AnnounceT_") && names[1].starts_with("__AnnounceT_"));
2467        assert_ne!(names[0], names[1]);
2468        // `name_dummies` is idempotent.
2469        let before = s(&list);
2470        list.name_dummies();
2471        assert_eq!(s(&list), before);
2472    }
2473
2474    #[test]
2475    fn redirect_ignores_ordinary_items() {
2476        let target: Path = syn::parse_quote!(::x::Y);
2477        for item in [
2478            template_quote::quote! { struct S; },
2479            template_quote::quote! { enum E { A, B } },
2480            template_quote::quote! { enum E { __AnnouncePayload } },
2481            template_quote::quote! { enum E { __AnnouncePayload = 1 } },
2482            template_quote::quote! { enum E { __AnnouncePayload = (1, 0).1 } },
2483            template_quote::quote! { enum E { __AnnouncePayload = (other!{ x }, 0).1 } },
2484            template_quote::quote! { enum E { __AnnouncePayload = (stringify!{ x }, 0).1, B } },
2485            template_quote::quote! { enum E { Other = (stringify!{ x }, 0).1 } },
2486            template_quote::quote! { fn f() {} },
2487        ] {
2488            assert!(payload(item.clone()).is_none(), "{}", item);
2489            assert!(redirect(item.clone(), &target).is_none(), "{}", item);
2490        }
2491    }
2492
2493    #[test]
2494    fn redirect_wraps_the_payload_with_the_dollar_prefix() {
2495        let list: ContList = [
2496            Cont::derive(syn::parse_quote!(p::D)),
2497            Cont::bang(syn::parse_quote!(cb)),
2498        ]
2499        .into_iter()
2500        .collect();
2501        let dummy = list.invoke(template_quote::quote!(body));
2502        let out = redirect(dummy.clone(), &syn::parse_quote!(::x::Y)).unwrap();
2503        let out_s = s(&out);
2504        assert!(
2505            out_s.starts_with(
2506                "::x::Y!{@announce_format\"1\"@__announce_self{::x::Y}@__announce_dollar{$}[#[derive(p::D)]__AnnounceT_"
2507            ),
2508            "{}",
2509            out_s
2510        );
2511        assert!(out_s.ends_with("cb!]body}"), "{}", out_s);
2512        // The prefix of the payload is moved to the front, not copied.
2513        assert_eq!(out_s.matches("@announce_format").count(), 1, "{}", out_s);
2514        // The `$` is a real punct, not a string.
2515        let has_dollar = out.into_iter().any(|tt| match tt {
2516            proc_macro2::TokenTree::Group(g) => g
2517                .stream()
2518                .into_iter()
2519                .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() == '$')))),
2520            _ => false,
2521        });
2522        assert!(has_dollar);
2523        // What `redirect` produces parses as an `Input` (the prefix is skipped).
2524        let input: Input = syn::parse2(
2525            match redirect(dummy, &syn::parse_quote!(::x::Y))
2526                .unwrap()
2527                .into_iter()
2528                .last()
2529            {
2530                Some(proc_macro2::TokenTree::Group(g)) => g.stream(),
2531                other => panic!("{:?}", other),
2532            },
2533        )
2534        .unwrap();
2535        assert_eq!(input.list.0.len(), 2);
2536        assert_eq!(s(input.rest), "body");
2537    }
2538}
2539
2540/// The format prefix `@announce_format "1"` of every call.
2541#[cfg(test)]
2542mod format_tests {
2543    use super::*;
2544    use proc_macro2::{Punct, Spacing};
2545
2546    fn s(ts: impl ToTokens) -> String {
2547        ts.to_token_stream()
2548            .to_string()
2549            .split_whitespace()
2550            .collect()
2551    }
2552
2553    /// What every call of announce starts with, without whitespace.
2554    const PREFIX: &str = "@announce_format\"1\"";
2555
2556    #[test]
2557    fn the_version_is_written_once_and_the_prefix_is_made_of_it() {
2558        assert_eq!(ANNOUNCE_FORMAT, "1");
2559        assert_eq!(s(format_prefix()), PREFIX);
2560        assert_eq!(
2561            s(format_prefix()),
2562            format!("@announce_format{:?}", ANNOUNCE_FORMAT)
2563        );
2564    }
2565
2566    #[test]
2567    fn input_reads_a_call_with_the_prefix_and_one_without() {
2568        let with: Input = syn::parse_quote! { @announce_format "1" (expr) [cb!] REST };
2569        assert_eq!(with.format.as_deref(), Some(ANNOUNCE_FORMAT));
2570        assert!(with.has_option("expr"));
2571        assert_eq!(s(&with.rest), "REST");
2572        let without: Input = syn::parse_quote! { (expr) [cb!] REST };
2573        assert_eq!(without.format, None);
2574        assert_eq!(s(&without.rest), "REST");
2575        // The rest of the input is read the same way.
2576        assert_eq!(s(&with.list), s(&without.list));
2577        // The prefix is first; the prefixes of the trampolines follow it.
2578        let all: Input = syn::parse_quote! {
2579            @announce_format "1" @__announce_self { a::b } @__announce_dollar { $ } (x) [cb!] REST
2580        };
2581        assert_eq!(all.format.as_deref(), Some("1"));
2582        assert!(all.has_option("x"));
2583        assert_eq!(s(&all.rest), "REST");
2584        // Entries and a REST that looks like an entry do not hide the prefix.
2585        let e: Input = syn::parse_quote! {
2586            @announce_format "1" [cb!] a::F! => [] { struct F; } b::G! => [] { struct G; }
2587        };
2588        assert_eq!(e.entries.len(), 2);
2589    }
2590
2591    #[test]
2592    fn the_prefix_after_the_other_prefixes_is_not_a_prefix() {
2593        let late = syn::parse2::<Input>(template_quote::quote! {
2594            @__announce_self { a::b } @announce_format "1" [cb!]
2595        });
2596        assert!(late.is_err());
2597        // Two of them are not one either.
2598        let twice = syn::parse2::<Input>(template_quote::quote! {
2599            @announce_format "1" @announce_format "1" [cb!]
2600        });
2601        assert!(twice.is_err());
2602    }
2603
2604    #[test]
2605    fn another_version_is_one_error_that_names_both() {
2606        let e = syn::parse2::<Input>(template_quote::quote! { @announce_format "2" [cb!] })
2607            .unwrap_err()
2608            .to_string();
2609        assert_eq!(
2610            e.matches("announce: incompatible wire format").count(),
2611            1,
2612            "{}",
2613            e
2614        );
2615        assert!(
2616            e.contains("found `@announce_format \"2\"`")
2617                && e.contains("expected `@announce_format \"1\"`"),
2618            "{}",
2619            e
2620        );
2621        // The same error from every parser.
2622        let query = template_quote::quote! { @announce_format "7" @__announce_attrs [ f! ] { } };
2623        let call = template_quote::quote! { @announce_format "7" (), struct S; };
2624        let errors = vec![
2625            syn::parse2::<CheckCall>(query.clone()).err().unwrap(),
2626            syn::parse2::<CheckCall>(call.clone()).err().unwrap(),
2627            syn::parse2::<Query>(query).err().unwrap(),
2628            syn::parse2::<CheckInput>(call).err().unwrap(),
2629        ];
2630        for e in errors {
2631            let e = e.to_string();
2632            assert!(
2633                e.starts_with("announce: incompatible wire format")
2634                    && e.contains("found `@announce_format \"7\"`")
2635                    && e.contains("expected `@announce_format \"1\"`"),
2636                "{}",
2637                e
2638            );
2639        }
2640    }
2641
2642    #[test]
2643    fn a_prefix_without_a_string_is_an_error() {
2644        for bad in [
2645            template_quote::quote! { @announce_format [cb!] },
2646            template_quote::quote! { @announce_format 1 [cb!] },
2647            template_quote::quote! { @announce_format },
2648        ] {
2649            let e = syn::parse2::<Input>(bad).unwrap_err().to_string();
2650            assert!(e.contains("followed by a string literal"), "{}", e);
2651        }
2652        // `@` and another word is not the prefix: the input is read as it is.
2653        let e = syn::parse2::<Input>(template_quote::quote! { @other [cb!] })
2654            .unwrap_err()
2655            .to_string();
2656        assert!(!e.contains("wire format"), "{}", e);
2657    }
2658
2659    #[test]
2660    fn checks_take_a_call_with_the_prefix_and_one_without() {
2661        for tokens in [
2662            template_quote::quote! { @announce_format "1" (a, b), struct S; },
2663            template_quote::quote! { (a, b), struct S; },
2664        ] {
2665            let i: CheckInput = syn::parse2(tokens.clone()).unwrap();
2666            assert_eq!(s(&i.args), "a,b");
2667            match syn::parse2::<CheckCall>(tokens).unwrap() {
2668                CheckCall::Run(i) => assert_eq!(s(&i.args), "a,b"),
2669                CheckCall::Query(_) => panic!("the check itself"),
2670            }
2671        }
2672        for tokens in [
2673            template_quote::quote! { @announce_format "1" @__announce_attrs [ me!, f! ] { st } },
2674            template_quote::quote! { @__announce_attrs [ me!, f! ] { st } },
2675        ] {
2676            let q: Query = syn::parse2(tokens.clone()).unwrap();
2677            assert_eq!(q.list.0.len(), 2);
2678            assert!(matches!(
2679                syn::parse2::<CheckCall>(tokens).unwrap(),
2680                CheckCall::Query(_)
2681            ));
2682            // The answer has the prefix, whatever the query had.
2683            assert!(
2684                s(q.answer(&OwnedAttrs::new())).starts_with(&format!("f!{{{}", PREFIX)),
2685                "{}",
2686                s(q.answer(&OwnedAttrs::new()))
2687            );
2688        }
2689        // `check` runs the closure for both forms of the check's own input.
2690        for tokens in [
2691            template_quote::quote! { @announce_format "1" (), struct S; },
2692            template_quote::quote! { (), struct S; },
2693        ] {
2694            let mut ran = false;
2695            let out = check(tokens, &[], |_| {
2696                ran = true;
2697                Ok(TokenStream::new())
2698            });
2699            assert!(ran && out.is_empty());
2700        }
2701        // The query of the other version is a `compile_error!`, and the check is not run.
2702        let out = s(check(
2703            template_quote::quote! { @announce_format "3" @__announce_attrs [ me!, f! ] { st } },
2704            &[],
2705            |_| unreachable!(),
2706        ));
2707        assert!(
2708            out.starts_with("::core::compile_error!") && out.contains("incompatiblewireformat"),
2709            "{}",
2710            out
2711        );
2712    }
2713
2714    #[test]
2715    fn every_call_that_a_helper_makes_starts_with_the_prefix() {
2716        let starts = |ts: TokenStream| {
2717            let out = s(&ts);
2718            // `path!{` + prefix, or the prefix in the payload of a dummy.
2719            assert!(
2720                out.contains(&format!("!{{{}", PREFIX))
2721                    || out.contains(&format!("stringify!{{{}", PREFIX)),
2722                "{}",
2723                out
2724            );
2725            // And it is first: nothing but the macro path comes before it.
2726            let at = out.find(PREFIX).unwrap();
2727            assert!(
2728                out[..at].ends_with("!{") || out[..at].ends_with("stringify!{"),
2729                "{}",
2730                out
2731            );
2732            assert_eq!(out.matches(PREFIX).count(), 1, "{}", out);
2733        };
2734        let input: Input = syn::parse_quote! { [me!, next!] a::F! => [] { struct F; } (rest) };
2735        // `forward`, `fetch` and `gather`.
2736        starts(input.forward().unwrap());
2737        starts(input.fetch([Cont::bang(syn::parse_quote!(g::G))]));
2738        match input.gather([Cont::bang(syn::parse_quote!(g::G))]) {
2739            Gather::Fetch(ts) => starts(ts),
2740            Gather::Ready(_) => panic!("g::G is not there"),
2741        }
2742        // `forward_transformed`.
2743        starts(input.forward_transformed(input.entries[0].clone()).unwrap());
2744        // `ContList::invoke` and `invoke_with_options`, for a macro head and for a
2745        // derive or attribute head (the prefix is first in the payload of the dummy).
2746        let bang: ContList = [Cont::bang(syn::parse_quote!(p::cb))].into_iter().collect();
2747        starts(bang.invoke(TokenStream::new()));
2748        starts(bang.invoke_with_options(Some(&template_quote::quote!(expr)), TokenStream::new()));
2749        let derive: ContList = [
2750            Cont::derive(syn::parse_quote!(p::D)),
2751            Cont::bang(syn::parse_quote!(cb)),
2752        ]
2753        .into_iter()
2754        .collect();
2755        let dummy = derive.invoke(template_quote::quote!(body));
2756        starts(dummy.clone());
2757        assert!(s(payload(dummy.clone()).unwrap()).starts_with(&format!("{}[", PREFIX)));
2758        // `redirect`: the prefix is first, before the two prefixes of the trampoline.
2759        let out = redirect(dummy, &syn::parse_quote!(::x::Y)).unwrap();
2760        starts(out.clone());
2761        assert!(s(out).starts_with(&format!(
2762            "::x::Y!{{{}@__announce_self{{::x::Y}}@__announce_dollar{{$}}",
2763            PREFIX
2764        )));
2765        // The query of a check, and the answer of `check`.
2766        let q: Query = syn::parse_quote! { @__announce_attrs [ me!, f! ] { st } };
2767        starts(q.answer(&OwnedAttrs::new()));
2768        starts(check(
2769            template_quote::quote! { @__announce_attrs [ me!, f! ] { st } },
2770            &["a"],
2771            |_| unreachable!(),
2772        ));
2773    }
2774
2775    #[test]
2776    fn the_callback_and_transformer_functions_take_and_write_the_prefix() {
2777        let call = |prefix: TokenStream| {
2778            template_quote::quote! { #prefix [me!, next!] a::S! => [] { struct S; } NAME }
2779        };
2780        let with = call(format_prefix());
2781        let without = call(TokenStream::new());
2782        // `callback` reads a call with the prefix and one without.
2783        for input in [with.clone(), without.clone()] {
2784            let out = callback(input, |i| {
2785                let name: syn::Ident = i.rest_as()?;
2786                Ok(template_quote::quote! { const #name: usize = 1; })
2787            });
2788            assert_eq!(s(out), "constNAME:usize=1;");
2789        }
2790        // What it forwards has the prefix, whatever it got.
2791        for input in [with.clone(), without.clone()] {
2792            let out = s(callback(input, |i| Ok(i.forward().unwrap())));
2793            assert!(out.starts_with(&format!("next!{{{}", PREFIX)), "{}", out);
2794        }
2795        // `transformer` forwards the rewritten entry with the prefix, too.
2796        for input in [with.clone(), without] {
2797            let out = s(transformer(input, |mut entry| {
2798                entry.meta.push(syn::parse_quote!(seen));
2799                Ok(entry)
2800            }));
2801            assert!(
2802                out.starts_with(&format!("next!{{{}[next!]a::S!=>[seen]", PREFIX)),
2803                "{}",
2804                out
2805            );
2806        }
2807        // Another version is one error from both, naming both versions.
2808        let other = template_quote::quote! { @announce_format "5" [me!, next!] a::S! => [] { struct S; } NAME };
2809        for out in [
2810            s(callback(other.clone(), |_| unreachable!())),
2811            s(transformer(other, |_| unreachable!())),
2812        ] {
2813            assert!(
2814                out.starts_with("::core::compile_error!")
2815                    && out.contains(
2816                        "found`@announce_format\\\"5\\\"`,expected`@announce_format\\\"1\\\"`"
2817                    ),
2818                "{}",
2819                out
2820            );
2821        }
2822    }
2823
2824    #[test]
2825    fn a_call_without_the_prefix_is_never_checked_for_a_version() {
2826        // Nothing given, nothing validated: every parser of announce reads the call as it
2827        // is, with no version and no error about one. (A given version that is not ours is
2828        // the error: `another_version_is_one_error_that_names_both`.)
2829        let no_error = |what: &str, out: &str| {
2830            assert!(
2831                !out.contains("wire format") && !out.contains("compile_error"),
2832                "{}: {}",
2833                what,
2834                out
2835            );
2836        };
2837        let dollar = Punct::new('$', Spacing::Alone);
2838        // `Input`: with the options, the prefixes of a trampoline, and an entry.
2839        let calls = [
2840            template_quote::quote! { [cb!] },
2841            template_quote::quote! { (expr) [cb!] a::F! => [] { struct F; } REST },
2842            template_quote::quote! { @__announce_dollar { #dollar } [cb!] REST },
2843            template_quote::quote! {
2844                @__announce_self { ::a::F } @__announce_dollar { #dollar } (o) [cb!] REST
2845            },
2846        ];
2847        for call in calls {
2848            let input: Input = syn::parse2(call.clone()).unwrap();
2849            assert_eq!(input.format, None, "{}", s(&call));
2850            // What it fetches has the prefix of this version.
2851            let fetched = s(input.fetch([Cont::bang(syn::parse_quote!(g::G))]));
2852            assert!(fetched.contains(&format!("!{{{}", PREFIX)), "{}", fetched);
2853            // And the callback function reads it.
2854            let out = s(callback(call.clone(), |_| Ok(TokenStream::new())));
2855            assert_eq!(out, "", "{}", s(&call));
2856        }
2857        // `Query` and `CheckCall`, `CheckInput`, `check`.
2858        let query = template_quote::quote! { @__announce_attrs [ me!, f! ] { st } };
2859        let own = template_quote::quote! { (a), struct S; };
2860        let q: Query = syn::parse2(query.clone()).unwrap();
2861        no_error(
2862            "Query::answer",
2863            &s(q.answer(&OwnedAttrs::from_strs(&["x"]))),
2864        );
2865        assert!(matches!(
2866            syn::parse2::<CheckCall>(query.clone()).unwrap(),
2867            CheckCall::Query(_)
2868        ));
2869        assert!(matches!(
2870            syn::parse2::<CheckCall>(own.clone()).unwrap(),
2871            CheckCall::Run(_)
2872        ));
2873        syn::parse2::<CheckInput>(own.clone()).unwrap();
2874        let answered = s(check(query, &["x"], |_| unreachable!()));
2875        no_error("check (query)", &answered);
2876        assert!(
2877            answered.starts_with(&format!("f!{{{}", PREFIX)),
2878            "{}",
2879            answered
2880        );
2881        let mut ran = false;
2882        let out = check(own, &["x"], |_| {
2883            ran = true;
2884            Ok(TokenStream::new())
2885        });
2886        assert!(ran && out.is_empty());
2887        // `callback` and `transformer` on a call by hand: no error, whatever they emit.
2888        let call = template_quote::quote! { [me!, next!] a::S! => [] { struct S; } NAME };
2889        no_error(
2890            "callback",
2891            &s(callback(call.clone(), |i| Ok(i.forward().unwrap()))),
2892        );
2893        no_error("transformer", &s(transformer(call, Ok)));
2894        // `redirect` on a dummy whose payload has no prefix: it writes ours.
2895        let dummy = template_quote::quote! {
2896            #[derive(p::D)]
2897            enum N { __AnnouncePayload = (stringify!{ [cb!] rest }, 0).1 }
2898        };
2899        let out = redirect(dummy, &syn::parse_quote!(::x::Y)).unwrap();
2900        no_error("redirect", &s(&out));
2901        assert!(s(out).starts_with(&format!("::x::Y!{{{}", PREFIX)));
2902    }
2903
2904    #[test]
2905    fn check_answers_a_query_with_the_prefix_and_without_it() {
2906        let answer = |tokens: TokenStream| s(check(tokens, &["my_lib"], |_| unreachable!()));
2907        let with = answer(template_quote::quote! {
2908            @announce_format "1" @__announce_attrs [ me!, f! ] { st }
2909        });
2910        let without = answer(template_quote::quote! { @__announce_attrs [ me!, f! ] { st } });
2911        assert_eq!(with, without);
2912        assert_eq!(
2913            with,
2914            format!("f!{{{}@__announce_attrs[f!]{{stattrs(my_lib)}}}}", PREFIX)
2915        );
2916    }
2917
2918    #[test]
2919    fn redirect_moves_the_prefix_of_the_payload_to_the_front_and_keeps_its_version() {
2920        let dollar = Punct::new('$', Spacing::Alone);
2921        let dummy = |payload: TokenStream| {
2922            template_quote::quote! {
2923                #[derive(p::D)]
2924                enum N { __AnnouncePayload = (stringify!{ #payload }, 0).1 }
2925            }
2926        };
2927        let target: Path = syn::parse_quote!(::x::Y);
2928        // A payload with the prefix: moved, not copied.
2929        let out = redirect(
2930            dummy(template_quote::quote!(@announce_format "1" [cb!] rest)),
2931            &target,
2932        )
2933        .unwrap();
2934        assert_eq!(
2935            s(out),
2936            format!(
2937                "::x::Y!{{{}@__announce_self{{::x::Y}}@__announce_dollar{{{}}}[cb!]rest}}",
2938                PREFIX,
2939                dollar.as_char()
2940            )
2941        );
2942        // The version of the maker is kept, so that the trampoline reports the mismatch.
2943        let out = redirect(
2944            dummy(template_quote::quote!(@announce_format "9" [cb!] rest)),
2945            &target,
2946        )
2947        .unwrap();
2948        assert!(s(out).starts_with("::x::Y!{@announce_format\"9\"@__announce_self"));
2949        // A dummy that was made by hand, without the prefix, gets this version.
2950        let out = redirect(dummy(template_quote::quote!([cb!] rest)), &target).unwrap();
2951        assert!(s(out).starts_with(&format!("::x::Y!{{{}@__announce_self", PREFIX)));
2952        // A payload that is shorter than the prefix is left alone.
2953        let out = redirect(dummy(template_quote::quote!(@announce_format)), &target).unwrap();
2954        assert!(s(out).ends_with("@__announce_dollar{$}@announce_format}"));
2955    }
2956}