derive_deftly_macros/
paste.rs

1//! Implementation of identifier pasting expansion `${paste }`
2
3use super::framework::*;
4
5/// Accumulator for things to be pasted
6///
7/// Implements [`ExpansionOutput`] and [`SubstParseContext`]:
8/// i.e., it corresponds to the lexical context for a `${paste }`,
9/// and collects the identifier fragments being pasted.
10#[derive(Debug)]
11pub struct Items {
12    /// Error reporting for bad identifiers, and also the output span
13    span: Span,
14    items: Vec<Item>,
15    errors: Vec<syn::Error>,
16    atoms: Vec<AtomForReport>,
17}
18
19/// One spanned input element, for use when reporting bad ident errors
20///
21/// We call this an "atom"; in nested pastes,
22/// the atoms are the original input items.
23#[derive(Debug, Clone)]
24pub struct AtomForReport {
25    text: String,
26    span: Span,
27}
28
29#[derive(Debug)]
30/// Entry in a `${paste ...}` or `${CASE ...}`
31///
32/// `te_span` is the span of this item entry in the template.
33/// It is used for error reporting if we can't cope with this entry
34/// (for example, if we have multiple nontrivial entries.)
35enum Item {
36    Plain {
37        text: String,
38        span: Option<Span>,
39    },
40    Complex {
41        surround: Surround,
42        text: String,
43        te_span: Span,
44    },
45}
46
47#[derive(Debug)]
48struct Surround {
49    pre: TokenStream,
50    post: TokenStream,
51    grouping: Grouping,
52}
53
54//---------- IdentFrag ----------
55
56/// Uninhabited "bad identifier" error for conversions from already-tokens
57#[derive(Copy, Clone, Debug)]
58pub struct IdentFragInfallible(pub Void);
59
60impl From<IdentFragInfallible> for syn::Error {
61    fn from(i: IdentFragInfallible) -> syn::Error {
62        void::unreachable(i.0)
63    }
64}
65
66impl IdentFragInfallible {
67    pub fn unreachable(&self) -> ! {
68        void::unreachable(self.0)
69    }
70}
71
72/// For use with `ExpansionOutput.append_identfrag_toks` etc.
73///
74/// Sort of like `quote::IdentFragment`.
75/// But:
76///
77///  * Strings available directly, not via the inconvenient `fmt`,
78///  * identifier construction doesn't involve `format_ident!` and panics.
79///  * `paste::InputAtom` passed through separately,
80///
81/// The main purpose of this trait is to allow deferral of errors
82/// from constructing bad identifiers.  Some *inputs* (implementors
83/// of `IdentFrag`, typically those which are already tokens) can
84/// infallibly be appended as tokens.
85///
86/// But paste results aren't converted to identifiers until the last
87/// moment: they can'tbed converted infallibly, and the error surfaces
88/// in `convert_to_ident`.
89///
90/// The framework methods `append_*ident*` propagate any error to the
91/// call site.  Call sites which pass already-tokens can just use `?`
92/// to convert the uninhabited error to `syn::Error` - or they can
93/// use `IdentFragInfallible::unreachable`.
94///
95/// Call sites which pass actually-fallible content (ie, paste results)
96/// end up with a `syn::Error`.
97pub trait IdentFrag: Spanned {
98    type BadIdent: Clone;
99
100    /// (Try to) convert to tokens (ie, real `Ident`)
101    ///
102    /// Depending on the implementor, this might be fallible, or not.
103    fn frag_to_tokens(
104        &self,
105        out: &mut TokenStream,
106    ) -> Result<(), Self::BadIdent>;
107
108    /// The fragment as a string
109    fn fragment(&self) -> String;
110
111    /// Transfer information about the atoms in this `IdentFrag` into `out`
112    ///
113    /// If, ultimately, a bad identifier is constructed using
114    /// some of this input,
115    /// these atoms will be reported.
116    fn note_atoms(&self, out: &mut Vec<AtomForReport>) {
117        out.push(AtomForReport {
118            text: self.fragment(),
119            span: self.span(),
120        });
121    }
122}
123
124impl<T: ToTokens + quote::IdentFragment + Display> IdentFrag for T {
125    type BadIdent = IdentFragInfallible;
126    fn frag_to_tokens(
127        &self,
128        out: &mut TokenStream,
129    ) -> Result<(), IdentFragInfallible> {
130        Ok(self.to_tokens(out))
131    }
132
133    fn fragment(&self) -> String {
134        let s = self.to_string();
135        if let Some(s) = s.strip_prefix("r#") {
136            s.to_string()
137        } else {
138            s
139        }
140    }
141}
142
143//---------- restricted case conversions ----------
144
145/// Token that an associated `ChangeCase` has been checked for legal context
146///
147/// Really this ought to be a field in each variant of `ChangeCase<O>`
148/// but that would make lots of pointless generics.
149///
150/// The keyword-specific part of the check is just
151/// an avoidance of user error, anyway.
152#[derive(Debug)]
153pub struct ChangeCaseOkIn<O: SubstParseContext> {
154    pub not_in_bool: O::NotInBool,
155    #[allow(dead_code)]
156    paste_context_checked: (),
157}
158
159impl<O: SubstParseContext> ChangeCaseOkIn<O> {
160    pub fn new_checked(case: ChangeCase, span: Span) -> syn::Result<Self> {
161        let not_in_bool = O::not_in_bool(&span)?;
162
163        let paste_context_checked = case
164            .ok_in_paste()
165            // Otherwise, it's a ${concat }-only case change keyword (& beta)
166            .or_else(|()| {
167                let _: O::ConcatOnly = O::concat_only(&span)?;
168                let _: beta::Enabled = beta::Enabled::new_for_syntax(span)?;
169                Ok::<_, syn::Error>(())
170            })?;
171
172        Ok(ChangeCaseOkIn {
173            not_in_bool,
174            paste_context_checked,
175        })
176    }
177}
178
179//---------- case conversion ----------
180
181/// Define cases using heck
182///
183/// heck doesn't provide standard names for case conversions,
184/// or an enum to represent a case conversion.
185/// This macro defines both.  Specifically:
186///  * The fieldless enum `ChangeCase`.
187///  * Its `FromStr` implementation, which accepts the `$keyword`s.
188///  * Its `apply()` method, which actually performs the conversion.
189///
190/// `$heck` is the name of the `As` conversion struct in the heck
191/// API.  It will become the variant name in `ChangeCase`.
192///
193/// `$keyword` are the keywords we recognise for this case conversion.
194macro_rules! define_cases { {
195    $(
196        $heck:ident $( $keyword:literal )* $in_paste:ident,
197    )*
198} => {
199    #[derive(Debug, Clone, Copy)]
200    pub enum ChangeCase {
201      $(
202        #[cfg(feature = "case")]
203        $heck,
204      )*
205    }
206
207    impl FromStr for ChangeCase {
208        type Err = ();
209        #[allow(unreachable_code)]
210        fn from_str(s: &str) -> Result<Self, ()> {
211            Ok(match s {
212              $(
213                $( $keyword )|* => {
214                    // Minimal build, so no need to bother with a proper error.
215                    #[cfg(not(feature = "case"))]
216                    panic!(
217 "case changing not supported, enable `case` feature of `derive-deftly`"
218                    );
219
220                    #[cfg(feature = "case")]
221                    ChangeCase::$heck
222                },
223              )*
224                _ => return Err(()),
225            })
226        }
227    }
228
229    impl ChangeCase {
230        fn apply(self, #[allow(unused)] input: &str) -> String {
231            match self {
232              $(
233                #[cfg(feature = "case")]
234                ChangeCase::$heck => heck::$heck(input).to_string(),
235              )*
236            }
237        }
238
239        fn ok_in_paste(self) -> Result<(), ()> {
240            match self {
241              $(
242                #[cfg(feature = "case")]
243                ChangeCase::$heck => $in_paste(()),
244              )*
245            }
246        }
247    }
248} }
249
250define_cases! {
251    // heck API       our keyword (and aliases)  our example(s)
252    AsUpperCamelCase    "pascal_case"         "upper_camel_case"   Ok,
253    AsLowerCamelCase    "lower_camel_case"                         Ok,
254    AsSnakeCase         "snake_case"                               Ok,
255    AsShoutySnakeCase   "shouty_snake_case"                        Ok,
256    AsKebabCase         "kebab_case"                               Err,
257    AsShoutyKebabCase   "shouty_kebab_case"                        Err,
258    AsTitleCase         "title_case"                               Err,
259    AsTrainCase         "train_case"                               Err,
260}
261
262//---------- TokenPastesAsIdent ----------
263
264/// For use with `ExpansionOutput.push_identfrag_toks`
265///
266/// Will then write out `T` as its tokens.
267/// In identifier pasting, converts the tokens to a string first
268/// (so they had better be identifiers, or ident fragments).
269pub struct TokenPastesAsIdent<T>(pub T);
270
271impl<T: ToTokens> Spanned for TokenPastesAsIdent<T> {
272    fn span(&self) -> Span {
273        self.0.span()
274    }
275}
276
277impl<T: ToTokens> IdentFrag for TokenPastesAsIdent<T> {
278    type BadIdent = IdentFragInfallible;
279
280    fn frag_to_tokens(
281        &self,
282        out: &mut TokenStream,
283    ) -> Result<(), Self::BadIdent> {
284        Ok(self.0.to_tokens(out))
285    }
286
287    fn fragment(&self) -> String {
288        self.0.to_token_stream().to_string()
289    }
290}
291
292//---------- implementation ----------
293
294impl Items {
295    pub fn new(span: Span) -> Self {
296        Items {
297            span,
298            items: vec![],
299            errors: vec![],
300            atoms: vec![],
301        }
302    }
303}
304
305/// Core of the results from mk_ident
306#[derive(Debug)]
307struct Pasted {
308    /// String if we're to make an identifier, or for more pasting
309    whole: String,
310    /// Span if we're to make an identifier
311    span: Span,
312    /// What to consider complaining about if we can't make an identifier
313    atoms: Vec<AtomForReport>,
314}
315
316impl Spanned for Pasted {
317    fn span(&self) -> Span {
318        self.span
319    }
320}
321impl IdentFrag for Pasted {
322    type BadIdent = syn::Error;
323
324    fn fragment(&self) -> String {
325        self.whole.clone()
326    }
327
328    fn note_atoms(&self, atoms: &mut Vec<AtomForReport>) {
329        atoms.extend(self.atoms.iter().cloned())
330    }
331
332    fn frag_to_tokens(&self, out: &mut TokenStream) -> syn::Result<()> {
333        let ident = convert_to_ident(self)?;
334        ident.to_tokens(out);
335        Ok(())
336    }
337}
338
339/// Element of input to `mk_ident`: one bit of the leaf identifier
340///
341/// This is, essentially, the part of an `Item` which contributes to the
342/// pasting.
343type Piece<'i> = (&'i str, Option<Span>);
344
345enum AssemblyInstruction {
346    Plain(Pasted),
347    Complex {
348        tspan: Span,
349        surround: Surround,
350        ident: Pasted,
351    },
352}
353
354use AssemblyInstruction as AI;
355
356/// Make a leaf identifier out of pieces
357///
358/// Actually pastes together the pieces.
359///
360/// Any surrounding path elements, generics, etc., of a nontrivial
361/// expansion, are handled by `assemble`, not here.
362fn mk_ident<'i>(
363    span: Span,
364    change_case: Option<ChangeCase>,
365    pieces: impl Iterator<Item = Piece<'i>> + Clone,
366    atoms: Vec<AtomForReport>,
367) -> Pasted {
368    let whole = pieces.clone().map(|i| i.0).collect::<String>();
369    let whole = if let Some(change_case) = change_case {
370        change_case.apply(&whole)
371    } else {
372        whole
373    };
374    Pasted { span, whole, atoms }
375}
376
377/// Error that stands in for the `syn::Error` from an invalid identifier
378///
379/// We don't expose this outside this module.
380/// It's used internally in `convert_to_ident`, and in tests.
381#[derive(Eq, PartialEq, Debug)]
382struct InvalidIdent;
383
384/// Obtain an actual `syn::Ident` from the results of pasting
385//
386/// The meat of `<Pasted as IdentFrag>::frag_to_tokens`.
387/// Split off largely to save on rightward drift.
388fn convert_to_ident(pasted: &Pasted) -> syn::Result<syn::Ident> {
389    // Try to make an identifier from a string
390    //
391    // `format_ident!` and `Ident::new` and so on all panic if the
392    // identifier is invalid.  That's quite inconvenient.  In particular,
393    // it can result in tests spewing junk output with RUST_BACKTRACE=1.
394    //
395    // syn::parse_str isn't perfect either:
396    //
397    // 1. It accepts whitespace and perhaps other irregularities.
398    //    We want to accept only precisely the identifier string.
399    //
400    // 2. It generates random extra errors, via some out of band means,
401    //    if the string can't be tokenised.
402    //    Eg, `<proc_macro2::TokenStream as FromStr>::parse("0_end")`
403    //    generates a spurious complaint to stderr as well as
404    //    a strange OK result containing a literal.
405    //    This doesn't matter very much for our purposes because we
406    //    never try to completely *swallow* a bad identifier error -
407    //    we always surface an error of our own, and the extra one
408    //    from parse_str is tolerable.
409    //
410    // 3. The syn::Error from an invalid identifier is not very illuminating.
411    //    So we discard it, and replace it with our own.
412    //
413    // 4. Just parsing Ident won't accept keywords.  We could use
414    //    IdentAny but that would give us keywords *as non-raw identifiers*
415    //    but we need *raw* identifiers if the string was a keyword:
416    //    i.e., in that case we want a raw identifier instead.
417    //    (This can happen if pasting or case changing generates a keyword,
418    //    or if a raw identifier euqal to a keyword is pasted with nothing.)
419    let mut ident = (|| {
420        let s = &pasted.whole;
421
422        let prefixed;
423        let (ident, comparator) = match syn::parse_str::<Ident>(s) {
424            Ok(ident) => {
425                // parse_str thought it was a valid identifier as-is
426                (ident, s)
427            }
428            Err(_) => {
429                // Problem 4 (needs raw) has arisen maybe?
430                prefixed = format!("r#{}", s);
431                let ident = syn::parse_str::<Ident>(&prefixed)
432                    // Oh, it doesn't parse this way either, bail
433                    .map_err(|_| InvalidIdent)?;
434                (ident, &prefixed)
435            }
436        };
437
438        // Check for problem 1 (accepting extraneous spaces etc.)
439        if &ident.to_string() != comparator {
440            return Err(InvalidIdent);
441        }
442
443        Ok(ident)
444    })()
445    .map_err(|_| {
446        // Make our own error (see problem 3 above)
447        let mut err = pasted.span.error(format_args!(
448            "constructed identifier {:?} is invalid",
449            &pasted.whole,
450        ));
451        // We want to show the user where the bad part is.  In
452        // particular, if it came from somewhere nontrivial like an
453        // ${Xmeta}.  But, we don't want to dump one error for every
454        // input, because most of them will be harmless fixed
455        // identifiers, in the template right next to the ${paste}.
456        // So, try out each input bit and see if it would make an
457        // identifier by itself.
458        for (
459            AtomForReport {
460                text: piece,
461                span: pspan,
462            },
463            pfx,
464        ) in izip!(
465            &pasted.atoms,
466            // The first entry must be valid as an identifier start.
467            // The subsequent entries, we prepend with "X".  If the first
468            // entry was empty, that would be reported too.  This may
469            // mean we make more reports than needed, which is why we say
470            // "probably".
471            chain!(iter::once(""), iter::repeat("X")),
472        ) {
473            // We accept keywords.  If the problem was that the output
474            // was a keyword because one of the inputs was, we hope that
475            // this is because one of the other inputs was empty.
476            //
477            // If the output was a keyword for some other reason, it
478            // probably means the identifier construction scheme is
479            // defective and hopefully the situation will be obvious to
480            // the user.
481            match syn::parse_str(&format!("{}{}", pfx, piece)) {
482                Ok::<IdentAny, _>(_) => {}
483                Err(_) => err
484                    .combine(pspan.error(
485                        "probably-invalid input to identifier pasting",
486                    )),
487            }
488        }
489        err
490    })?;
491
492    ident.set_span(pasted.span);
493    Ok(ident)
494}
495
496#[test]
497fn ident_from_str() {
498    let span = Span::call_site();
499    let chk = |s: &str, exp: Result<&str, _>| {
500        let p = Pasted {
501            whole: s.to_string(),
502            span,
503            atoms: vec![],
504        };
505        let parsed = convert_to_ident(&p)
506            .map(|i| i.to_string())
507            .map_err(|_| InvalidIdent);
508        let exp = exp.map(|i| i.to_string());
509        assert_eq!(parsed, exp);
510    };
511    let chk_ok = |s| chk(s, Ok(s));
512    let chk_err = |s| chk(s, Err(InvalidIdent));
513
514    chk("for", Ok("r#for"));
515    chk_ok("r#for");
516    chk_ok("_thing");
517    chk_ok("thing_");
518    chk_ok("r#raw");
519    chk_err("");
520    chk_err("a b");
521    chk_err("spc ");
522    chk_err(" spc");
523    chk_err("r#a spc");
524    chk_err(" r#a ");
525    chk_err(" r#for ");
526    chk_err("r#r#doubly_raw");
527    chk_err("0");
528}
529
530pub fn expand<'c>(
531    ctx: GeneralContext<'c>,
532    tspan: Span,
533    content: &Template<paste::Items>,
534    out: &mut impl ExpansionOutput,
535) -> syn::Result<()> {
536    let mut items = paste::Items::new(tspan);
537    content.expand(ctx, &mut items);
538    items.assemble(out, None)
539}
540
541impl Items {
542    fn append_atom(&mut self, item: Item) {
543        match &item {
544            Item::Plain {
545                text,
546                span: Some(span),
547                ..
548            }
549            | Item::Complex {
550                text,
551                te_span: span,
552                ..
553            } => {
554                self.atoms.push(AtomForReport {
555                    text: text.clone(),
556                    span: *span,
557                });
558            }
559            Item::Plain { span: None, .. } => {}
560        };
561        self.items.push(item);
562    }
563    fn append_item_raw(&mut self, item: Item) {
564        self.items.push(item);
565    }
566    /// Append a plain entry from something `Display`
567    ///
568    /// Like `ExpansionOutput::append_display` but doesn't need `Spanned`
569    fn append_plain<V: Display>(&mut self, span: Span, v: V) {
570        self.append_atom(Item::Plain {
571            text: v.to_string(),
572            span: Some(span),
573        })
574    }
575    pub fn append_fixed_string(&mut self, text: &'static str) {
576        self.append_atom(Item::Plain {
577            text: text.into(),
578            span: None,
579        })
580    }
581
582    /// Combine the accumulated pieces and append them to `out`
583    ///
584    /// Calls
585    /// [`append_idpath`](ExpansionOutput::append_idpath)
586    /// if the content contained a nontrivial expansion
587    /// or
588    /// [`append_identfrag_toks`](ExpansionOutput::append_identfrag_toks)
589    /// otherwise.
590    pub fn assemble(
591        self,
592        out: &mut impl ExpansionOutput,
593        change_case: Option<ChangeCase>,
594    ) -> syn::Result<()> {
595        if !self.errors.is_empty() {
596            for error in self.errors {
597                out.record_error(error);
598            }
599            return Ok(());
600        }
601
602        match Self::assemble_inner(
603            self.span,
604            self.items,
605            change_case,
606            self.atoms,
607        )? {
608            AI::Plain(ident) => out.append_identfrag_toks(
609                &ident, //
610            )?,
611            AI::Complex {
612                tspan,
613                surround,
614                ident,
615            } => out.append_idpath(
616                tspan,
617                |ta| ta.append(surround.pre),
618                &ident,
619                |ta| ta.append(surround.post),
620                surround.grouping,
621            )?,
622        }
623
624        Ok(())
625    }
626
627    /// Combine the accumulated pieces and say what to do
628    ///
629    /// Inner, non-monomorphised, function for [`Items::assemble`].
630    ///
631    /// Returns `Right` with values to pass to
632    /// [`append_idpath`](ExpansionOutput::append_idpath)
633    /// or
634    /// `Left` with the value to pass to
635    /// [`append_identfrag_toks`](ExpansionOutput::append_identfrag_toks).
636    fn assemble_inner(
637        tspan: Span,
638        items: Vec<Item>,
639        change_case: Option<ChangeCase>,
640        atoms: Vec<AtomForReport>,
641    ) -> syn::Result<AssemblyInstruction> {
642        // We must always use a similar span when we emit identifiers
643        // that are going to be used to bind variables, or the hygiene
644        // system doesn't think they're the same identifier.
645        //
646        // We choose the template keyword span for this.
647        // (The span of `paste` in `${paste ...}`).
648        // This isn't perfect, since we might want to point at the driver,
649        // but we don't always have a suitable driver span.
650        //
651        // This applies to `fpatname`, `vpat`, and so on, too.
652        //
653        // We don't apply to fname too.  The template author ought to
654        // us $vpat to bind fields, not $fname, since $fname risks clashes
655        // with other variables that might be in scope.  But the rustc error
656        // messages for identifiers with the wrong span are rather poor.
657        let out_span = tspan;
658
659        let nontrivial = items
660            .iter()
661            .enumerate()
662            .filter_map(|(pos, it)| match it {
663                Item::Plain { .. } => None,
664                Item::Complex { te_span, .. } => Some((pos, te_span)),
665            })
666            .at_most_one()
667            .map_err(|several| {
668                // Report one error for each nontrivial expansion
669                let mut several = several.map(|(_pos, span)| {
670                    span.error("multiple nontrivial entries in ${paste ...}")
671                });
672                let mut collect = several.next().unwrap();
673                collect.extend(several);
674                collect
675            })?
676            .map(|(pos, _)| pos);
677
678        fn plain_strs(
679            items: &[Item],
680        ) -> impl Iterator<Item = Piece<'_>> + Clone {
681            items.iter().map(|item| match item {
682                Item::Plain { text, span } => (text.as_str(), *span),
683                _ => panic!("non plain item"),
684            })
685        }
686
687        if let Some(nontrivial) = nontrivial {
688            // We decompose `items` into `items_before`, `items_after`,
689            // and `items_nontrivial`.  There's a bit of shuffling
690            // to avoid reallocating the array while still providing
691            // an owned `items_nontrivial` that we can move out of.
692            let mut items = items;
693            let item_nontrivial = {
694                let dummy_item = Item::Plain {
695                    text: String::new(),
696                    span: None,
697                };
698                mem::replace(&mut items[nontrivial], dummy_item)
699            };
700            let items_before = &items[0..nontrivial];
701            let items_after = &items[nontrivial + 1..];
702
703            let mk_ident_nt = |(text, txspan): Piece, atoms| {
704                mk_ident(
705                    out_span,
706                    change_case,
707                    chain!(
708                        plain_strs(items_before),
709                        iter::once((text, txspan)),
710                        plain_strs(items_after),
711                    ),
712                    atoms,
713                )
714            };
715
716            match item_nontrivial {
717                Item::Complex {
718                    surround,
719                    text,
720                    te_span,
721                } => {
722                    return Ok(AI::Complex {
723                        tspan,
724                        surround,
725                        ident: mk_ident_nt((&text, Some(te_span)), atoms),
726                    })
727                }
728                Item::Plain { .. } => panic!("trivial nontrivial"),
729            }
730        } else {
731            return Ok(AI::Plain(
732                mk_ident(out_span, change_case, plain_strs(&items), atoms), //
733            ));
734        }
735    }
736}
737
738impl SubstParseContext for Items {
739    type NotInPaste = Void;
740    type NotInConcat = ();
741    type NotInBool = ();
742    type BoolOnly = Void;
743    type ConcatOnly = Void;
744    type DbgContent = Template<Self>;
745
746    fn not_in_bool(_: &impl Spanned) -> syn::Result<()> {
747        Ok(())
748    }
749    fn not_in_concat(_: &impl Spanned) -> syn::Result<()> {
750        Ok(())
751    }
752
753    fn not_in_paste(span: &impl Spanned) -> syn::Result<Void> {
754        Err(span
755            .error("not allowed in within ${paste ...} (or case_changing)"))
756    }
757
758    type SpecialParseContext = Option<AngleBrackets>;
759}
760
761impl SpecialParseContext for Option<AngleBrackets> {
762    fn before_element_hook(
763        &mut self,
764        input: ParseStream,
765    ) -> syn::Result<Option<SpecialInstructions>> {
766        if input.peek(Token![>]) {
767            if let Some(state) = self {
768                let _: Token![>] = input.parse()?;
769                state.found_close = true;
770                return Ok(Some(SpecialInstructions::EndOfTemplate));
771            } else {
772                return Err(
773                    input.error("stray > within curly-bracketed ${paste }")
774                );
775            }
776        }
777        return Ok(None);
778    }
779}
780
781/// Parsing state for `$<...>`
782#[derive(Default)]
783pub struct AngleBrackets {
784    found_close: bool,
785}
786
787impl AngleBrackets {
788    pub fn finish(self, start_span: Span) -> syn::Result<()> {
789        if !self.found_close {
790            return Err(
791                start_span.error("unmatched paste $< start - missing >")
792            );
793        }
794        Ok(())
795    }
796}
797
798impl ExpansionOutput for Items {
799    fn append_identfrag_toks<I: IdentFrag>(
800        &mut self,
801        ident: &I,
802    ) -> Result<(), I::BadIdent> {
803        ident.note_atoms(&mut self.atoms);
804        self.append_plain(ident.span(), ident.fragment());
805        Ok(())
806    }
807    fn append_idpath<A, B, I>(
808        &mut self,
809        te_span: Span,
810        pre_: A,
811        ident: &I,
812        post_: B,
813        grouping: Grouping,
814    ) -> Result<(), I::BadIdent>
815    where
816        A: FnOnce(&mut TokenAccumulator),
817        B: FnOnce(&mut TokenAccumulator),
818        I: IdentFrag,
819    {
820        let mut pre = TokenAccumulator::new();
821        pre_(&mut pre);
822        let mut post = TokenAccumulator::new();
823        post_(&mut post);
824        let text = ident.fragment();
825        let mut handle_err = |prepost: TokenAccumulator| {
826            prepost.tokens().unwrap_or_else(|err| {
827                self.record_error(err);
828                TokenStream::new()
829            })
830        };
831        let pre = handle_err(pre);
832        let post = handle_err(post);
833        ident.note_atoms(&mut self.atoms);
834        let surround = Surround {
835            pre,
836            post,
837            grouping,
838        };
839        self.append_item_raw(Item::Complex {
840            surround,
841            text,
842            te_span,
843        });
844        Ok(())
845    }
846    fn append_syn_litstr(&mut self, lit: &syn::LitStr) {
847        self.append_plain(lit.span(), lit.value());
848    }
849    fn append_syn_type_inner(
850        &mut self,
851        te_span: Span,
852        ty: syn::Type,
853        grouping: Grouping,
854    ) {
855        (|| {
856            match ty {
857                syn::Type::Path(mut path) => {
858                    let (last_segment, last_punct) = path
859                        .path
860                        .segments
861                        .pop()
862                        .ok_or_else(|| {
863                            te_span.error(
864                                "derive-deftly token pasting applied to path with no components",
865                            )
866                        })?
867                        .into_tuple();
868                    let syn::PathSegment { ident, arguments } = last_segment;
869                    let mut pre = TokenStream::new();
870                    path.to_tokens(&mut pre);
871                    let text = ident.to_string();
872                    let mut post = TokenStream::new();
873                    arguments.to_tokens(&mut post);
874                    last_punct.to_tokens(&mut post);
875                    let surround = Surround { pre, post, grouping };
876                    let item = Item::Complex {
877                        surround,
878                        text,
879                        te_span,
880                    };
881                    self.append_atom(item)
882                }
883                x => {
884                    return Err(x.error(
885                        "derive-deftly macro wanted to do identifier pasting, but complex type provided",
886                    ))
887                }
888            }
889            Ok::<_, syn::Error>(())
890        })()
891        .unwrap_or_else(|e| self.record_error(e));
892    }
893    fn append_tokens_with(
894        &mut self,
895        (not_in_paste, _not_in_compat): &(Void, ()),
896        _: impl FnOnce(&mut TokenAccumulator) -> syn::Result<()>,
897    ) -> syn::Result<()> {
898        void::unreachable(*not_in_paste)
899    }
900
901    fn append_bool_only(&mut self, bool_only: &Self::BoolOnly) -> ! {
902        void::unreachable(*bool_only)
903    }
904
905    fn record_error(&mut self, err: syn::Error) {
906        self.errors.push(err);
907    }
908
909    fn new_with_span(kw_span: Span) -> Self {
910        Self::new(kw_span)
911    }
912
913    fn default_subst_meta_as(_: Span) -> syn::Result<meta::SubstAs<Self>> {
914        Ok(meta::SubstAs::str(()))
915    }
916
917    fn ignore_impl(self) -> syn::Result<()> {
918        let Items {
919            span: _,
920            items: _,
921            atoms: _,
922            errors,
923        } = self;
924        let mut accum = ErrorAccumulator::default();
925        for e in errors {
926            accum.push(e);
927        }
928        accum.finish()
929    }
930
931    fn dbg_expand<'c>(
932        &mut self,
933        kw_span: Span,
934        ctx: GeneralContext<'c>,
935        msg: &mut String,
936        content: &Template<Items>,
937    ) -> fmt::Result {
938        let mut child = Items::new(kw_span);
939        content.expand(ctx, &mut child);
940        let Items {
941            span: _,
942            items,
943            errors,
944            atoms: _,
945        } = child;
946
947        for e in &errors {
948            writeln!(msg, "ERROR: {}", e)?;
949        }
950        for (sep, i) in izip!(
951            chain!([""], iter::repeat(" ")), //
952            &items,
953        ) {
954            write!(msg, "{}", sep)?;
955            match i {
956                Item::Plain { text, .. } => write!(msg, "{:?}", text)?,
957                Item::Complex { surround, text, .. } => {
958                    write!(
959                        msg,
960                        "{:?}+{:?}+{:?}",
961                        surround.pre.to_string(),
962                        text,
963                        surround.post.to_string(),
964                    )?;
965                }
966            }
967        }
968
969        self.items.extend(items);
970        self.errors.extend(errors);
971        Ok(())
972    }
973}
974
975impl Expand<Items> for TemplateElement<Items> {
976    fn expand<'c>(
977        &self,
978        ctx: GeneralContext<'c>,
979        out: &mut Items,
980    ) -> syn::Result<()> {
981        match self {
982            TE::Ident(ident, ..) => out.append_identfrag_toks(&ident)?,
983            TE::LitStr(lit) => out.append_syn_litstr(&lit),
984            TE::Subst(e) => e.expand(ctx, out)?,
985            TE::Repeat(e) => e.expand(ctx, out),
986            TE::Literal(_, allow_tokens)
987            | TE::Punct(_, allow_tokens)
988            | TE::Group { allow_tokens, .. } => {
989                void::unreachable(allow_tokens.0)
990            }
991        }
992        Ok(())
993    }
994}