Skip to main content

announce_core/
lib.rs

1//! The implementation behind `#[announce]`, as plain functions, and the helpers for
2//! callbacks and checks. Do not depend on this
3//! crate: go through [`announce`](https://docs.rs/announce), which re-exports
4//! the public API as `announce::machinery` and `announce::helper`.
5//!
6//! A proc-macro crate cannot export functions, so the code behind the macros
7//! lives here, in an ordinary library. `announce-macros` is a thin wrapper
8//! around it. The crate name and layout are not a stable interface.
9//!
10//! The API of the generation is [`announce()`] and [`Options`] (`announce::machinery`). See their docs, and
11//! `announce::machinery` for how a library uses them. [`helper`] is what
12//! callbacks and checks use (`announce::helper`): the parser of the tokens a
13//! trampoline sends, and the functions [`helper::callback()`] and
14//! [`helper::transformer()`].
15
16mod cratepath;
17mod fastpath;
18mod finish;
19mod generate;
20pub mod helper;
21mod leak;
22mod options;
23mod runner;
24mod scan;
25mod timing;
26
27use crate::helper::OwnedAttrs;
28use proc_macro2::{Ident, Span, TokenStream};
29use std::sync::atomic::{AtomicU64, Ordering};
30use template_quote::{quote, ToTokens};
31
32pub use finish::{finish, try_finish};
33pub use generate::SINGLE_COPY_THRESHOLD;
34pub use options::{Check, Layout, Options};
35
36fn random_u64() -> u64 {
37    use std::hash::{BuildHasher, Hasher};
38    static COUNTER: AtomicU64 = AtomicU64::new(0);
39    let mut h = std::collections::hash_map::RandomState::new().build_hasher();
40    h.write_u64(COUNTER.fetch_add(1, Ordering::Relaxed));
41    h.finish()
42}
43
44/// Does what `#[announce(attr)]` does to `item`.
45///
46/// `attr` holds the tokens inside the parentheses of the attribute
47/// (`macro_name = Name, my_lib::check`), and `item` the item the attribute is
48/// on. The result replaces the item. It keeps the item as written (unless
49/// `emit_item = false` or `mock`) and adds the code that emits the
50/// trampoline `macro_rules!` and the other generated items. The item that the
51/// trampoline holds, and the checks get, has its `#[doc = ..]` attributes
52/// removed unless `docs = true`; the emitted item keeps them.
53///
54/// With checks (`my_lib::check`) the result is not the item yet: it is the
55/// call of the first check, with the query that asks every check which
56/// attributes it owns. The last element of the query is the hidden macro
57/// `finish!` of the `announce` crate (`<crate path>::__private::finish!`, see
58/// [`finish()`]), which expands to what is described above. See [`helper::attrs`]
59/// for the protocol.
60///
61/// A returned error is a syntax or type-context error of the same kind as
62/// `#[announce]` reports. Here it comes back as a `compile_error!` invocation
63/// in the returned tokens, so a proc macro can return the result as it is.
64/// Use [`try_announce`] to get the [`syn::Error`] instead.
65///
66/// The generated code names the `announce` crate. See [`Options::krate()`] and
67/// the `crate = ..` option.
68///
69/// If the item has other outer attributes, the result is not the trampoline yet: it is the
70/// item with the hidden attribute macro `#[<crate path>::__private::__announce_runner(..)]`
71/// (the *runner*) as its last attribute, which holds the options and generates the
72/// trampoline when it expands, after every other attribute macro on the item (and drops
73/// the item, with `mock` or `emit_item = false`), and, after the item, a *sentinel* that
74/// fails at the definition if the runner never runs (a macro below that drops the attributes
75/// of the item). If the item has the runner already (another announcement was expanded
76/// first), the options are merged into it, with no sentinel. An item with no other attribute
77/// that could expand (documentation and `repr` cannot) is announced at once, with no runner
78/// and no sentinel. See [Several announcements on one
79/// item](https://docs.rs/announce/latest/announce/attr.announce.html#several-announcements-on-one-item).
80pub fn announce(attr: TokenStream, item: TokenStream) -> TokenStream {
81    try_announce(attr, item).unwrap_or_else(|e| e.to_compile_error())
82}
83
84/// [`announce()`] with the error returned as a [`syn::Error`].
85///
86/// The item is not parsed here: only its kind and name are read (`scan`), to
87/// refuse what is not supported at once. Its syntax is validated once, by the parse of
88/// the `Capture` derive that the runner (or this attribute, when it announces at once)
89/// emits.
90pub fn try_announce(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
91    let mut t = timing::Timer::new("attribute");
92    let opts: Options = syn::parse2(attr)?;
93    t.step("options");
94    let head = scan::head(&item)?;
95    t.subject(|| format!("{:?} {}", head.kind, head.ident));
96    t.step("scan");
97    let attr = opts.to_token_stream();
98    let random = random_u64();
99    // The query of the checks, which ends with the announcement (`finish`), or the
100    // announcement.
101    let out = if opts.checks.is_empty() {
102        announce_now(&opts, &attr, item, random, &OwnedAttrs::new(), &head)
103    } else {
104        finish::query(&opts, &attr, &item, random, &opts.checks)
105    };
106    t.step("emit");
107    Ok(out)
108}
109
110/// The announcement, once the attribute names that its checks own are known (`owned`:
111/// empty without checks): the trampoline at once if nothing can expand after it, or the
112/// item with the runner (see `runner`). That is so for `mock` and `emit_item = false`
113/// as well: the runner drops the item.
114pub(crate) fn announce_now(
115    opts: &Options,
116    attr: &TokenStream,
117    item: TokenStream,
118    random: u64,
119    owned: &OwnedAttrs,
120    head: &scan::Head,
121) -> TokenStream {
122    // No other attribute that could expand (documentation, `repr`, .. and the attributes
123    // that the checks own do not): there is nothing to wait for.
124    if !runner::can_expand_after(&item, owned) {
125        return capture_output(opts, attr, item, random, owned, None);
126    }
127    runner::announce(opts, head, item, owned)
128}
129
130/// What an announcement expands to when it is made (see `runner`), once the attribute
131/// names that its checks own are known (`owned`; empty without checks): the item and the
132/// capture dummy.
133///
134/// The derive sees the copy with every nested cfg
135/// evaluated in this crate, then `remove` deletes the dummy. Without owned
136/// attributes the item is emitted here, as written. With owned attributes it
137/// is the derive that emits it, from its cfg-evaluated copy and without them.
138///
139/// `sentinel` is the kind of sentinel that the announcement emitted next to the item, if
140/// it did: the derive then emits a stand-in for what the sentinel needs if it fails, so
141/// that the error it reports is not followed by the one of the sentinel.
142pub(crate) fn capture_output(
143    opts: &Options,
144    attr: &TokenStream,
145    item: TokenStream,
146    random: u64,
147    owned: &OwnedAttrs,
148    sentinel: Option<runner::Sentinel>,
149) -> TokenStream {
150    let krate = opts.krate();
151    let cap = Ident::new(
152        &format!("__AnnounceCap_{}", generate::hex(random)),
153        Span::call_site(),
154    );
155    let random_lit = syn::LitInt::new(&format!("{}u64", random), Span::call_site());
156    let emit_item = opts.mock.is_none() && opts.emit_item && owned.is_empty();
157    let names = owned.names();
158    let owned_arg = (!owned.is_empty()).then(|| quote! { [ #(#names),* ] ; });
159    let sentinel_arg = sentinel.map(|s| {
160        let word = Ident::new(s.capture_word(), Span::call_site());
161        quote! { #word ; }
162    });
163    quote! {
164        #(if emit_item) { #item }
165        #[derive(#krate::__private::Capture)]
166        #[__announce(#random_lit; #sentinel_arg #owned_arg #attr)]
167        #[#krate::__private::remove]
168        #[allow(dead_code, non_camel_case_types)]
169        enum #cap { V = { #item 0 } }
170    }
171}
172
173/// What the runner `#[<crate path>::__private::__announce_runner(args)]` does to
174/// the item that follows it. Not part of the API: it is the body of the hidden attribute
175/// macro that `#[announce]` puts last among the attributes of an item. See the docs of
176/// `announce::announce` ("Several announcements on one item").
177#[doc(hidden)]
178pub fn run_announce(args: TokenStream, item: TokenStream) -> TokenStream {
179    runner::run(args, item)
180}
181
182/// Does what `define_repeater!($vis $ident)` does: the definition of the
183/// repeater trait, as type-leak writes it (`type_leak::repeater_definition`).
184///
185/// ```
186/// let out = announce_core::define_repeater("pub Repeater".parse().unwrap());
187/// let out = out.to_string();
188/// assert!(out.contains("pub trait Repeater"), "{}", out);
189/// ```
190///
191/// `$vis` may be empty (a private trait), `pub`, `pub(crate)`, .. The output
192/// is the trait, whose const parameters with defaults need Rust 1.59 in the
193/// crate that expands it. An error comes back as a `compile_error!`
194/// invocation; see [`try_define_repeater`] for a [`syn::Error`].
195pub fn define_repeater(input: TokenStream) -> TokenStream {
196    try_define_repeater(input).unwrap_or_else(|e| e.to_compile_error())
197}
198
199/// [`define_repeater()`] with the error returned as a [`syn::Error`].
200pub fn try_define_repeater(input: TokenStream) -> syn::Result<TokenStream> {
201    use syn::parse::Parser;
202    const USAGE: &str = "announce: expected `define_repeater!(<visibility> <Name>)`, \
203                         e.g. `define_repeater!(pub Repeater)`";
204    let (vis, ident) = (|input: syn::parse::ParseStream| {
205        let vis: syn::Visibility = input.parse()?;
206        let ident: Ident = input
207            .parse()
208            .map_err(|e: syn::Error| syn::Error::new(e.span(), USAGE))?;
209        if !input.is_empty() {
210            return Err(input.error(USAGE));
211        }
212        Ok((vis, ident))
213    })
214    .parse2(input)?;
215    let mut definition = type_leak::repeater_definition(&vis, &ident);
216    definition.attrs.push(syn::parse_quote! {
217        #[doc = "The repeater of `#[announce(repeater = ..)]`: it carries the types of \
218                 announced items, which do not resolve in other crates, to the macros \
219                 that copy them. Defined by `announce::define_repeater!`; do not \
220                 implement it or write its paths yourself."]
221    });
222    Ok(quote! { #definition })
223}
224
225/// What the `Capture` derive reads from its input, the capture dummy:
226/// the arguments of `#[__announce(..)]` and the tokens of the item, if it is
227/// still there (it is gone when it was `#[cfg]`'d out).
228struct Dummy {
229    args: options::CaptureArgs,
230    item: Option<TokenStream>,
231}
232
233/// Reads the dummy `#[__announce(ARGS)] .. enum Cap { V = { ITEM 0 } }` from its
234/// top-level tokens, without parsing it. `None` if it has another shape.
235fn scan_dummy(input: TokenStream) -> Option<(TokenStream, Option<TokenStream>)> {
236    use proc_macro2::{Delimiter, TokenTree};
237    let mut it = input.into_iter().peekable();
238    let mut args = None;
239    while matches!(it.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '#') {
240        it.next();
241        let group = match it.next()? {
242            TokenTree::Group(g) if g.delimiter() == Delimiter::Bracket => g,
243            _ => return None,
244        };
245        let mut inner = group.stream().into_iter();
246        if matches!(inner.next(), Some(TokenTree::Ident(i)) if i == "__announce") {
247            match inner.next() {
248                Some(TokenTree::Group(a))
249                    if a.delimiter() == Delimiter::Parenthesis && inner.next().is_none() =>
250                {
251                    args = Some(a.stream())
252                }
253                _ => return None,
254            }
255        }
256    }
257    if !matches!(it.next()?, TokenTree::Ident(i) if i == "enum") {
258        return None;
259    }
260    if !matches!(it.next()?, TokenTree::Ident(_)) {
261        return None;
262    }
263    let body = match it.next()? {
264        TokenTree::Group(g) if g.delimiter() == Delimiter::Brace => g,
265        _ => return None,
266    };
267    if it.next().is_some() {
268        return None;
269    }
270    let mut variant = body.stream().into_iter();
271    if !matches!(variant.next()?, TokenTree::Ident(_)) {
272        return None;
273    }
274    if !matches!(variant.next()?, TokenTree::Punct(p) if p.as_char() == '=') {
275        return None;
276    }
277    let block = match variant.next()? {
278        TokenTree::Group(g) if g.delimiter() == Delimiter::Brace => g,
279        _ => return None,
280    };
281    if variant.next().is_some() {
282        return None;
283    }
284    let mut tts: Vec<TokenTree> = block.stream().into_iter().collect();
285    match tts.pop()? {
286        TokenTree::Literal(l) if l.to_string() == "0" => {}
287        _ => return None,
288    }
289    let item = if tts.is_empty() {
290        None
291    } else {
292        Some(tts.into_iter().collect())
293    };
294    Some((args?, item))
295}
296
297/// The same with `syn`, for a dummy of another shape than [`scan_dummy`] knows.
298fn read_dummy_with_syn(input: TokenStream) -> syn::Result<Dummy> {
299    let di: syn::DeriveInput = syn::parse2(input)?;
300    let attr = di
301        .attrs
302        .iter()
303        .find(|a| a.path().is_ident("__announce"))
304        .ok_or_else(|| {
305            syn::Error::new_spanned(
306                &di.ident,
307                "announce: missing #[__announce] on capture dummy",
308            )
309        })?;
310    let args: options::CaptureArgs = attr.parse_args()?;
311    let expr = match &di.data {
312        syn::Data::Enum(e) => e
313            .variants
314            .first()
315            .and_then(|v| v.discriminant.as_ref())
316            .map(|d| &d.1),
317        _ => None,
318    };
319    let stmts = match expr {
320        Some(syn::Expr::Block(b)) => &b.block.stmts,
321        _ => {
322            return Err(syn::Error::new_spanned(
323                &di.ident,
324                "announce: malformed capture dummy",
325            ))
326        }
327    };
328    // The copy is gone when the item itself was `#[cfg]`'d out.
329    let item = stmts.iter().find_map(|s| match s {
330        syn::Stmt::Item(i) => Some(i.to_token_stream()),
331        _ => None,
332    });
333    Ok(Dummy { args, item })
334}
335
336fn read_dummy(input: TokenStream) -> syn::Result<Dummy> {
337    match scan_dummy(input.clone()) {
338        Some((args, item)) => Ok(Dummy {
339            args: syn::parse2(args)?,
340            item,
341        }),
342        None => read_dummy_with_syn(input),
343    }
344}
345
346/// The body of the hidden `Capture` derive that `#[announce]` expands to.
347/// Not part of the API.
348///
349/// The item is parsed here, once: everything else is done
350/// on that tree and on the tokens it prints.
351#[doc(hidden)]
352pub fn capture(input: TokenStream) -> syn::Result<TokenStream> {
353    let mut t = timing::Timer::new("capture");
354    let dummy = read_dummy(input)?;
355    t.step("read");
356    let tokens = match dummy.item {
357        Some(tokens) => tokens,
358        None => return Ok(TokenStream::new()),
359    };
360    let args = dummy.args;
361    let item: syn::Item = match syn::parse2(tokens.clone()) {
362        Ok(item) => item,
363        // The announcement has a sentinel: what it needs stands in for the trampoline.
364        Err(e) => match (args.sentinel, scan::head(&tokens)) {
365            (Some(sentinel), Ok(head)) => {
366                let mut out = e.to_compile_error();
367                out.extend(generate::sentinel_stub(
368                    sentinel,
369                    &head.ident,
370                    &args.opts,
371                    args.random,
372                ));
373                return Ok(out);
374            }
375            _ => return Err(e),
376        },
377    };
378    let ident = generate::ident_vis(&item).ok().map(|(i, _)| i);
379    t.subject(|| ident.as_ref().map_or_else(String::new, |i| i.to_string()));
380    t.step("parse");
381    let prep = generate::prepare(item, Some(tokens), &args.opts, &args.owned);
382    t.step("strip");
383    Ok(
384        match generate::emit(&prep, &args.opts, args.random, &args.owned, &mut t) {
385            Ok(generated) => generated,
386            Err(e) => {
387                let mut out = e.to_compile_error();
388                out.extend(generate::real_item(&prep, &args.opts, &args.owned));
389                if let (Some(sentinel), Some(ident)) = (args.sentinel, &ident) {
390                    out.extend(generate::sentinel_stub(
391                        sentinel,
392                        ident,
393                        &args.opts,
394                        args.random,
395                    ));
396                }
397                out
398            }
399        },
400    )
401}
402
403#[cfg(test)]
404mod tests {
405    use super::*;
406    use template_quote::ToTokens;
407
408    fn norm(ts: &TokenStream) -> String {
409        ts.to_string().split_whitespace().collect()
410    }
411
412    /// The sentinel of a struct `S` without generics (in the crate `::l::announce`), as
413    /// printed: it stands after the item.
414    const CHK: &str = "#[allow(deprecated)]const_:()={trait__AnnounceSentinel{}\
415                       impl__AnnounceSentinelforSwhereS:::l::announce::Announce{}};";
416
417    fn ts(s: &str) -> TokenStream {
418        s.parse().unwrap()
419    }
420
421    #[test]
422    fn options_round_trip() {
423        let src = "crate = ::l::announce, macro_name = N, mock = a::B, repeater = crate::R, \
424                   allow_primitive = false, allow_std_prelude = true, emit_item = false, \
425                   layout = single, docs = true, meta = m::info(1, 2), meta = m::bare, \
426                   c::check(x), c::bare";
427        let opts: Options = syn::parse2(ts(src)).unwrap();
428        let printed = opts.to_token_stream();
429        let again: Options = syn::parse2(printed.clone()).unwrap();
430        assert_eq!(norm(&printed), norm(&again.to_token_stream()));
431        assert_eq!(
432            norm(&printed),
433            "crate=::l::announce,macro_name=N,mock=a::B,repeater=crate::R,\
434             allow_primitive=false,allow_std_prelude=true,emit_item=false,layout=single,docs=true,\
435             meta=m::info(1,2),meta=m::bare,c::check(x),c::bare"
436        );
437        for (src, want) in [("layout = auto", ""), ("layout = direct", "layout=direct")] {
438            let o: Options = syn::parse2(ts(src)).unwrap();
439            assert_eq!(norm(&o.to_token_stream()), want);
440        }
441        let e = syn::parse2::<Options>(ts("layout = fast")).err().unwrap();
442        assert!(
443            e.to_string().contains("`auto`, `single` or `direct`"),
444            "{}",
445            e
446        );
447    }
448
449    #[test]
450    fn define_repeater_is_type_leaks_definition() {
451        // The tokens are `type_leak::repeater_definition()`'s, with the docs on top.
452        for (input, vis) in [
453            ("pub Repeater", "pub"),
454            ("pub(crate) Repeater", "pub(crate)"),
455            ("Repeater", ""),
456        ] {
457            let out = try_define_repeater(ts(input)).unwrap();
458            let def = type_leak::repeater_definition(
459                &syn::parse_str::<syn::Visibility>(vis).unwrap(),
460                &Ident::new("Repeater", Span::call_site()),
461            );
462            let expected = norm(&def.to_token_stream());
463            let got = norm(&out);
464            assert!(got.ends_with(&expected), "{}\n{}", got, expected);
465            assert!(got.starts_with("#[doc="), "{}", got);
466        }
467    }
468
469    #[test]
470    fn define_repeater_reports_a_bad_input() {
471        for bad in ["", "pub", "pub Repeater extra", "pub 1", "Rep, pub"] {
472            let e = try_define_repeater(ts(bad)).err();
473            assert!(e.is_some(), "{:?} was accepted", bad);
474        }
475        let out = norm(&define_repeater(ts("pub")));
476        assert!(
477            out.starts_with("::core::compile_error!")
478                && out.contains("expected`define_repeater!(<visibility><Name>)`"),
479            "{}",
480            out
481        );
482    }
483
484    #[test]
485    fn default_options_print_nothing() {
486        assert!(Options::default().to_token_stream().is_empty());
487    }
488
489    #[test]
490    fn announce_names_the_given_crate() {
491        // An item with no other attribute is announced at once: the item and the capture
492        // derive, which names the given crate.
493        for item in ["pub struct S { pub a: u8 }", "pub trait T { fn f(&self); }"] {
494            let out = norm(&announce(ts("crate = ::l::announce"), ts(item)));
495            assert!(out.starts_with(&norm(&ts(item))), "{}", out);
496            assert!(
497                out.contains("derive(::l::announce::__private::Capture)"),
498                "{}",
499                out
500            );
501            assert!(
502                out.contains("#[::l::announce::__private::remove]"),
503                "{}",
504                out
505            );
506            assert!(!out.contains("__announce_runner"), "{}", out);
507        }
508        // An item with another attribute has the runner as its last attribute, in the
509        // given crate, and nothing else.
510        let out = norm(&announce(
511            ts("crate = ::l::announce"),
512            ts("#[derive(Debug)] pub struct S { pub a: u8 }"),
513        ));
514        assert_eq!(
515            out,
516            [
517                "#[derive(Debug)]#[::l::announce::__private::__announce_runner(__announce_format_version=\"1\",__announce_sentinel=\"impl\",crate=::l::announce)]pubstructS{puba:u8}",
518                CHK
519            ]
520            .concat()
521        );
522    }
523
524    #[test]
525    fn announce_through_options_equals_the_attribute() {
526        let mut opts: Options = syn::parse2(ts("macro_name = N")).unwrap();
527        opts.krate = Some(syn::parse_quote!(::l::announce));
528        let item = ts("pub struct S;");
529        let a = norm(&announce(opts.to_token_stream(), item.clone()));
530        let b = norm(&announce(ts("crate = ::l::announce, macro_name = N"), item));
531        // Only the random numbers differ; the attribute arguments are the same.
532        let args = |s: &str| {
533            s.split("u64;")
534                .nth(1)
535                .unwrap()
536                .split(")]")
537                .next()
538                .unwrap()
539                .to_string()
540        };
541        assert_eq!(args(&a), "crate=::l::announce,macro_name=N");
542        assert_eq!(args(&a), args(&b));
543    }
544
545    #[test]
546    fn errors_are_compile_error_tokens_or_syn_errors() {
547        let bad = ts("fn f() {}");
548        let out = announce(TokenStream::new(), bad.clone());
549        assert!(out.to_string().contains("compile_error"), "{}", out);
550        let e = try_announce(TokenStream::new(), bad).err().unwrap();
551        assert!(e.to_string().contains("supports struct, enum"), "{}", e);
552        let e = try_announce(ts("nope = 1"), ts("struct S;")).err().unwrap();
553        assert!(e.to_string().contains("unknown announce option"), "{}", e);
554    }
555
556    #[test]
557    fn layout_is_an_option_of_the_attribute() {
558        let item = "pub struct S { pub a: u8 }";
559        // The capture derive reads it, and makes the trampoline of that layout.
560        let captured = |opts: &str| {
561            norm(
562                &capture(derive_input(
563                    &format!("7u64; crate = ::l::announce{}", opts),
564                    item,
565                ))
566                .unwrap(),
567            )
568        };
569        assert!(!captured("").contains("__announce_emit"));
570        assert!(!captured(", layout = direct").contains("__announce_emit"));
571        assert!(captured(", layout = single").contains("__announce_emit"));
572        // The attribute hands the option to the capture derive, and to the runner.
573        let out = norm(&announce(ts("layout = single"), ts(item)));
574        assert!(out.contains("#[__announce("), "{}", out);
575        assert!(out.contains("layout=single"), "{}", out);
576        let out = norm(&announce(
577            ts("layout = single"),
578            ts(&format!("#[x] {}", item)),
579        ));
580        assert!(
581            out.contains(
582                "__announce_runner(__announce_format_version=\"1\",__announce_sentinel=\"impl\",layout=single)"
583            ),
584            "{}",
585            out
586        );
587    }
588
589    /// The state group of a query for `S`, as `query` writes it, to feed `finish`.
590    fn finish_input(attr: &str, item: &str, answers: &str) -> TokenStream {
591        ts(&format!(
592            "@__announce_attrs [ ::l::announce::__private::finish! ] {{ [7u64; attr ({}) {{ {} }}] {} }}",
593            attr, item, answers
594        ))
595    }
596
597    #[test]
598    fn checks_send_the_query_before_anything_is_emitted() {
599        let out = norm(&announce(
600            ts("crate = ::l::announce, a::check, b::check(x), meta = m::info"),
601            ts("pub struct S { pub a: u8 }"),
602        ));
603        // The first check is called, and heads the list: all the checks and
604        // `finish!` follow, like a continuation list.
605        assert!(
606            out.starts_with(
607                "a::check!{@announce_format\"1\"@__announce_attrs[a::check!,b::check!,::l::announce::__private::finish!]{[",
608            ),
609            "{}",
610            out
611        );
612        // The state: a random number, the mode, the attribute and the item.
613        assert!(
614            out.contains(";attr(crate=::l::announce,meta=m::info,a::check,b::check(x)){pubstructS{puba:u8}}]}}"),
615            "{}",
616            out
617        );
618        // Nothing else yet: no item, no runner, no capture dummy.
619        assert!(
620            !out.contains("Capture")
621                && !out.contains("__announce_runner")
622                && out.matches("pubstructS").count() == 1,
623            "{}",
624            out
625        );
626        // Without checks there is no query.
627        let out = norm(&announce(
628            ts("crate = ::l::announce, meta = m::info"),
629            ts("pub struct S;"),
630        ));
631        assert!(
632            !out.contains("__announce_attrs") && out.contains("Capture"),
633            "{}",
634            out
635        );
636    }
637
638    #[test]
639    fn finish_without_owned_attributes_emits_what_announce_emits_without_checks() {
640        let item = "pub struct S { pub a: u8 }";
641        // No other attribute: the item and the capture dummy, as without checks.
642        let out = norm(&finish(finish_input(
643            "crate=::l::announce,a::check",
644            item,
645            "",
646        )));
647        assert!(
648            out.starts_with("pubstructS{puba:u8}#[derive(::l::announce::__private::Capture)]"),
649            "{}",
650            out
651        );
652        assert!(
653            out.contains("#[__announce(7u64;crate=::l::announce,a::check)]"),
654            "{}",
655            out
656        );
657        // Another attribute: the runner, which carries the options.
658        let out = norm(&finish(finish_input(
659            "crate=::l::announce,a::check",
660            &format!("#[x] {}", item),
661            "",
662        )));
663        assert_eq!(
664            out,
665            [
666                "#[x]#[::l::announce::__private::__announce_runner(__announce_format_version=\"1\",__announce_sentinel=\"impl\",crate=::l::announce,a::check)]pubstructS{puba:u8}",
667                CHK
668            ]
669            .concat()
670        );
671    }
672
673    #[test]
674    fn finish_with_owned_attributes_leaves_the_item_to_the_capture_derive() {
675        let item = "pub struct S { #[my_lib(skip)] pub a: u8 }";
676        let out = norm(&finish(finish_input(
677            "crate=::l::announce,a::check",
678            item,
679            "attrs(my_lib) attrs(other, my_lib)",
680        )));
681        // The item is not emitted here (it would have an attribute no macro knows),
682        // only in the dummy that the derive reads, with the owned names in front
683        // of the options.
684        assert!(
685            out.starts_with("#[derive(::l::announce::__private::Capture)]"),
686            "{}",
687            out
688        );
689        assert!(
690            out.contains("#[__announce(7u64;[my_lib,other];crate=::l::announce,a::check)]"),
691            "{}",
692            out
693        );
694        assert_eq!(out.matches("#[my_lib(skip)]").count(), 1, "{}", out);
695        // With another attribute there is the runner, which has the names. The attribute
696        // that is owned follows it.
697        let out = norm(&finish(finish_input(
698            "crate=::l::announce,a::check",
699            &format!("#[my_lib(on_item)] #[x] {}", item),
700            "attrs(my_lib) attrs(other, my_lib)",
701        )));
702        assert_eq!(
703            out,
704            [
705                "#[x]#[::l::announce::__private::__announce_runner(__announce_format_version=\"1\",__announce_sentinel=\"impl\",__announce_owned(my_lib,other),crate=::l::announce,a::check)]\
706                 #[my_lib(on_item)]pubstructS{#[my_lib(skip)]puba:u8}",
707                CHK
708            ]
709            .concat()
710        );
711    }
712
713    #[test]
714    fn finish_reports_a_bad_query() {
715        let item = "struct S;";
716        let attr = "a::check";
717        let err = |ts: TokenStream| try_finish(ts).err().unwrap().to_string();
718        // A name that rustc uses.
719        let e = err(finish_input(attr, item, "attrs(doc)"));
720        assert!(
721            e.contains("`doc` cannot be an owned attribute name"),
722            "{}",
723            e
724        );
725        // A check that changed the state.
726        let e = err(finish_input(attr, item, "other(x)"));
727        assert!(e.contains("was changed by a check"), "{}", e);
728        let e = err(finish_input(attr, item, "attrs[x]"));
729        assert!(e.contains("was changed by a check"), "{}", e);
730        let e = err(ts(
731            "@__announce_attrs [ f::finish! ] { [7u64; weird (a) { struct S; }] }",
732        ));
733        assert!(e.contains("was changed by a check"), "{}", e);
734        // A list that was not forwarded to the end: a check kept its own head ...
735        let e = err(ts(
736            "@__announce_attrs [ x!, f::finish! ] { [7u64; attr (a) { struct S; }] }",
737        ));
738        assert!(e.contains("elements left in its list"), "{}", e);
739        // ... or dropped more than that.
740        let e = err(ts(
741            "@__announce_attrs [] { [7u64; attr (a) { struct S; }] }",
742        ));
743        assert!(e.contains("with an empty list"), "{}", e);
744        let e = err(ts(
745            "@__announce_attrs [ #[derive(A)] ] { [7u64; attr (a) { struct S; }] }",
746        ));
747        assert!(e.contains("elements left in its list"), "{}", e);
748        // The list `[ finish! ]`, whatever the path of the macro, is the end.
749        try_finish(ts(
750            "@__announce_attrs [ x::y! ] { [7u64; attr () { struct S; }] }",
751        ))
752        .unwrap();
753        // Called by hand.
754        let e = err(ts("struct S;"));
755        assert!(e.contains("cannot be called by hand"), "{}", e);
756        // The item is parsed again, with its error.
757        assert!(try_finish(finish_input(attr, "fn f() {}", "")).is_err());
758    }
759
760    #[test]
761    fn finish_reads_the_query_with_the_prefix_and_without_it() {
762        let state = |prefix: &str| {
763            ts(&format!(
764                "{} @__announce_attrs [ ::l::announce::__private::finish! ] {{ [7u64; attr (crate=::l::announce) {{ pub struct S; }}] attrs(a) }}",
765                prefix
766            ))
767        };
768        let without = norm(&finish(state("")));
769        let with = norm(&finish(state("@announce_format \"1\"")));
770        assert!(without.contains("Capture"), "{}", without);
771        assert_eq!(without, with);
772        // Another version is one error that names both versions; it is not the
773        // error of a call by hand.
774        let e = try_finish(state("@announce_format \"2\""))
775            .err()
776            .unwrap()
777            .to_string();
778        assert!(
779            e.starts_with("announce: incompatible wire format: found `@announce_format \"2\"`, expected `@announce_format \"1\"`")
780                && !e.contains("cannot be called by hand"),
781            "{}",
782            e
783        );
784        // `finish` turns it into a `compile_error!`.
785        let out = norm(&finish(state("@announce_format \"2\"")));
786        assert!(
787            out.starts_with("::core::compile_error!") && out.contains("incompatiblewireformat"),
788            "{}",
789            out
790        );
791    }
792
793    #[test]
794    fn capture_arguments_read_the_owned_names() {
795        let with: options::CaptureArgs =
796            syn::parse2(ts("5u64; [a, b]; crate = ::l::announce, c::check")).unwrap();
797        assert_eq!(with.random, 5);
798        assert_eq!(
799            with.owned
800                .names()
801                .iter()
802                .map(|n| n.to_string())
803                .collect::<Vec<_>>(),
804            ["a", "b"]
805        );
806        assert_eq!(
807            norm(&with.opts.to_token_stream()),
808            "crate=::l::announce,c::check"
809        );
810        let without: options::CaptureArgs = syn::parse2(ts("5u64; c::check")).unwrap();
811        assert!(without.owned.is_empty());
812        assert_eq!(norm(&without.opts.to_token_stream()), "c::check");
813    }
814
815    // ---- several announcements of one item (see `runner`) --------------------------
816
817    /// Follows `#[announce(..)]` on the item the way rustc does, with checks that own
818    /// attributes (`owned`: the check and the names it owns), until the trampoline: the
819    /// first attribute whose path ends in `announce` is expanded, then the runner; the
820    /// rest of the attributes are inert here. Returns the terminal output and how many
821    /// times the query was sent.
822    fn drive(attr: &str, item: &str, owned: &[(&str, &[&str])]) -> (String, usize) {
823        let (mut attr, mut item) = (ts(attr), ts(item));
824        let mut queries = 0;
825        let mut out = announce(attr.clone(), item.clone());
826        for _ in 0..20 {
827            if norm(&out).contains("@__announce_attrs") {
828                // The call of the first check. The checks forward the query, each adds
829                // its `attrs(..)`, `finish!` ends it.
830                queries += 1;
831                let group = match out.clone().into_iter().last() {
832                    Some(proc_macro2::TokenTree::Group(g)) => g,
833                    _ => panic!("{}", out),
834                };
835                let q: helper::Query = syn::parse2(group.stream()).unwrap();
836                let mut names = OwnedAttrs::new();
837                for c in &q.list.0 {
838                    let text: String = norm(&c.to_token_stream()).replace('!', "");
839                    for (check, own) in owned {
840                        if *check == text {
841                            names.extend(OwnedAttrs::from_strs(own).names().iter().cloned());
842                        }
843                    }
844                }
845                let (state, last) = (&q.state, q.list.0.last().unwrap());
846                out = finish(quote! { @__announce_attrs [ #last ] { #state #names } });
847                continue;
848            }
849            if norm(&out).contains("Capture") {
850                return (norm(&out), queries);
851            }
852            // The next macro is the first attribute from the top that is an `announce`,
853            // or the runner when none is left: its arguments, and the item without it.
854            let o = scan::outer(&out);
855            let at = o
856                .attrs
857                .iter()
858                .position(|a| a.segments.last().map_or(false, |l| l == "announce"))
859                .or_else(|| {
860                    o.attrs.iter().position(|a| {
861                        a.segments
862                            .last()
863                            .map_or(false, |l| l == "__announce_runner")
864                    })
865                })
866                .unwrap_or_else(|| panic!("nothing left to expand: {}", norm(&out)));
867            attr = o.attrs[at].args.clone().unwrap().stream();
868            item = o
869                .tts
870                .iter()
871                .enumerate()
872                .filter(|(i, _)| *i != o.attrs[at].at && *i != o.attrs[at].at + 1)
873                .map(|(_, t)| t.clone())
874                .collect();
875            let is_runner = o.attrs[at]
876                .segments
877                .last()
878                .map_or(false, |l| l == "__announce_runner");
879            out = if is_runner {
880                run_announce(attr.clone(), item.clone())
881            } else {
882                announce(attr.clone(), item.clone())
883            };
884        }
885        panic!("no end: {}", norm(&out));
886    }
887
888    #[test]
889    fn the_query_is_sent_once_for_an_announcement() {
890        let owned: &[(&str, &[&str])] = &[("a::check", &["my_lib"]), ("b::check", &["other"])];
891        // The first expansion sends the query, `finish` puts the runner on the item with
892        // the answer in its arguments, and the owned attribute after it. The runner makes
893        // the trampoline without asking again.
894        let first = norm(&announce(
895            ts("crate = ::l::announce, a::check"),
896            ts("#[other] #[my_lib(x)] pub struct S;"),
897        ));
898        assert!(
899            first.starts_with("a::check!{@announce_format\"1\"@__announce_attrs"),
900            "{}",
901            first
902        );
903        let runner = norm(&finish(finish_input(
904            "crate=::l::announce,a::check",
905            "#[other] #[my_lib(x)] pub struct S;",
906            "attrs(my_lib)",
907        )));
908        assert_eq!(
909            runner,
910            [
911                "#[other]#[::l::announce::__private::__announce_runner(__announce_format_version=\"1\",__announce_sentinel=\"impl\",__announce_owned(my_lib),crate=::l::announce,a::check)]#[my_lib(x)]pubstructS;",
912                CHK
913            ]
914            .concat()
915        );
916        // The same, driven all the way.
917        let (end, queries) = drive(
918            "crate = ::l::announce, a::check",
919            "#[other] #[my_lib(x)] pub struct S;",
920            owned,
921        );
922        assert_eq!(queries, 1, "{}", end);
923        assert_eq!(end.matches("Capture").count(), 1, "{}", end);
924        // The owned names go to the capture derive, which emits the item without them.
925        assert!(
926            end.contains("#[__announce(")
927                && end.contains("[my_lib];crate=::l::announce,a::check)]"),
928            "{}",
929            end
930        );
931        // An answer without names is carried as well, and is no reason to ask again.
932        let (end, queries) = drive("crate = ::l::announce, b::check", "#[x] pub struct S;", &[]);
933        assert_eq!(queries, 1, "{}", end);
934        // No other attribute, no runner: one query, and the trampoline.
935        let (end, queries) = drive("crate = ::l::announce, a::check", "pub struct S;", owned);
936        assert_eq!(queries, 1);
937        assert!(!end.contains("__announce_runner"), "{}", end);
938    }
939
940    #[test]
941    fn two_announcements_with_checks_ask_once_each() {
942        // Two announcements with checks, merged into the runner: the query is sent for
943        // each of them once, and the answers are united. The attribute of each stands
944        // after the runner when it expands.
945        let owned: &[(&str, &[&str])] = &[("a::check", &["my_lib"]), ("b::check", &["other"])];
946        let item = "#[::l::announce::announce(b::check)] #[my_lib(x)] #[other(y)] pub struct S;";
947        let (end, queries) = drive("crate = ::l::announce, a::check", item, owned);
948        assert_eq!(queries, 2, "{}", end);
949        assert_eq!(end.matches("Capture").count(), 1, "{}", end);
950        assert!(
951            end.contains("[my_lib,other];crate=::l::announce,a::check,b::check)]"),
952            "{}",
953            end
954        );
955        // The same checks twice: one query for each announcement, the check once in META.
956        let item = "#[::l::announce::announce(a::check)] #[my_lib(x)] pub struct S;";
957        let (end, queries) = drive("crate = ::l::announce, a::check", item, owned);
958        assert_eq!(queries, 2, "{}", end);
959        assert_eq!(end.matches("a::check").count(), 1, "{}", end);
960        // The same on a trait.
961        let item = "#[::l::announce::announce(b::check)] #[my_lib(x)] #[other(y)] pub trait T {}";
962        let (end, queries) = drive("crate = ::l::announce, a::check", item, owned);
963        assert_eq!(queries, 2, "{}", end);
964        assert!(end.contains("[my_lib,other];"), "{}", end);
965    }
966
967    #[test]
968    fn an_item_that_is_not_emitted_goes_through_the_runner_as_well() {
969        for opt in ["emit_item = false", "mock = a::B"] {
970            let attr = format!("crate = ::l::announce, {}", opt);
971            // Another attribute: the runner, which holds the option and drops the item
972            // when it expands; the macros below see the item until then.
973            let out = norm(&announce(ts(&attr), ts("#[derive(Debug)] pub struct S;")));
974            // The item is not emitted, so the sentinel is the call of the trampoline.
975            let want = format!(
976                "#[derive(Debug)]#[::l::announce::__private::__announce_runner(__announce_format_version=\"1\",__announce_sentinel=\"macro\",crate=::l::announce,{})]pubstructS;S!{{@announce_format\"1\"@__announce_exists}}",
977                opt.replace(' ', "")
978            );
979            assert_eq!(out, want);
980            // No other attribute: nothing to wait for, the dummy alone (the item is not
981            // emitted).
982            let out = norm(&announce(ts(&attr), ts("pub struct S;")));
983            assert!(
984                out.starts_with("#[derive(::l::announce::__private::Capture)]")
985                    && !out.contains("__announce_runner"),
986                "{}",
987                out
988            );
989            assert_eq!(out.matches("pubstructS").count(), 1, "{}", out);
990            // The runner makes the trampoline and drops the item.
991            let args = format!("__announce_format_version = \"1\", {}", attr);
992            let out = norm(&run_announce(ts(&args), ts("#[doc = \"d\"] pub struct S;")));
993            assert!(
994                out.starts_with("#[derive(::l::announce::__private::Capture)]"),
995                "{}",
996                out
997            );
998            assert_eq!(out.matches("pubstructS").count(), 1, "{}", out);
999            // With a check, the query comes first, then the runner.
1000            let out = norm(&announce(
1001                ts(&format!("{}, a::check", attr)),
1002                ts("#[derive(Debug)] pub struct S;"),
1003            ));
1004            assert!(
1005                out.starts_with("a::check!{@announce_format\"1\"@__announce_attrs"),
1006                "{}",
1007                out
1008            );
1009            let end = norm(&finish(finish_input(
1010                &format!("crate=::l::announce,{},a::check", opt.replace(' ', "")),
1011                "#[derive(Debug)] pub struct S;",
1012                "attrs(my_lib)",
1013            )));
1014            assert!(
1015                end.contains("pubstructS;S!{@announce_format\"1\"@__announce_exists}")
1016                    && end.contains("__announce_runner(__announce_format_version=\"1\",__announce_sentinel=\"macro\",__announce_owned(my_lib),"),
1017                "{}",
1018                end
1019            );
1020        }
1021    }
1022
1023    #[test]
1024    fn announcements_merge_with_one_that_is_not_emitted() {
1025        // The values that are set win over those that are not, in both orders; two
1026        // values that differ are the one merge error.
1027        let runner = |opt: &str| {
1028            format!(
1029                "#[::l::announce::__private::__announce_runner(__announce_format_version = \"1\"{})]",
1030                opt
1031            )
1032        };
1033        let out = norm(&announce(
1034            ts("meta = a::x"),
1035            ts(&format!(
1036                "#[x] {} pub struct S;",
1037                runner(", emit_item = false")
1038            )),
1039        ));
1040        assert!(out.contains("emit_item=false,meta=a::x)]"), "{}", out);
1041        let out = norm(&announce(
1042            ts("emit_item = false"),
1043            ts(&format!("#[x] {} pub struct S;", runner(", meta = a::x"))),
1044        ));
1045        assert!(out.contains("emit_item=false"), "{}", out);
1046        assert!(out.contains("meta=a::x"), "{}", out);
1047        let out = norm(&announce(
1048            ts("mock = a::B"),
1049            ts(&format!(
1050                "#[x] {} pub trait S {{}}",
1051                runner(", meta = a::x")
1052            )),
1053        ));
1054        assert!(
1055            out.contains("mock=a::B") && !out.contains("compile_error"),
1056            "{}",
1057            out
1058        );
1059        // Different values are a conflict, in one error with the item.
1060        let out = norm(&announce(
1061            ts("emit_item = false"),
1062            ts(&format!(
1063                "#[x] {} pub struct S;",
1064                runner(", emit_item = true")
1065            )),
1066        ));
1067        assert!(out.starts_with("::core::compile_error!"), "{}", out);
1068        assert!(out.contains("`emit_item=false`"), "{}", out);
1069        assert_eq!(out.matches("compile_error").count(), 1, "{}", out);
1070        let out = norm(&announce(
1071            ts("mock = a::B"),
1072            ts(&format!(
1073                "#[x] {} pub trait S {{}}",
1074                runner(", mock = a::C")
1075            )),
1076        ));
1077        assert!(
1078            out.starts_with("::core::compile_error!") && out.contains("mock="),
1079            "{}",
1080            out
1081        );
1082    }
1083
1084    #[test]
1085    fn announcements_merge_only_under_one_trampoline_name() {
1086        let runner = |opts: &str| {
1087            format!(
1088                "#[::l::announce::__private::__announce_runner(__announce_format_version = \"1\"{})]",
1089                opts
1090            )
1091        };
1092        // Unset is the name of the item: `Parse!` and `ParseAlt!` are two names.
1093        let item = format!("#[x] {} pub trait Parse {{}}", runner(""));
1094        let out = norm(&announce(ts("macro_name = ParseAlt"), ts(&item)));
1095        assert!(out.starts_with("::core::compile_error!"), "{}", out);
1096        assert_eq!(out.matches("compile_error").count(), 1, "{}", out);
1097        assert!(out.contains("(`ParseAlt`and`Parse`)"), "{}", out);
1098        assert!(out.contains("onetrampoline"), "{}", out);
1099        assert!(out.ends_with(&norm(&ts(&item))), "{}", out);
1100        // The same when one runner expands with another on the item.
1101        let out = norm(&run_announce(
1102            ts("__announce_format_version = \"1\", macro_name = ParseAlt"),
1103            ts(&format!("{} pub trait Parse {{}}", runner(""))),
1104        ));
1105        assert!(out.starts_with("::core::compile_error!"), "{}", out);
1106        assert!(out.contains("(`ParseAlt`and`Parse`)"), "{}", out);
1107        // The same name, set in one of them or in both, is one trampoline.
1108        for (attr, set) in [
1109            ("macro_name = Parse", ""),
1110            ("", ", macro_name = Parse"),
1111            ("macro_name = ParseAlt", ", macro_name = ParseAlt"),
1112        ] {
1113            let item = format!("#[x] {} pub trait Parse {{}}", runner(set));
1114            let out = norm(&announce(ts(attr), ts(&item)));
1115            assert!(!out.contains("compile_error"), "{}: {}", item, out);
1116            assert_eq!(out.matches("__announce_runner").count(), 1, "{}", out);
1117        }
1118    }
1119
1120    // ---- the capture dummy, read without a parse ----------------------------
1121
1122    const DUMMY_ARGS: &str = "7u64; crate = ::l::announce";
1123
1124    fn dummy(args: &str, body: &str) -> TokenStream {
1125        ts(&format!(
1126            "#[derive(::l::announce::__private::Capture)] #[__announce({})] \
1127             #[::l::announce::__private::remove] #[allow(dead_code, non_camel_case_types)] \
1128             enum __AnnounceCap_0 {{ V = {{ {} 0 }} }}",
1129            args, body
1130        ))
1131    }
1132
1133    /// `#[derive(..)]` is not part of what a derive macro receives.
1134    fn derive_input(args: &str, body: &str) -> TokenStream {
1135        let mut it = dummy(args, body).into_iter();
1136        it.next();
1137        it.next();
1138        it.collect()
1139    }
1140
1141    #[test]
1142    fn the_dummy_is_read_from_its_top_level_tokens() {
1143        let (args, item) =
1144            scan_dummy(derive_input(DUMMY_ARGS, "pub struct S { pub a: u8 }")).unwrap();
1145        assert_eq!(norm(&args), "7u64;crate=::l::announce");
1146        assert_eq!(norm(&item.unwrap()), "pubstructS{puba:u8}");
1147        // An item that was `#[cfg]`'d out leaves the block with the `0` only.
1148        let (_, item) = scan_dummy(derive_input(DUMMY_ARGS, "")).unwrap();
1149        assert!(item.is_none());
1150        // Other shapes are not read (the code then parses the dummy).
1151        for bad in [
1152            "struct S;",
1153            "enum E { V = 1 }",
1154            "enum E { V = { 1 } }",
1155            "enum E { V = { S } }",
1156            "#[__announce] enum E { V = { 0 } }",
1157            "#[__announce(1u64;)] enum E { V = { S 1 } }",
1158        ] {
1159            assert!(scan_dummy(ts(bad)).is_none(), "{}", bad);
1160        }
1161    }
1162
1163    #[test]
1164    fn a_dummy_of_another_shape_is_read_with_syn() {
1165        // `pub`: the scan does not know it, `syn::DeriveInput` does.
1166        let input = ts(
1167            "#[__announce(7u64; crate = ::l::announce)] pub enum Cap { V = { pub struct S; 0 } }",
1168        );
1169        assert!(scan_dummy(input.clone()).is_none());
1170        let d = read_dummy(input).unwrap();
1171        assert_eq!(d.args.random, 7);
1172        assert_eq!(norm(&d.item.unwrap()), "pubstructS;");
1173        let e = read_dummy(ts("pub enum Cap { V = { 0 } }")).err().unwrap();
1174        assert!(e.to_string().contains("missing #[__announce]"), "{}", e);
1175    }
1176
1177    #[test]
1178    fn capture_emits_what_the_derive_emits() {
1179        let out = norm(&capture(derive_input(DUMMY_ARGS, "pub struct S { pub a: u8 }")).unwrap());
1180        assert!(out.contains("macro_rules!__announce_S_"), "{}", out);
1181        assert!(out.contains("::l::announce::AnnounceforS{}"), "{}", out);
1182        // The item itself is not part of it: `#[announce]` emitted it.
1183        assert!(!out.starts_with("pubstruct"), "{}", out);
1184        // Gone with its `#[cfg]`.
1185        assert!(capture(derive_input(DUMMY_ARGS, "")).unwrap().is_empty());
1186        // Not a valid item: the error of the parse.
1187        assert!(capture(derive_input(DUMMY_ARGS, "pub struct S { pub a: }")).is_err());
1188    }
1189
1190    #[test]
1191    fn capture_keeps_the_real_item_next_to_its_error() {
1192        // With owned attributes the derive emits the item, so an error in the type
1193        // context is followed by the item (without the owned attributes and the cfgs).
1194        let args = "7u64; [my_lib]; crate = ::l::announce";
1195        let out = norm(
1196            &capture(derive_input(
1197                args,
1198                "pub struct S { #[my_lib] #[cfg(a)] pub a: Local, pub b: u8 }",
1199            ))
1200            .unwrap(),
1201        );
1202        assert!(out.starts_with("::core::compile_error!"), "{}", out);
1203        assert!(out.ends_with("pubstructS{puba:Local,pubb:u8}"), "{}", out);
1204        // Without owned names `#[announce]` emitted it: the error only.
1205        let out =
1206            norm(&capture(derive_input(DUMMY_ARGS, "pub struct S { pub a: Local }")).unwrap());
1207        assert!(out.starts_with("::core::compile_error!"), "{}", out);
1208        assert!(!out.contains("pubstructS"), "{}", out);
1209    }
1210
1211    #[test]
1212    fn the_capture_arguments_read_the_sentinel() {
1213        use runner::Sentinel;
1214        let with: options::CaptureArgs =
1215            syn::parse2(ts("5u64; sentinel_impl; [a]; crate = ::l::announce")).unwrap();
1216        assert_eq!(with.sentinel, Some(Sentinel::Impl));
1217        assert_eq!(with.owned.names().len(), 1);
1218        let with: options::CaptureArgs = syn::parse2(ts("5u64; sentinel_macro; c::check")).unwrap();
1219        assert_eq!(with.sentinel, Some(Sentinel::Macro));
1220        assert_eq!(norm(&with.opts.to_token_stream()), "c::check");
1221        let without: options::CaptureArgs = syn::parse2(ts("5u64; c::check")).unwrap();
1222        assert_eq!(without.sentinel, None);
1223        assert!(syn::parse2::<options::CaptureArgs>(ts("5u64; sentinel_cake; c::check")).is_err());
1224        // The announcement writes the word into the dummy, the capture reads it.
1225        let out = norm(&capture_output(
1226            &Options::default(),
1227            &TokenStream::new(),
1228            ts("pub struct S;"),
1229            5,
1230            &OwnedAttrs::new(),
1231            Some(runner::Sentinel::Macro),
1232        ));
1233        assert!(
1234            out.contains("#[__announce(5u64;sentinel_macro;)]"),
1235            "{}",
1236            out
1237        );
1238    }
1239
1240    #[test]
1241    fn capture_leaves_what_the_sentinel_needs_next_to_its_error() {
1242        // The error is reported; the sentinel of the announcement is satisfied, so that it
1243        // does not add an error about the same item.
1244        let args = "7u64; sentinel_impl; crate = ::l::announce";
1245        for body in ["pub struct S { pub a: Local }", "pub struct S { pub a: }"] {
1246            let out = norm(&capture(derive_input(args, body)).unwrap());
1247            assert!(
1248                out.starts_with("::core::compile_error!"),
1249                "{}: {}",
1250                body,
1251                out
1252            );
1253            assert!(
1254                out.ends_with("impl::l::announce::AnnounceforS{}"),
1255                "{}: {}",
1256                body,
1257                out
1258            );
1259        }
1260        let args = "7u64; sentinel_macro; crate = ::l::announce, macro_name = N";
1261        for body in [
1262            "pub trait S { fn f(&self) -> Local; }",
1263            "pub trait S { fn }",
1264        ] {
1265            let out = norm(&capture(derive_input(args, body)).unwrap());
1266            assert!(
1267                out.starts_with("::core::compile_error!"),
1268                "{}: {}",
1269                body,
1270                out
1271            );
1272            assert!(
1273                out.contains(
1274                    "macro_rules!__announce_stub_0000000000000007{($(@announce_format\"1\")?@__announce_exists)=>{};}"
1275                ) && out.ends_with("use__announce_stub_0000000000000007asN;"),
1276                "{}: {}",
1277                body,
1278                out
1279            );
1280        }
1281        // Without a sentinel there is nothing of the kind.
1282        let out =
1283            norm(&capture(derive_input(DUMMY_ARGS, "pub struct S { pub a: Local }")).unwrap());
1284        assert!(
1285            !out.contains("Announce") && !out.contains("__announce_exists"),
1286            "{}",
1287            out
1288        );
1289        // The item is `cfg`'d out: nothing, as its sentinel.
1290        let args = "7u64; sentinel_macro; crate = ::l::announce";
1291        assert!(capture(derive_input(args, "")).unwrap().is_empty());
1292    }
1293
1294    #[test]
1295    fn the_attribute_reads_no_more_than_the_head_of_the_item() {
1296        // The syntax of the rest is the business of the derive: an item with an
1297        // error in it is passed on, to be refused there.
1298        for item in ["pub struct S { pub a: }", "pub trait T { fn }"] {
1299            let out = norm(&announce(TokenStream::new(), ts(item)));
1300            assert!(
1301                (out.contains("Capture") || out.contains("__announce_runner"))
1302                    && !out.contains("compile_error"),
1303                "{}",
1304                out
1305            );
1306        }
1307        // But what is not a supported item is refused at once, with the same errors.
1308        for (src, msg) in [
1309            ("pub fn f() {}", "supports struct, enum"),
1310            ("mod m;", "only inline modules"),
1311            ("impl S {}", "supports struct, enum"),
1312        ] {
1313            let e = try_announce(TokenStream::new(), ts(src)).err().unwrap();
1314            assert!(e.to_string().contains(msg), "{}: {}", src, e);
1315        }
1316        // The kinds, with all their qualifiers.
1317        for src in [
1318            "struct S;",
1319            "pub(crate) enum E {}",
1320            "union U { a: u8 }",
1321            "pub unsafe trait T {}",
1322            "type A = u8;",
1323            "mod m { }",
1324        ] {
1325            assert!(try_announce(TokenStream::new(), ts(src)).is_ok(), "{}", src);
1326        }
1327    }
1328}