derive_deftly_macros/
expand.rs

1//! Expansion of a template into output tokens
2//!
3//! Contains the implementations of `fn expand()`
4//! for the various template types in [`super::syntax`].
5
6use super::framework::*;
7
8impl<O> Expand<O> for SubstIf<O>
9where
10    Template<O>: ExpandInfallible<O>,
11    O: ExpansionOutput,
12{
13    fn expand<'c>(
14        &self,
15        ctx: GeneralContext<'c>,
16        out: &mut O,
17    ) -> syn::Result<()> {
18        for (condition, consequence) in &self.tests {
19            //dbg!(&condition);
20            if condition.eval_bool(ctx)? {
21                //dbg!(&consequence);
22                consequence.expand(ctx, out);
23                return Ok(());
24            }
25        }
26        if let Some(consequence) = &self.otherwise {
27            //dbg!(&consequence);
28            consequence.expand(ctx, out);
29        }
30        Ok(())
31    }
32}
33
34impl<O> SubstIf<O>
35where
36    Template<O>: ExpandInfallible<O>,
37    O: ExpansionOutput,
38{
39    fn expand_select1<'c>(
40        &self,
41        ctx: GeneralContext<'c>,
42        out: &mut O,
43    ) -> syn::Result<()> {
44        let mut found: Result<Option<(Span, &Template<O>)>, Vec<ErrorLoc>> =
45            Ok(None);
46
47        for (condition, consequence) in &self.tests {
48            if !condition.eval_bool(ctx)? {
49                continue;
50            }
51            let cspan = condition.span();
52            let error_loc = |span| (span, "true condition");
53            match &mut found {
54                Ok(None) => found = Ok(Some((cspan, consequence))),
55                Ok(Some((span1, _))) => {
56                    found = Err(ctx.prepend_error_loc(&[
57                        error_loc(*span1),
58                        error_loc(cspan),
59                    ]))
60                }
61                Err(several) => several.push(error_loc(cspan)),
62            }
63        }
64        let found = found
65            .map_err(|several| several.error("multiple conditions matched"))?
66            .map(|(_cspan, consequence)| consequence)
67            .or(self.otherwise.as_deref())
68            .ok_or_else(|| {
69                ctx.prepend_error_loc(
70                    &[(self.kw_span, "select1 expansion")], //
71                )
72                .error("no conditions matched, and no else clause")
73            })?;
74        found.expand(ctx, out);
75        Ok(())
76    }
77}
78
79impl SubstVType {
80    fn expand(
81        &self,
82        ctx: &Context,
83        out: &mut TokenAccumulator,
84        kw_span: Span,
85        self_def: SubstDetails<TokenAccumulator>,
86    ) -> syn::Result<()> {
87        let expand_spec_or_sd =
88            |out: &mut _,
89             spec: &Option<Argument<TokenAccumulator>>,
90             sd: SubstDetails<TokenAccumulator>| {
91                if let Some(spec) = spec {
92                    spec.expand(ctx.as_general(), out);
93                    Ok(())
94                } else {
95                    sd.expand(ctx.as_general(), out, kw_span)
96                }
97            };
98
99        if !ctx.is_enum() {
100            return expand_spec_or_sd(out, &self.self_, self_def);
101        }
102        // It's an enum.  We need to write the main type name,
103        // and the variant.  Naively we might expect to just do
104        //    TTYPE::VNAME
105        // but that doesn't work, because if TTYPE has generics, that's
106        //    TNAME::<TGENERICS>::VNAME
107        // and this triggers bizarre (buggy) behaviour in rustc -
108        // see rust-lang/rust/issues/108224.
109        // So we need to emit
110        //    TNAME::VNAME::<TGENERICS>
111        //
112        // The most convenient way to do that seems to be to re-parse
113        // this bit of the expansion as a syn::Path.  That lets
114        // us fish out the generics, for writing out later.
115
116        let mut self_ty = TokenAccumulator::new();
117        expand_spec_or_sd(&mut self_ty, &self.self_, self_def)?;
118        let self_ty = self_ty.tokens()?;
119        let mut self_ty: syn::Path =
120            syn::parse2(self_ty).map_err(|mut e| {
121                e.combine(kw_span.error(
122                    "error re-parsing self type path for this expansion",
123                ));
124                e
125            })?;
126
127        let mut generics = mem::take(
128            &mut self_ty
129                .segments
130                .last_mut()
131                .ok_or_else(|| {
132                    kw_span.error(
133                        "self type path for this expansion is empty path!",
134                    )
135                })?
136                .arguments,
137        );
138
139        out.append(self_ty);
140        out.append(Token![::](kw_span));
141        expand_spec_or_sd(out, &self.vname, SD::vname(Default::default()))?;
142        let gen_content = match &mut generics {
143            syn::PathArguments::AngleBracketed(content) => Some(content),
144            syn::PathArguments::None => None,
145            syn::PathArguments::Parenthesized(..) => {
146                return Err([
147                    (generics.span(), "generics"),
148                    (kw_span, "template keyword"),
149                ]
150                .error("self type has parenthesised generics, not supported"))
151            }
152        };
153        if let Some(gen_content) = gen_content {
154            // Normalise `<GENERICS>` to `::<TGENERICS>`.
155            gen_content
156                .colon2_token
157                .get_or_insert_with(|| Token![::](kw_span));
158            out.append(&generics);
159        }
160        Ok(())
161    }
162}
163
164impl SubstVPat {
165    // $vpat      for struct    $tname         { $( $fname: $fpatname, ) }
166    // $vpat      for enum      $tname::$vname { $( $fname: $fpatname, ) }
167    fn expand(
168        &self,
169        ctx: &Context,
170        out: &mut TokenAccumulator,
171        kw_span: Span,
172    ) -> syn::Result<()> {
173        let self_def = SD::tname(Default::default());
174        SubstVType::expand(&self.vtype, ctx, out, kw_span, self_def)?;
175
176        let in_braces = braced_group(kw_span, |mut out| {
177            WithinField::for_each(ctx, |ctx, field| {
178                SD::fname::<TokenAccumulator>(()).expand(
179                    ctx.as_general(),
180                    &mut out,
181                    kw_span,
182                )?;
183                out.append_tokens(&((), ()), Token![:](kw_span))?;
184
185                // Do the expansion with the paste machinery, since
186                // that has a ready-made notion of what fprefix= might
187                // allow, and how to use it.
188                let mut paste = paste::Items::new(kw_span);
189                if let Some(fprefix) = &self.fprefix {
190                    fprefix.expand(ctx.as_general(), &mut paste);
191                } else {
192                    paste.append_fixed_string("f_");
193                }
194                paste.append_identfrag_toks(&field.fname(kw_span))?;
195                paste.assemble(out, None)?;
196                out.append(Token![,](kw_span));
197
198                Ok::<_, syn::Error>(())
199            })
200        })?;
201        out.append(in_braces);
202        Ok(())
203    }
204}
205
206impl<O> ExpandInfallible<O> for Template<O>
207where
208    TemplateElement<O>: Expand<O>,
209    O: ExpansionOutput,
210{
211    fn expand<'c>(&self, ctx_in: GeneralContext<'c>, out: &mut O) {
212        Self::expand_iter(&self.elements, ctx_in, out)
213    }
214}
215
216impl<'s, O> Template<O>
217where
218    TemplateElement<O>: Expand<O>,
219    O: ExpansionOutput + 's,
220{
221    pub fn expand_iter<'c>(
222        elements: impl IntoIterator<Item = &'s TemplateElement<O>>,
223        ctx_in: GeneralContext<'c>,
224        out: &mut O,
225    ) {
226        let mut ctx_buf;
227        let mut definitions_here = vec![];
228        let mut defconds_here = vec![];
229        let mut ctx = ctx_in;
230
231        for element in elements {
232            macro_rules! handle_definition { {
233                $variant:ident, $store:expr
234            } => {
235                if let TE::Subst(Subst {
236                    sd: SD::$variant(def, _),
237                    ..
238                }) = element
239                {
240                    // Doing this with a macro makes it nice and obvious
241                    // to the borrow checker.
242                    $store.push(def);
243                    ctx_buf = ctx_in.clone_buf();
244                    ctx_buf.as_mut().defs = Definitions {
245                        earlier: Some(&ctx_in.defs().defs),
246                        here: &definitions_here,
247                        conds: &defconds_here,
248                    };
249                    ctx = ctx_buf.as_ref();
250                    continue;
251                }
252
253            } }
254
255            handle_definition!(define, definitions_here);
256            handle_definition!(defcond, defconds_here);
257
258            let () = element
259                .expand(ctx, out)
260                .unwrap_or_else(|err| out.record_error(err));
261        }
262    }
263}
264
265impl Expand<TokenAccumulator> for TemplateElement<TokenAccumulator> {
266    fn expand<'c>(
267        &self,
268        ctx: GeneralContext<'c>,
269        out: &mut TokenAccumulator,
270    ) -> syn::Result<()> {
271        match self {
272            TE::Ident(tt, ..) => out.append(tt.clone()),
273            TE::Literal(tt, ..) => out.append(tt.clone()),
274            TE::LitStr(tt) => out.append(tt.clone()),
275            TE::Punct(tt, _) => out.append(tt.clone()),
276            TE::Group {
277                delim_span,
278                delimiter,
279                template,
280                allow_tokens: _,
281            } => {
282                let mut content = TokenAccumulator::new();
283                template.expand(ctx, &mut content);
284                let group = group_new_with_span(
285                    *delimiter,
286                    *delim_span,
287                    content.tokens()?,
288                );
289                out.append(TT::Group(group));
290            }
291            TE::Subst(exp) => {
292                exp.expand(ctx, out)?;
293            }
294            TE::Repeat(repeated_template) => {
295                repeated_template.expand(ctx, out);
296            }
297        }
298        Ok(())
299    }
300}
301
302impl<O> Expand<O> for Subst<O>
303where
304    O: ExpansionOutput,
305    TemplateElement<O>: Expand<O>,
306{
307    fn expand<'c>(
308        &self,
309        ctx: GeneralContext<'c>,
310        out: &mut O,
311    ) -> syn::Result<()> {
312        self.sd.expand(ctx, out, self.kw_span)
313    }
314}
315
316impl<O> SubstDetails<O>
317where
318    O: ExpansionOutput,
319    TemplateElement<O>: Expand<O>,
320{
321    /// Expand this template element, by adding it to `O`
322    ///
323    /// This is done using `O`'s [`ExpansionOutput`] methods.
324    fn expand<'c>(
325        &self,
326        gctx: GeneralContext<'c>,
327        out: &mut O,
328        kw_span: Span,
329    ) -> syn::Result<()> {
330        // eprintln!("@@@@@@@@@@@@@@@@@@@@ EXPAND {:?}", self);
331
332        let ctx = gctx.full_ctx(kw_span);
333
334        let do_meta = |sm: &meta::SubstMeta<_>, out, meta| {
335            sm.expand(ctx?, kw_span, out, meta)
336        };
337
338        // Methods for handling generics.  Most take `composable: bool`,
339        // which lets us control the trailing comma.  This is desirable
340        // because we should include it for expansions like $tgens that the
341        // user can append things to, but ideally *not* for expansions like
342        // $ttype that the user can't.
343        let do_tgnames = |out: &mut TokenAccumulator,
344                          ctx: &Context,
345                          composable| {
346            for pair in ctx.top.generics.params.pairs() {
347                use syn::GenericParam as GP;
348                match pair.value() {
349                    GP::Type(t) => out.append(&t.ident),
350                    GP::Const(c) => out.append(&c.ident),
351                    GP::Lifetime(l) => out.append(&l.lifetime),
352                }
353                out.append_maybe_punct_composable(&pair.punct(), composable);
354            }
355        };
356        let do_tgens_nodefs = |out: &mut TokenAccumulator| {
357            for pair in ctx?.top.generics.params.pairs() {
358                use syn::GenericParam as GP;
359                let out_attrs = |out: &mut TokenAccumulator, attrs: &[_]| {
360                    attrs.iter().for_each(|attr| out.append(attr));
361                };
362                match pair.value() {
363                    GP::Type(t) => {
364                        out_attrs(out, &t.attrs);
365                        out.append(&t.ident);
366                        out.append(&t.colon_token);
367                        out.append(&t.bounds);
368                    }
369                    GP::Const(c) => {
370                        out_attrs(out, &c.attrs);
371                        out.append(&c.const_token);
372                        out.append(&c.ident);
373                        out.append(&c.colon_token);
374                        out.append(&c.ty);
375                    }
376                    GP::Lifetime(l) => out.append(&l),
377                }
378                out.with_tokens(|out| {
379                    pair.punct().to_tokens_punct_composable(out);
380                });
381            }
382            Ok::<_, MissingContextError>(())
383        };
384        let do_tgens =
385            |out: &mut TokenAccumulator, ctx: &Context, composable: bool| {
386                out.append_maybe_punct_composable(
387                    &ctx.top.generics.params,
388                    composable,
389                );
390            };
391
392        // There are three contexts where the top-level type
393        // name might occur with generics, and two syntaxes:
394        //   referring to the type    $ttype     Type::<G>
395        //   impl'ing for the type    $ttype     Type::<G>
396        //   defining a new type      $tdeftype  Type<G: bounds>
397        // Handles $ttype and $tdeftype, and, indirectly, $vtype
398        let do_ttype = |out: &mut O, colons: Option<()>, do_some_gens| {
399            let _: &dyn Fn(&mut _, _, bool) = do_some_gens; // specify type
400            let ctx = ctx?;
401            let gens = &ctx.top.generics;
402            let colons = gens
403                .lt_token
404                .and_then(|_| colons.map(|()| Token![::](kw_span)));
405            out.append_idpath(
406                kw_span,
407                |_| {},
408                &ctx.top.ident,
409                |out| {
410                    out.append(colons);
411                    out.append(gens.lt_token);
412                    do_some_gens(out, ctx, false);
413                    out.append(gens.gt_token);
414                },
415                Grouping::Ungrouped,
416            )
417            .unwrap_or_else(|e| e.unreachable());
418            Ok::<_, MissingContextError>(())
419        };
420
421        let do_maybe_delimited_group = |out, np, delim, content| {
422            let _: &mut O = out;
423            let _: &Template<TokenAccumulator> = content;
424            out.append_tokens_with(np, |out| {
425                if let Some(delim) = delim {
426                    out.append(delimit_token_group(
427                        delim,
428                        kw_span,
429                        |inside: &mut TokenAccumulator| {
430                            Ok(content.expand(gctx, inside))
431                        },
432                    )?);
433                } else {
434                    content.expand(gctx, out);
435                }
436                Ok(())
437            })
438        };
439
440        let do_index = |out: &mut O, index| {
441            let index = syn::Index {
442                index,
443                span: kw_span,
444            };
445            out.append_identfrag_toks(&TokenPastesAsIdent(index))
446        };
447
448        match self {
449            SD::tname(_) => out.append_identfrag_toks(&ctx?.top.ident)?,
450            SD::ttype(_) => do_ttype(out, Some(()), &do_tgnames)?,
451            SD::tdeftype(_) => do_ttype(out, None, &do_tgens)?,
452            SD::vname(_) => {
453                out.append_identfrag_toks(&ctx?.syn_variant(&kw_span)?.ident)?
454            }
455            SD::fname(_) => {
456                let fname = ctx?.field(&kw_span)?.fname(kw_span);
457                out.append_identfrag_toks(&fname)?;
458            }
459            SD::ftype(_) => {
460                let f = ctx?.field(&kw_span)?;
461                out.append_syn_type(
462                    kw_span,
463                    f.field.ty.clone(),
464                    Grouping::Invisible,
465                );
466            }
467            SD::fpatname(_) => {
468                let f = ctx?.field(&kw_span)?;
469                let fpatname =
470                    Ident::new(&format!("f_{}", f.fname(kw_span)), kw_span);
471                out.append_identfrag_toks(&fpatname)?;
472            }
473            SD::vindex(..) => do_index(out, ctx?.variant(&kw_span)?.index)?,
474            SD::findex(..) => do_index(out, ctx?.field(&kw_span)?.index)?,
475
476            SD::Xmeta(sm) => do_meta(sm, out, sm.pmetas(ctx?, kw_span)?)?,
477            SD::error(e, _) => e.throw(gctx, kw_span)?,
478
479            SD::Vis(vis, at) => {
480                out.append_tokens(at, vis.syn_vis(ctx?, kw_span)?)?
481            }
482            SD::tdefkwd(_) => {
483                fn w<O>(out: &mut O, t: impl ToTokens)
484                where
485                    O: ExpansionOutput,
486                {
487                    out.append_identfrag_toks(&TokenPastesAsIdent(t))
488                        .unwrap_or_else(|e| e.unreachable());
489                }
490                use syn::Data::*;
491                match &ctx?.top.data {
492                    Struct(d) => w(out, &d.struct_token),
493                    Enum(d) => w(out, &d.enum_token),
494                    Union(d) => w(out, &d.union_token),
495                };
496            }
497
498            SD::tattrs(ra, np, ..) => out.append_tokens_with(np, |out| {
499                ra.expand(ctx?, out, &ctx?.top.attrs)
500            })?,
501            SD::vattrs(ra, np, ..) => out.append_tokens_with(np, |out| {
502                let variant = ctx?.variant(&kw_span)?.variant;
503                let attrs = variant.as_ref().map(|v| &*v.attrs);
504                ra.expand(ctx?, out, attrs.unwrap_or_default())
505            })?,
506            SD::fattrs(ra, np, ..) => out.append_tokens_with(np, |out| {
507                ra.expand(ctx?, out, &ctx?.field(&kw_span)?.field.attrs)
508            })?,
509
510            SD::tgens(np, ..) => out.append_tokens_with(np, |out| {
511                do_tgens_nodefs(out)?;
512                Ok(())
513            })?,
514            SD::tdefgens(np, ..) => out.append_tokens_with(np, |out| {
515                do_tgens(out, ctx?, true);
516                Ok(())
517            })?,
518            SD::tgnames(np, ..) => out.append_tokens_with(np, |out| {
519                do_tgnames(out, ctx?, true);
520                Ok(())
521            })?,
522            SD::twheres(np, ..) => out.append_tokens_with(np, |out| {
523                if let Some(clause) = &ctx?.top.generics.where_clause {
524                    out.with_tokens(|out| {
525                        clause.predicates.to_tokens_punct_composable(out);
526                    });
527                }
528                Ok(())
529            })?,
530
531            SD::vpat(v, np, ..) => out.append_tokens_with(np, |out| {
532                // This comment prevents rustfmt making this unlike the others
533                v.expand(ctx?, out, kw_span)
534            })?,
535            SD::vtype(v, np, ..) => out.append_tokens_with(np, |out| {
536                v.expand(ctx?, out, kw_span, SD::ttype(Default::default()))
537            })?,
538
539            SD::tdefvariants(content, np, ..) => {
540                let delim = if ctx?.is_enum() {
541                    Some(Delimiter::Brace)
542                } else {
543                    None
544                };
545                do_maybe_delimited_group(out, np, delim, content)?;
546            }
547            SD::fdefine(spec_f, np, ..) => {
548                out.append_tokens_with(np, |out| {
549                    let field = ctx?.field(&kw_span)?.field;
550                    if let Some(driver_f) = &field.ident {
551                        if let Some(spec_f) = spec_f {
552                            spec_f.expand(gctx, out);
553                        } else {
554                            out.append(driver_f);
555                        }
556                    }
557                    out.append(&field.colon_token);
558                    Ok(())
559                })?
560            }
561            SD::vdefbody(vname, content, np, ..) => {
562                use syn::Fields as SF;
563                let variant = ctx?.variant(&kw_span)?;
564                let struct_variant = variant.is_struct_toplevel_as_variant();
565                if !struct_variant {
566                    vname.expand(gctx, out);
567                }
568                let delim = match variant.fields {
569                    SF::Unit => None,
570                    SF::Unnamed(..) => Some(Delimiter::Parenthesis),
571                    SF::Named(..) => Some(Delimiter::Brace),
572                };
573                do_maybe_delimited_group(out, np, delim, content)?;
574                if !struct_variant {
575                    // Any enum variant: terminate with a comma.
576                    out.append_tokens(np, Token![,](kw_span))?;
577                } else if matches!(variant.fields, SF::Named(_)) {
578                    // struct {} at top-level: no terminator.
579                } else {
580                    // Unit or tuple struct: Terminate with a semicolon.
581                    out.append_tokens(np, Token![;](kw_span))?;
582                }
583            }
584            SD::Crate(np, ..) => {
585                out.append_tokens(np, &ctx?.template_crate)?
586            }
587
588            SD::paste(content, ..) => {
589                paste::expand(gctx, kw_span, content, out)?;
590            }
591            SD::paste_spanned(span, content, ..) => {
592                let span = span.span_from_arg(gctx).unwrap_or_else(|e| {
593                    out.record_error(e);
594                    kw_span
595                });
596                paste::expand(gctx, span, content, out)?;
597            }
598            SD::ChangeCase(content, case, ..) => {
599                let mut items = paste::Items::new(kw_span);
600                content.expand(gctx, &mut items);
601                items.assemble(out, Some(*case))?;
602            }
603            SD::concat(content, _not_in_bool, not_in_paste, _) => {
604                let mut accum = concat::Accumulator::new_with_span(kw_span);
605                content.expand(gctx, &mut accum);
606                accum.finish_onto(not_in_paste, out);
607            }
608
609            SD::define(..) | SD::defcond(..) => out.write_error(
610                &kw_span,
611                // I think this is impossible.  It could only occur if
612                // someone parsed a Subst or SubstDetails that wasn't
613                // in a Template.  It is Template.expand() that handles this.
614                // We could possibly use proof tokens to see if this happens
615                // and exclude it, but that would be super invasive.
616                //
617                // (There are some parallels between this and `${when }`)
618                "${define } and ${defcond } only allowed in a full template",
619            ),
620            SD::UserDefined(name) => name.lookup_expand(gctx, out)?,
621
622            SD::ignore(content, _) => {
623                let mut ignore = O::new_with_span(kw_span);
624                content.expand(gctx, &mut ignore);
625                let () = ignore.ignore_impl()?;
626            }
627            SD::when(..) => out.write_error(
628                &kw_span,
629                "internal error - misplaced ${when } detected too late!",
630            ),
631            SD::If(conds, ..) => conds.expand(gctx, out)?,
632            SD::select1(conds, ..) => conds.expand_select1(gctx, out)?,
633            SD::For(repeat, _) => repeat.expand(gctx, out),
634
635            SD::require_beta(..) => {}
636            SD::dbg(ddr) => ddr.expand(gctx, out, kw_span),
637            SD::dbg_all_keywords(_) => dbg_allkw::dump(ctx?),
638
639            // ## maint/check-keywords-documented BoolOnly ##
640            SD::is_struct(bo)
641            | SD::is_enum(bo)
642            | SD::is_union(bo)
643            | SD::v_is_unit(bo)
644            | SD::v_is_tuple(bo)
645            | SD::v_is_named(bo)
646            | SD::is_empty(bo, _)
647            | SD::approx_equal(bo, _)
648            | SD::False(bo)
649            | SD::True(bo)
650            | SD::not(_, bo)
651            | SD::any(_, bo)
652            | SD::all(_, bo) => out.append_bool_only(bo),
653        };
654        Ok(())
655    }
656}
657
658impl<O: ExpansionOutput> DbgDumpRequest<O> {
659    fn expand<'c>(&self, ctx: GeneralContext<'c>, out: &mut O, kw_span: Span) {
660        let desc =
661            format!("derive-deftly dbg dump {}", self.display_heading(ctx),);
662
663        let mut msg = String::new();
664        let () = self.content_string;
665        writeln!(
666            msg, //
667            r#"---------- {} expansion (start) ----------"#,
668            desc,
669        )
670        .expect("write to String failed");
671
672        out.dbg_expand(kw_span, ctx, &mut msg, &self.content_parsed)
673            .expect("write dbg expansion info failed");
674
675        writeln!(
676            msg,
677            r#"
678---------- {} expansion (end) ----------"#,
679            desc
680        )
681        .expect("write to String failed");
682
683        eprint!("{}", msg);
684    }
685}
686
687impl ExplicitError {
688    pub fn throw<'c, T>(
689        &self,
690        gctx: GeneralContext<'c>,
691        kw_span: Span,
692    ) -> Result<T, syn::Error> {
693        let mut message = concat::Accumulator::new_with_span(kw_span);
694        self.message.expand(gctx, &mut message);
695        let message = message.finish_literal()?;
696
697        let explicit_loc = self
698            .span
699            .as_ref()
700            .map(|(span, _)| {
701                let span = span.span_from_arg(gctx)?;
702                let frag = (|| {
703                    let ctx = gctx.full_ctx_raw().ok()?;
704                    let templ = ctx.template_name?.to_token_stream();
705                    Some(format!("reported by {}", templ))
706                })()
707                .unwrap_or_else(|| format!("reported by template"));
708                Ok::<_, syn::Error>((span, frag))
709            })
710            .transpose()?;
711
712        let message_loc = (message.span(), "template error message");
713        let locs = chain!(
714            explicit_loc.as_ref().map(|(s, f)| (*s, &**f)), //
715            gctx.error_loc(),
716            [message_loc],
717        )
718        .collect_vec();
719
720        Err(locs.error(message.value()))
721    }
722}
723
724impl DefinitionName {
725    fn lookup_expand<'c, O: ExpansionOutput>(
726        &self,
727        ctx: GeneralContext<'c>,
728        out: &mut O,
729    ) -> syn::Result<()> {
730        let (def, ctx) = ctx.find_definition(self)?.ok_or_else(|| {
731            self.error(format!("user-defined expansion `{}` not found", self))
732        })?;
733
734        let not_in_paste = || {
735            O::not_in_paste(self).map_err(|mut unpasteable| {
736                unpasteable.combine(def.body_span.error(
737 "user-defined expansion is not pasteable because it isn't, itself, ${paste }"
738                ));
739                unpasteable
740            })
741        };
742        let not_in_concat = || {
743            O::not_in_concat(self).map_err(|mut unconcatable| {
744                unconcatable.combine(def.body_span.error(
745"user-defined expansion is not concatenable because it isn't, itself, ${concat } or ${paste }"
746                ));
747                unconcatable
748            })
749        };
750        let ctx = ctx.as_ref();
751
752        match &def.body {
753            DefinitionBody::Paste(content) => {
754                paste::expand(ctx, def.body_span, content, out)?;
755            }
756            DefinitionBody::Concat(content) => {
757                let mut inner =
758                    concat::Accumulator::new_with_span(def.body_span);
759                content.expand(ctx, &mut inner);
760                inner.finish_onto(&not_in_paste()?, out);
761            }
762            DefinitionBody::Normal(content) => {
763                let ok = (not_in_paste()?, not_in_concat()?);
764                out.append_tokens_with(&ok, |out| {
765                    content.expand(ctx, out);
766                    Ok(())
767                })?;
768            }
769        }
770        Ok(())
771    }
772}
773
774impl RawAttr {
775    fn expand(
776        &self,
777        ctx: &Context,
778        out: &mut TokenAccumulator,
779        attrs: &[syn::Attribute],
780    ) -> syn::Result<()> {
781        for attr in attrs {
782            match self {
783                RawAttr::Default => {
784                    if ["deftly", "derive_deftly", "derive_deftly_adhoc"]
785                        .iter()
786                        .all(|exclude| !attr.path().is_ident(exclude))
787                    {
788                        out.append(attr);
789                    }
790                }
791                RawAttr::Include { entries } => {
792                    let ent = entries.iter().find(|ent| ent.matches(attr));
793                    if let Some(ent) = ent {
794                        ent.expand(ctx, out, attr)?;
795                    }
796                }
797                RawAttr::Exclude { exclusions } => {
798                    if !exclusions.iter().any(|excl| excl == attr.path()) {
799                        out.append(attr);
800                    }
801                }
802            }
803        }
804        Ok(())
805    }
806}
807
808impl RawAttrEntry {
809    fn matches(&self, attr: &syn::Attribute) -> bool {
810        &self.path == attr.path()
811    }
812
813    fn expand(
814        &self,
815        _ctx: &Context,
816        out: &mut TokenAccumulator,
817        attr: &syn::Attribute,
818    ) -> syn::Result<()> {
819        out.append(attr);
820        Ok(())
821    }
822}
823
824impl<O> ExpandInfallible<O> for RepeatedTemplate<O>
825where
826    Template<O>: ExpandInfallible<O>,
827    O: ExpansionOutput,
828{
829    fn expand<'c>(&self, ctx: GeneralContext<'c>, out: &mut O) {
830        let ctx = match ctx.full_ctx(self.span) {
831            Ok(y) => y,
832            Err(e) => {
833                out.record_error(e.into());
834                return;
835            }
836        };
837
838        // for_with_within expects a fallible closure, but we want to do
839        // infallible work in our infallible context, so we use `Void`
840        // as the error type and wrap each call in `Ok`.
841        #[allow(clippy::unit_arg)] // clippy wants us to worsify the style
842        match self.over {
843            RO::Variants => ctx.for_with_within(|ctx, _: &WithinVariant| {
844                Ok::<_, Void>(self.expand_inner(ctx, out))
845            }),
846            RO::Fields => ctx.for_with_within(|ctx, _: &WithinField| {
847                Ok::<_, Void>(self.expand_inner(ctx, out))
848            }),
849        }
850        .void_unwrap()
851    }
852}
853
854impl<O: ExpansionOutput> RepeatedTemplate<O> {
855    /// private, does the condition
856    fn expand_inner(&self, ctx: &Context, out: &mut O)
857    where
858        Template<O>: ExpandInfallible<O>,
859        O: ExpansionOutput,
860    {
861        let mut ctx = ctx.clone();
862        ctx.within_loop = WithinLoop::When;
863
864        for when in &self.whens {
865            match when.eval_bool(ctx.as_general()) {
866                Ok(true) => continue,
867                Ok(false) => return,
868                Err(e) => {
869                    out.record_error(e);
870                    return;
871                }
872            }
873        }
874
875        ctx.within_loop = WithinLoop::Body;
876        self.template.expand(ctx.as_general(), out)
877    }
878}