type_sitter_gen/queries/
print.rs

1use crate::anon_unions::AnonUnions;
2use crate::mk_syntax::{concat_doc, ident, lit_array, lit_str, modularize};
3use crate::queries::sexp::SExpSeq;
4use crate::queries::sexp_node_type::SExpNodeType;
5use crate::queries::GeneratedQueryTokens;
6use crate::NodeType;
7use crate::PrintCtx;
8use crate::{sexp_name_to_rust_names, unmake_reserved};
9use proc_macro2::TokenStream;
10use quote::{format_ident, quote};
11use slice_group_by::GroupBy;
12use syn::{Ident, Path};
13use tree_sitter::CaptureQuantifier;
14
15impl<'tree> SExpSeq<'tree> {
16    /// Generate the AST for the typed wrapper of the given query.
17    ///
18    /// # Parameters
19    /// - `ts_query`: The tree-sitter version of the query at compile time
20    /// - `def_ident`: Identifier used for the query definition
21    /// - `language_ident`: Identifier used for the language to create the query for
22    /// - `disable_patterns`: List of patterns to ignore on the query
23    /// - `disabled_capture_names`: List of capture names to ignore on the query
24    /// - `disabled_capture_idxs`: List of capture indices to ignore on the query (both these and
25    ///   all indices with names in `disabled_capture_names` are disabled)
26    /// - `nodes`: Path to the crate with the typed node wrappers. Typically
27    ///   [`type_sitter_gen::super_nodes`]
28    /// - `use_yak_sitter`: Whether to use `yak_sitter` or `tree_sitter`
29    /// - `ctx.all_types`: Map of node type names to their types.
30    /// - `ctx.tree_sitter`: Path to the crate with the tree-sitter API. For cli-generated sources,
31    ///    use [`crate::tree_sitter`] if `use_yak_sitter` is false or [`crate::yak_sitter`] if
32    ///    `use_yak_sitter` is true. For proc-macro generated sources, use
33    ///    [`crate::type_sitter_raw`] either way.
34    /// - `ctx.type_sitter_lib`: Path to the crate with the type-sitter API. For cli-generated
35    ///   sources, use [`crate::type_sitter_lib`]. For proc-macro generated sources, use
36    ///   [`crate::type_sitter`].
37    /// - `anon_unions`: Anonymous unions for query capture type
38    pub(crate) fn print(
39        &self,
40        query_str: &str,
41        ts_query: tree_sitter::Query,
42        def_ident: &Ident,
43        language_ident: &syn::Ident,
44        disabled_patterns: &[&str],
45        disabled_capture_names: &[&str],
46        disabled_capture_idxs: &[usize],
47        nodes: &Path,
48        use_yak_sitter: bool,
49        ctx @ PrintCtx {
50            tree_sitter,
51            type_sitter_lib,
52            ..
53        }: PrintCtx,
54        anon_unions: &mut AnonUnions,
55    ) -> TokenStream {
56        let disabled_captures = disabled_capture_idxs
57            .iter()
58            .copied()
59            .chain(disabled_capture_names.iter().flat_map(|&name| {
60                ts_query
61                    .capture_names()
62                    .iter()
63                    .enumerate()
64                    .filter(move |(_, n)| **n == name)
65                    .map(|(idx, _)| idx)
66            }))
67            .collect::<Vec<_>>();
68        let capture_idxs_and_names = ts_query
69            .capture_names()
70            .iter()
71            .enumerate()
72            .filter(|(capture_idx, _)| !disabled_captures.contains(capture_idx))
73            .collect::<Vec<_>>();
74        let (capture_idxs, capture_names) = capture_idxs_and_names
75            .iter()
76            .map(|(idx, name)| (*idx, **name))
77            .unzip::<_, _, Vec<_>, Vec<_>>();
78
79        let def_name = def_ident.to_string();
80        let language_name = language_ident.to_string();
81        let query_parse_error = lit_str(&format!(
82            "query parsed at compile-time but failed at runtime. Is the language '{}' correct, and did \
83            you use the same tree-sitter / {} version?",
84            language_name, language_name
85        ));
86        let internal_query = format_ident!("__{}__", def_ident);
87        let query_matches = format_ident!("{}Matches", def_ident);
88        let query_captures = format_ident!("{}Captures", def_ident);
89        let query_match = format_ident!("{}Match", def_ident);
90        let query_capture = format_ident!("{}Capture", def_ident);
91        let disabled_patterns = disabled_patterns.iter().map(|p| lit_str(p));
92        let full_query_documentation = format!("\n\n```sexp\n{}\n```", query_str);
93        let def_doc = concat_doc!("Typed version of the query:", full_query_documentation);
94        let matches_doc = concat_doc!(
95            "Matches returned by a query cursor running the query [`",
96            def_name,
97            "`]:",
98            full_query_documentation
99        );
100        let query_match_doc = concat_doc!(
101            "A match returned by the query [`",
102            def_name,
103            "`]:",
104            full_query_documentation
105        );
106        let captures_doc = concat_doc!(
107            "Captures returned by a query cursor running the query [`",
108            def_name,
109            "`]:",
110            full_query_documentation
111        );
112        let query_capture_doc = concat_doc!(
113            "A capture returned by the query [`",
114            def_name,
115            "`]:",
116            full_query_documentation
117        );
118
119        let (tree_t, tree_fn, tree_query, tree_to_raws) = match use_yak_sitter {
120            false => (
121                quote! { , Text, I },
122                quote! {},
123                quote! {},
124                capture_idxs
125                    .iter()
126                    .map(|capture_idx| {
127                        quote! {
128                            #tree_sitter::QueryCapture {
129                                index: #capture_idx as u32,
130                                node: *node.raw()
131                            }
132                        }
133                    })
134                    .collect::<Vec<_>>(),
135            ),
136            true => (
137                quote! {},
138                quote! {
139                    #[inline]
140                    fn tree(&self) -> &'tree yak_sitter::Tree {
141                        self.0.tree()
142                    }
143                },
144                quote! { 'query, },
145                capture_idxs_and_names
146                    .iter()
147                    .map(|(i, c)| (*i, lit_str(c)))
148                    .map(|(capture_idx, capture_name)| {
149                        quote! {
150                            yak_sitter::QueryCapture {
151                                node: *node.raw(),
152                                index: #capture_idx,
153                                name: #capture_name
154                            }
155                        }
156                    })
157                    .collect::<Vec<_>>(),
158            ),
159        };
160
161        // Pattern-idx-specific matches and capture-idx-specific captures (TODO)
162        // Pattern-idx-agnostic matches and capture-idx-specific captures
163        // Capture-idx-agnostic captures
164        let capture_methods_and_variants = capture_idxs_and_names
165            .binary_group_by_key(|(_, capture_name)| *capture_name)
166            .map(|capture_idxs_and_name| {
167                let capture_idxs = capture_idxs_and_name
168                    .iter()
169                    .map(|(capture_idx, _)| *capture_idx)
170                    .collect::<Vec<_>>();
171                let capture_name = capture_idxs_and_name[0].1;
172                self.print_capture_method_and_variant(
173                    capture_name,
174                    &capture_idxs,
175                    query_str,
176                    &ts_query,
177                    nodes,
178                    ctx,
179                    anon_unions,
180                )
181            })
182            .collect::<Vec<_>>();
183        let capture_methods = capture_methods_and_variants
184            .iter()
185            .map(|x| x.0.clone())
186            .collect::<TokenStream>();
187        let capture_variant_extract_methods = capture_methods_and_variants
188            .iter()
189            .map(|x| x.1.clone())
190            .collect::<TokenStream>();
191        let capture_variants = capture_methods_and_variants
192            .iter()
193            .map(|x| &x.2)
194            .collect::<Vec<_>>();
195        let capture_variant_documentations = capture_methods_and_variants
196            .iter()
197            .map(|x| &x.3)
198            .collect::<Vec<_>>();
199        let capture_node_types = capture_methods_and_variants
200            .iter()
201            .map(|x| &x.4)
202            .collect::<Vec<_>>();
203        let non_existent_variant = match capture_methods_and_variants.is_empty() {
204            false => quote! {},
205            true => quote! {
206                /// This node has no captures so the enum has no instantiable variants. This variant
207                /// is necessary to keep lifetime parameters, but the `Never` type means it can't be
208                /// instantiated.
209                __NonExistent(#type_sitter_lib::Never, std::marker::PhantomData<&'tree ()>)
210            },
211        };
212
213        quote! {
214            #[doc = #def_doc]
215            #[allow(non_camel_case_types)]
216            #[derive(Debug, Clone, Copy)]
217            pub struct #def_ident;
218
219            #[doc = #matches_doc]
220            #[allow(unused, non_camel_case_types)]
221            pub type #query_matches<'query, 'tree #tree_t> = #type_sitter_lib::QueryMatches<'query, 'tree, #def_ident #tree_t>;
222            #[doc = #captures_doc]
223            #[allow(unused, non_camel_case_types)]
224            pub type #query_captures<'query, 'tree #tree_t> = #type_sitter_lib::QueryCaptures<'query, 'tree, #def_ident #tree_t>;
225            #[doc = #query_match_doc]
226            #[repr(transparent)]
227            pub struct #query_match<'query, 'tree: 'query>(#tree_sitter::QueryMatch<'query, 'tree>);
228            #[doc = #query_capture_doc]
229            #[derive(Clone, Debug)]
230            pub enum #query_capture<'tree> {
231                #(#capture_variant_documentations #capture_variants(#capture_node_types),)*
232                #non_existent_variant
233            }
234
235            #[automatically_derived]
236            impl #type_sitter_lib::Query for #def_ident {
237                type Match<'query, 'tree: 'query> = #query_match<'query, 'tree>;
238                type Capture<'query, 'tree: 'query> = #query_capture<'tree>;
239
240                fn as_str(&self) -> &'static str {
241                    #query_str
242                }
243
244                fn raw(&self) -> &'static #tree_sitter::Query {
245                    #[allow(non_upper_case_globals)]
246                    static #internal_query: std::sync::OnceLock<#tree_sitter::Query> = std::sync::OnceLock::new();
247
248                    #internal_query.get_or_init(|| {
249                        #[allow(unused_mut)]
250                        let mut query = #tree_sitter::Query::new(
251                            &#language_ident::LANGUAGE.into(),
252                            #query_str
253                        ).expect(#query_parse_error);
254                        #(query.disable_capture(#disabled_captures);)*
255                        #(query.disable_pattern(#disabled_patterns);)*
256                        query
257                    })
258                }
259
260                #[inline]
261                unsafe fn wrap_match<'query, 'tree>(
262                    &self,
263                    r#match: #tree_sitter::QueryMatch<'query, 'tree>
264                ) -> #query_match<'query, 'tree> {
265                    #query_match(r#match)
266                }
267
268                #[inline]
269                unsafe fn wrap_match_ref<'m, 'query, 'tree>(
270                    &self,
271                    r#match: &'m #tree_sitter::QueryMatch<'query, 'tree>
272                ) -> &'m #query_match<'query, 'tree> {
273                    // SAFETY: Same repr
274                    &*(r#match as *const #tree_sitter::QueryMatch<'query, 'tree> as *const #query_match<'query, 'tree>)
275                }
276
277                #[inline]
278                unsafe fn wrap_capture<'query, 'tree: 'query>(
279                    &self,
280                    capture: #tree_sitter::QueryCapture<#tree_query 'tree>,
281                ) -> #query_capture<'tree> {
282                    // SAFETY: As long as the capture came from the query this is safe, because the
283                    // query only captures nodes of the correct type
284                    match capture.index as usize {
285                        #(#capture_idxs => #query_capture::#capture_variants(
286                            <#capture_node_types as #type_sitter_lib::Node<'tree>>::from_raw_unchecked(capture.node)
287                        ),)*
288                        capture_index => unreachable!("Invalid capture index: {}", capture_index)
289                    }
290                }
291            }
292
293            #[automatically_derived]
294            #[allow(unused)]
295            impl<'query, 'tree: 'query> #query_match<'query, 'tree> {
296                #capture_methods
297            }
298
299            #[automatically_derived]
300            impl<'query, 'tree: 'query> std::fmt::Debug for #query_match<'query, 'tree> {
301                fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
302                    f.debug_tuple(stringify!(#query_match))
303                        .field(&self.0)
304                        .finish()
305                }
306            }
307
308            #[automatically_derived]
309            impl<'query, 'tree: 'query> #type_sitter_lib::QueryMatch<'query, 'tree> for #query_match<'query, 'tree> {
310                type Query = #def_ident;
311
312                #[inline]
313                fn query(&self) -> &'query Self::Query {
314                    &#def_ident
315                }
316
317                #tree_fn
318
319                #[inline]
320                fn raw(&self) -> &#tree_sitter::QueryMatch<'query, 'tree> {
321                    &self.0
322                }
323
324                #[inline]
325                fn into_raw(self) -> #tree_sitter::QueryMatch<'query, 'tree> {
326                    self.0
327                }
328            }
329
330            #[automatically_derived]
331            #[allow(unused)]
332            impl<'tree> #query_capture<'tree> {
333                #capture_variant_extract_methods
334            }
335
336            #[automatically_derived]
337            impl<'query, 'tree: 'query> #type_sitter_lib::QueryCapture<'query, 'tree> for #query_capture<'tree> {
338                type Query = #def_ident;
339
340                #[inline]
341                fn query(&self) -> &'query Self::Query {
342                    &#def_ident
343                }
344
345                #[inline]
346                fn raw(&self) -> #tree_sitter::QueryCapture<#tree_query 'tree> {
347                    #[allow(unused_imports)]
348                    use #type_sitter_lib::Node;
349                    match self {
350                        #(Self::#capture_variants(node) => #tree_to_raws,)*
351                        // https://github.com/rust-lang/rust/issues/78123 for empty enums is why this exists
352                        #[allow(unreachable_patterns)]
353                        _ => unreachable!()
354                    }
355                }
356
357                #[inline]
358                fn node(&self) -> &#type_sitter_lib::UntypedNode<'tree> {
359                    #[allow(unused_imports)]
360                    use #type_sitter_lib::Node;
361                    match self {
362                        #(Self::#capture_variants(node) => #type_sitter_lib::UntypedNode::r#ref(node.raw()),)*
363                        // https://github.com/rust-lang/rust/issues/78123 for empty enums is why this exists
364                        #[allow(unreachable_patterns)]
365                        _ => unreachable!()
366                    }
367                }
368
369                #[inline]
370                fn node_mut(&mut self) -> &mut #type_sitter_lib::UntypedNode<'tree> {
371                    #[allow(unused_imports)]
372                    use #type_sitter_lib::Node;
373                    match self {
374                        #(Self::#capture_variants(node) => #type_sitter_lib::UntypedNode::r#mut(node.raw_mut()),)*
375                        // https://github.com/rust-lang/rust/issues/78123 for empty enums is why this exists
376                        #[allow(unreachable_patterns)]
377                        _ => unreachable!()
378                    }
379                }
380
381                #[inline]
382                fn name(&self) -> &'query str {
383                    match self {
384                        #(Self::#capture_variants { .. } => #capture_names,)*
385                        // https://github.com/rust-lang/rust/issues/78123 for empty enums is why this exists
386                        #[allow(unreachable_patterns)]
387                        _ => unreachable!()
388                    }
389                }
390
391                #[inline]
392                fn index(&self) -> usize {
393                    match self {
394                        #(Self::#capture_variants { .. } => #capture_idxs,)*
395                        // https://github.com/rust-lang/rust/issues/78123 for empty enums is why this exists
396                        #[allow(unreachable_patterns)]
397                        _ => unreachable!()
398                    }
399                }
400            }
401        }
402    }
403
404    fn print_capture_method_and_variant(
405        &self,
406        capture_name: &str,
407        capture_idxs: &[usize],
408        query_str: &str,
409        ts_query: &tree_sitter::Query,
410        nodes: &Path,
411        ctx @ PrintCtx {
412            all_types,
413            type_sitter_lib,
414            ..
415        }: PrintCtx,
416        anon_unions: &mut AnonUnions,
417    ) -> (TokenStream, TokenStream, Ident, TokenStream, TokenStream) {
418        let (capture_variant_name, capture_method_name) =
419            sexp_name_to_rust_names(&capture_name.replace(".", "_"));
420        let capture_method = ident!(capture_method_name, "capture name (capture method)")
421            .expect("ident should be valid because we get them from a names function");
422        let capture_variant = ident!(capture_variant_name, "capture name (capture variant)")
423            .expect("ident should be valid because we get them from a names function");
424
425        let mut capture_method_name = capture_method.to_string();
426        // We must remove the `r#` prefix because we're prepending `as_` and we don't have to add
427        // back because no reserved identifiers start with it.
428        unmake_reserved(&mut capture_method_name);
429        let as_capture_method = format_ident!("as_{}", capture_method_name);
430
431        let mut captured_sexps = self.captured_patterns(capture_name).collect::<Vec<_>>();
432        captured_sexps.sort_by(|lhs, rhs| lhs.span().cmp(rhs.span()));
433        let captured_sexp_strs = captured_sexps.iter().map(|s| &query_str[s.span()]);
434        let capture_node_type = captured_sexps
435            .iter()
436            .map(|s| s.node_type(false, all_types))
437            .collect::<SExpNodeType>();
438        let capture_node_type_tokens =
439            capture_node_type.print(&capture_variant_name, nodes, ctx, anon_unions);
440
441        let capture_doc = format!(
442            "`{}` ([`{}`])",
443            capture_name,
444            capture_node_type.rust_type_path(nodes, &capture_variant_name)
445        );
446
447        let capture_quantifier = capture_idxs
448            .iter()
449            .flat_map(|capture_idx| {
450                (0..ts_query.pattern_count())
451                    .map(|pattern_idx| &ts_query.capture_quantifiers(pattern_idx)[*capture_idx])
452            })
453            .copied()
454            .reduce(CaptureQuantifierExt::union)
455            .unwrap_or(CaptureQuantifier::Zero);
456        let captured_type = capture_quantifier.print_type(&capture_node_type_tokens);
457        let captured_nonempty_iterator_doc = capture_quantifier.print_nonempty_iterator_doc();
458        let capture_idxs_array = lit_array(capture_idxs.iter().map(|i| *i as u32));
459        let captured_expr = capture_quantifier.print_expr(&quote! {
460            #capture_idxs_array.into_iter().flat_map(|i| self.0.nodes_for_capture_index(i))
461            // SAFETY: Query only captures nodes of the correct type and tree
462                .map(|n| unsafe { <#capture_node_type_tokens as #type_sitter_lib::Node<'tree>>::from_raw_unchecked(n) })
463        });
464
465        let full_capture_pattern_doc = captured_sexp_strs
466            .map(|captured_sexp_str| {
467                let doc = concat_doc!(captured_sexp_str, " @", capture_name);
468                quote! { #[doc = #doc] }
469            })
470            .collect::<TokenStream>();
471        let full_capture_documentation = quote! {
472            #[doc = ""]
473            #[doc = "The full capture including pattern(s) is:"]
474            #[doc = "```sexp"]
475            #full_capture_pattern_doc
476            #[doc = "```"]
477        };
478
479        let capture_method_doc = concat_doc!(
480            "Returns an iterator over the nodes captured by ",
481            capture_doc
482        );
483        let capture_method = quote! {
484            #[doc = #capture_method_doc]
485            #captured_nonempty_iterator_doc
486            #full_capture_documentation
487            #[inline]
488            pub fn #capture_method(&self) -> #captured_type {
489                #captured_expr
490            }
491        };
492        let capture_variant_extract_method_doc =
493            concat_doc!("Try to interpret this capture as a ", capture_doc);
494        let capture_variant_extract_method = quote! {
495            #[doc = #capture_variant_extract_method_doc]
496            #full_capture_documentation
497            #[inline]
498            pub fn #as_capture_method(&self) -> ::std::option::Option<&#capture_node_type_tokens> {
499                #[allow(irrefutable_let_patterns)]
500                if let Self::#capture_variant(node) = self {
501                    Some(node)
502                } else {
503                    None
504                }
505            }
506        };
507        let capture_variant_main_doc = concat_doc!("A ", capture_doc);
508        let capture_variant_documentation = quote! {
509            #[doc = #capture_variant_main_doc]
510            #full_capture_documentation
511        };
512        (
513            capture_method,
514            capture_variant_extract_method,
515            capture_variant,
516            capture_variant_documentation,
517            capture_node_type_tokens,
518        )
519    }
520}
521
522impl SExpNodeType {
523    fn print(
524        &self,
525        capture_variant_name: &str,
526        nodes: &Path,
527        ctx @ PrintCtx {
528            type_sitter_lib, ..
529        }: PrintCtx,
530        anon_unions: &mut AnonUnions,
531    ) -> TokenStream {
532        match self {
533            Self::Single { r#type } => {
534                let type_ = r#type.print_rust_type();
535                quote! { #nodes::#type_ }
536            }
537            Self::Union { types, .. } => {
538                let mut types = types.iter().collect::<Vec<_>>();
539                // Can't do `..._by_key` because `K` has an existential lifetime, which Rust can't express.
540                types.sort_unstable_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
541                types.dedup_by(|lhs, rhs| lhs.name == rhs.name);
542
543                NodeType::print_query_capture_sum_type(
544                    capture_variant_name,
545                    &types,
546                    nodes,
547                    ctx,
548                    anon_unions,
549                )
550            }
551            Self::Untyped { is_named } => match is_named {
552                false => quote! { #type_sitter_lib::UntypedNode<'tree> },
553                true => quote! { #type_sitter_lib::UntypedNamedNode<'tree> },
554            },
555        }
556    }
557}
558
559trait CaptureQuantifierExt {
560    fn union(self, other: CaptureQuantifier) -> CaptureQuantifier;
561    fn print_type(&self, inner_type: &TokenStream) -> TokenStream;
562    fn print_nonempty_iterator_doc(&self) -> TokenStream;
563    fn print_expr(&self, iterator: &TokenStream) -> TokenStream;
564}
565
566impl CaptureQuantifierExt for CaptureQuantifier {
567    fn union(self, rhs: CaptureQuantifier) -> CaptureQuantifier {
568        match (self, rhs) {
569            (CaptureQuantifier::Zero, CaptureQuantifier::Zero) => CaptureQuantifier::Zero,
570            (CaptureQuantifier::Zero, CaptureQuantifier::ZeroOrOne) => CaptureQuantifier::ZeroOrOne,
571            (CaptureQuantifier::Zero, CaptureQuantifier::ZeroOrMore) => {
572                CaptureQuantifier::ZeroOrMore
573            }
574            (CaptureQuantifier::Zero, CaptureQuantifier::One) => CaptureQuantifier::ZeroOrOne,
575            (CaptureQuantifier::Zero, CaptureQuantifier::OneOrMore) => {
576                CaptureQuantifier::ZeroOrMore
577            }
578            (CaptureQuantifier::ZeroOrOne, CaptureQuantifier::Zero) => CaptureQuantifier::ZeroOrOne,
579            (CaptureQuantifier::ZeroOrOne, CaptureQuantifier::ZeroOrOne) => {
580                CaptureQuantifier::ZeroOrOne
581            }
582            (CaptureQuantifier::ZeroOrOne, CaptureQuantifier::ZeroOrMore) => {
583                CaptureQuantifier::ZeroOrMore
584            }
585            (CaptureQuantifier::ZeroOrOne, CaptureQuantifier::One) => CaptureQuantifier::ZeroOrOne,
586            (CaptureQuantifier::ZeroOrOne, CaptureQuantifier::OneOrMore) => {
587                CaptureQuantifier::ZeroOrMore
588            }
589            (CaptureQuantifier::ZeroOrMore, CaptureQuantifier::Zero) => {
590                CaptureQuantifier::ZeroOrMore
591            }
592            (CaptureQuantifier::ZeroOrMore, CaptureQuantifier::ZeroOrOne) => {
593                CaptureQuantifier::ZeroOrMore
594            }
595            (CaptureQuantifier::ZeroOrMore, CaptureQuantifier::ZeroOrMore) => {
596                CaptureQuantifier::ZeroOrMore
597            }
598            (CaptureQuantifier::ZeroOrMore, CaptureQuantifier::One) => {
599                CaptureQuantifier::ZeroOrMore
600            }
601            (CaptureQuantifier::ZeroOrMore, CaptureQuantifier::OneOrMore) => {
602                CaptureQuantifier::ZeroOrMore
603            }
604            (CaptureQuantifier::One, CaptureQuantifier::Zero) => CaptureQuantifier::ZeroOrOne,
605            (CaptureQuantifier::One, CaptureQuantifier::ZeroOrOne) => CaptureQuantifier::ZeroOrOne,
606            (CaptureQuantifier::One, CaptureQuantifier::ZeroOrMore) => {
607                CaptureQuantifier::ZeroOrMore
608            }
609            (CaptureQuantifier::One, CaptureQuantifier::One) => CaptureQuantifier::One,
610            (CaptureQuantifier::One, CaptureQuantifier::OneOrMore) => CaptureQuantifier::OneOrMore,
611            (CaptureQuantifier::OneOrMore, CaptureQuantifier::Zero) => {
612                CaptureQuantifier::ZeroOrMore
613            }
614            (CaptureQuantifier::OneOrMore, CaptureQuantifier::ZeroOrOne) => {
615                CaptureQuantifier::ZeroOrMore
616            }
617            (CaptureQuantifier::OneOrMore, CaptureQuantifier::ZeroOrMore) => {
618                CaptureQuantifier::ZeroOrMore
619            }
620            (CaptureQuantifier::OneOrMore, CaptureQuantifier::One) => CaptureQuantifier::OneOrMore,
621            (CaptureQuantifier::OneOrMore, CaptureQuantifier::OneOrMore) => {
622                CaptureQuantifier::OneOrMore
623            }
624        }
625    }
626
627    fn print_type(&self, inner_type: &TokenStream) -> TokenStream {
628        match self {
629            CaptureQuantifier::Zero => quote! { () },
630            CaptureQuantifier::ZeroOrOne => quote! { ::std::option::Option<#inner_type> },
631            CaptureQuantifier::ZeroOrMore => {
632                quote! { impl ::std::iter::Iterator<Item=#inner_type> + '_ }
633            }
634            CaptureQuantifier::One => quote! { #inner_type },
635            CaptureQuantifier::OneOrMore => {
636                quote! { impl ::std::iter::Iterator<Item=#inner_type> + '_ }
637            }
638        }
639    }
640
641    fn print_nonempty_iterator_doc(&self) -> TokenStream {
642        match self {
643            CaptureQuantifier::Zero => quote! {},
644            CaptureQuantifier::ZeroOrOne => quote! {},
645            CaptureQuantifier::ZeroOrMore => quote! {},
646            CaptureQuantifier::One => quote! {},
647            CaptureQuantifier::OneOrMore => {
648                quote! { #[doc = "\n\nThis is guaranteed to return at least one child"] }
649            }
650        }
651    }
652
653    fn print_expr(&self, iterator: &TokenStream) -> TokenStream {
654        let iterator = quote! {{ #iterator }};
655        match self {
656            CaptureQuantifier::Zero => {
657                quote! { ::std::debug_assert!(#iterator.next().is_none(), "zero quantifier returned an item") }
658            }
659            CaptureQuantifier::ZeroOrOne => quote! { #iterator.next() },
660            CaptureQuantifier::ZeroOrMore => quote! { #iterator },
661            CaptureQuantifier::One => quote! {
662                let mut iterator = #iterator;
663                let result = iterator.next().expect("one quantifier returned nothing");
664                ::std::debug_assert!(iterator.next().is_none(), "one quantifier returned more than one item");
665                result
666            },
667            CaptureQuantifier::OneOrMore => quote! { #iterator },
668        }
669    }
670}
671
672impl GeneratedQueryTokens {
673    /// Strip extra info, converting this into a regular [`TokenStream`].
674    ///
675    /// To pretty-print, call [`into_string`](Self::into_string).
676    ///
677    /// ## Parameters
678    /// - `nodes`: Path to the crate with the typed node wrappers. Typically
679    ///   [`type_sitter_gen::super_nodes`]
680    pub fn collapse(self, nodes: &Path) -> TokenStream {
681        let nodes = match nodes
682            .segments
683            .first()
684            .map_or(false, |s| s.ident.to_string() == "super")
685        {
686            false => quote! { #nodes },
687            true => quote! { super::#nodes },
688        };
689        let GeneratedQueryTokens {
690            toplevel,
691            anon_unions,
692        } = self;
693        let anon_unions = anon_unions.into_values().collect::<TokenStream>();
694        let anon_unions = modularize!(anon_unions (use #nodes::*));
695        quote! {
696            #toplevel
697            #anon_unions
698        }
699    }
700}