Skip to main content

treesitter_types/codegen/
emitter.rs

1use super::type_mapper::{
2    AlternationEnumDef, ChildrenDef, FieldDef, FieldType, LeafStructDef, StructDef,
3    SupertypeEnumDef, TypeDecision, TypeReference, VariantDef,
4};
5use proc_macro2::TokenStream;
6use quote::{format_ident, quote};
7
8/// Emits all generated code from a set of type decisions.
9pub fn emit(decisions: &[TypeDecision]) -> TokenStream {
10    let mut tokens = TokenStream::new();
11
12    // Collect type names that have no lifetime parameter.
13    // First pass: anonymous-only supertype enums (known statically).
14    let mut no_lifetime_types: std::collections::HashSet<String> = decisions
15        .iter()
16        .filter_map(|d| {
17            if let TypeDecision::SupertypeEnum(def) = d {
18                if !def.variants.iter().any(|v| v.named) {
19                    return Some(def.type_name.to_string());
20                }
21            }
22            None
23        })
24        .collect();
25
26    // Second pass: structs whose fields all resolve to no-lifetime types.
27    // Iterate until stable (handles transitive dependencies).
28    loop {
29        let mut changed = false;
30        for decision in decisions {
31            if let TypeDecision::Struct(def) = decision {
32                let name = def.type_name.to_string();
33                if !no_lifetime_types.contains(&name)
34                    && !struct_needs_lifetime(def, &no_lifetime_types)
35                {
36                    no_lifetime_types.insert(name);
37                    changed = true;
38                }
39            }
40        }
41        if !changed {
42            break;
43        }
44    }
45
46    // Collect all alternation enums from struct fields to emit them alongside
47    let mut alternation_enums: Vec<&AlternationEnumDef> = Vec::new();
48    for decision in decisions {
49        if let TypeDecision::Struct(def) = decision {
50            collect_alternation_enums(def, &mut alternation_enums);
51        }
52    }
53
54    for decision in decisions {
55        match decision {
56            TypeDecision::Struct(def) => tokens.extend(emit_struct(def, &no_lifetime_types)),
57            TypeDecision::LeafStruct(def) => tokens.extend(emit_leaf_struct(def)),
58            TypeDecision::SupertypeEnum(def) => {
59                tokens.extend(emit_supertype_enum(def, &no_lifetime_types))
60            }
61        }
62    }
63
64    for alt in &alternation_enums {
65        tokens.extend(emit_alternation_enum(alt, &no_lifetime_types));
66    }
67
68    // Emit the AnyNode top-level enum
69    tokens.extend(emit_any_node(decisions, &no_lifetime_types));
70
71    tokens
72}
73
74fn collect_alternation_enums<'a>(def: &'a StructDef, out: &mut Vec<&'a AlternationEnumDef>) {
75    for field in &def.fields {
76        collect_alternation_from_field_type(&field.field_type, out);
77    }
78    if let Some(children) = &def.children {
79        collect_alternation_from_field_type(&children.field_type, out);
80    }
81}
82
83fn collect_alternation_from_field_type<'a>(
84    ft: &'a FieldType,
85    out: &mut Vec<&'a AlternationEnumDef>,
86) {
87    let type_ref = match ft {
88        FieldType::Direct(tr) | FieldType::Optional(tr) | FieldType::Repeated(tr) => tr,
89    };
90    if let TypeReference::Alternation(alt) = type_ref {
91        out.push(alt);
92    }
93}
94
95/// Check whether a field type needs a lifetime parameter.
96fn field_type_needs_lifetime(
97    ft: &FieldType,
98    no_lifetime_types: &std::collections::HashSet<String>,
99) -> bool {
100    let tr = match ft {
101        FieldType::Direct(tr) | FieldType::Optional(tr) | FieldType::Repeated(tr) => tr,
102    };
103    match tr {
104        TypeReference::Named(ident) => !no_lifetime_types.contains(&ident.to_string()),
105        TypeReference::Alternation(alt) => alt.variants.iter().any(|v| v.named),
106    }
107}
108
109/// Check whether a struct definition needs a lifetime parameter.
110fn struct_needs_lifetime(
111    def: &StructDef,
112    no_lifetime_types: &std::collections::HashSet<String>,
113) -> bool {
114    def.fields
115        .iter()
116        .any(|f| field_type_needs_lifetime(&f.field_type, no_lifetime_types))
117        || def
118            .children
119            .as_ref()
120            .is_some_and(|c| field_type_needs_lifetime(&c.field_type, no_lifetime_types))
121}
122
123fn emit_struct(
124    def: &StructDef,
125    no_lifetime_types: &std::collections::HashSet<String>,
126) -> TokenStream {
127    let type_name = &def.type_name;
128    let kind_str = &def.kind;
129    let needs_lifetime = struct_needs_lifetime(def, no_lifetime_types);
130
131    let field_decls: Vec<_> = def
132        .fields
133        .iter()
134        .map(|f| emit_field_decl(f, type_name, no_lifetime_types))
135        .collect();
136    let field_parsers: Vec<_> = def
137        .fields
138        .iter()
139        .map(|f| emit_field_parser(f, type_name, no_lifetime_types))
140        .collect();
141
142    let (children_decl, children_parser) = if let Some(children) = &def.children {
143        (
144            Some(emit_children_decl(children, type_name, no_lifetime_types)),
145            Some(emit_children_parser(children, type_name)),
146        )
147    } else {
148        (None, None)
149    };
150
151    if needs_lifetime {
152        quote! {
153            #[derive(Debug, Clone, PartialEq, Eq)]
154            pub struct #type_name<'tree> {
155                pub span: ::treesitter_types::Span,
156                #(#field_decls)*
157                #children_decl
158            }
159
160            impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name<'tree> {
161                #[allow(clippy::match_single_binding, clippy::suspicious_else_formatting)]
162                fn from_node(
163                    node: ::treesitter_types::tree_sitter::Node<'tree>,
164                    src: &'tree [u8],
165                ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
166                    debug_assert_eq!(node.kind(), #kind_str);
167                    Ok(Self {
168                        span: ::treesitter_types::Span::from(node),
169                        #(#field_parsers)*
170                        #children_parser
171                    })
172                }
173            }
174
175            impl ::treesitter_types::Spanned for #type_name<'_> {
176                fn span(&self) -> ::treesitter_types::Span {
177                    self.span
178                }
179            }
180        }
181    } else {
182        // No-lifetime struct: fields are all anonymous-only types.
183        // Still use `src` param since field parsers pass it to FromNode::from_node.
184        let has_fields = !def.fields.is_empty() || def.children.is_some();
185        let src_param = if has_fields {
186            quote! { src: &'tree [u8] }
187        } else {
188            quote! { _src: &'tree [u8] }
189        };
190        quote! {
191            #[derive(Debug, Clone, PartialEq, Eq)]
192            pub struct #type_name {
193                pub span: ::treesitter_types::Span,
194                #(#field_decls)*
195                #children_decl
196            }
197
198            impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name {
199                #[allow(clippy::match_single_binding, clippy::suspicious_else_formatting)]
200                fn from_node(
201                    node: ::treesitter_types::tree_sitter::Node<'tree>,
202                    #src_param,
203                ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
204                    debug_assert_eq!(node.kind(), #kind_str);
205                    Ok(Self {
206                        span: ::treesitter_types::Span::from(node),
207                        #(#field_parsers)*
208                        #children_parser
209                    })
210                }
211            }
212
213            impl ::treesitter_types::Spanned for #type_name {
214                fn span(&self) -> ::treesitter_types::Span {
215                    self.span
216                }
217            }
218        }
219    }
220}
221
222fn emit_field_decl(
223    field: &FieldDef,
224    parent_type: &proc_macro2::Ident,
225    no_lifetime_types: &std::collections::HashSet<String>,
226) -> TokenStream {
227    let name = &field.field_name;
228    let ty = emit_rust_type(&field.field_type, parent_type, no_lifetime_types);
229    quote! { pub #name: #ty, }
230}
231
232fn emit_children_decl(
233    children: &ChildrenDef,
234    parent_type: &proc_macro2::Ident,
235    no_lifetime_types: &std::collections::HashSet<String>,
236) -> TokenStream {
237    let ty = emit_rust_type(&children.field_type, parent_type, no_lifetime_types);
238    quote! { pub children: #ty, }
239}
240
241/// Check if a type reference refers to the parent type (self-referential).
242fn is_self_referential(tr: &TypeReference, parent_type: &proc_macro2::Ident) -> bool {
243    match tr {
244        TypeReference::Named(ident) => ident == parent_type,
245        TypeReference::Alternation(_) => false,
246    }
247}
248
249fn emit_rust_type(
250    ft: &FieldType,
251    parent_type: &proc_macro2::Ident,
252    no_lifetime_types: &std::collections::HashSet<String>,
253) -> TokenStream {
254    match ft {
255        FieldType::Direct(tr) => {
256            let inner = emit_type_reference(tr, no_lifetime_types);
257            if is_self_referential(tr, parent_type) {
258                quote! { ::std::boxed::Box<#inner> }
259            } else {
260                quote! { #inner }
261            }
262        }
263        FieldType::Optional(tr) => {
264            let inner = emit_type_reference(tr, no_lifetime_types);
265            if is_self_referential(tr, parent_type) {
266                quote! { ::core::option::Option<::std::boxed::Box<#inner>> }
267            } else {
268                quote! { ::core::option::Option<#inner> }
269            }
270        }
271        FieldType::Repeated(tr) => {
272            let inner = emit_type_reference(tr, no_lifetime_types);
273            quote! { ::std::vec::Vec<#inner> }
274        }
275    }
276}
277
278fn emit_type_reference(
279    tr: &TypeReference,
280    no_lifetime_types: &std::collections::HashSet<String>,
281) -> TokenStream {
282    match tr {
283        TypeReference::Named(ident) => {
284            if no_lifetime_types.contains(&ident.to_string()) {
285                quote! { #ident }
286            } else {
287                quote! { #ident<'tree> }
288            }
289        }
290        TypeReference::Alternation(alt) => {
291            let name = &alt.type_name;
292            let has_named = alt.variants.iter().any(|v| v.named);
293            if has_named {
294                quote! { #name<'tree> }
295            } else {
296                quote! { #name }
297            }
298        }
299    }
300}
301
302fn emit_field_parser(
303    field: &FieldDef,
304    parent_type: &proc_macro2::Ident,
305    no_lifetime_types: &std::collections::HashSet<String>,
306) -> TokenStream {
307    let _ = no_lifetime_types; // used via emit_rust_type for field decls
308    let name = &field.field_name;
309    let raw_name = &field.raw_field_name;
310
311    match &field.field_type {
312        FieldType::Direct(type_ref) => {
313            let from_node = emit_from_node_call(type_ref);
314            let self_ref = is_self_referential(type_ref, parent_type);
315            let value_expr = if self_ref {
316                quote! { ::std::boxed::Box::new(#from_node) }
317            } else {
318                from_node
319            };
320            quote! {
321                #name: {
322                    let child = node
323                        .child_by_field_name(#raw_name)
324                        .ok_or_else(|| ::treesitter_types::ParseError::missing_field(#raw_name, node))?;
325                    #value_expr
326                },
327            }
328        }
329        FieldType::Optional(type_ref) => {
330            let from_node = emit_from_node_call(type_ref);
331            let self_ref = is_self_referential(type_ref, parent_type);
332            let some_expr = if self_ref {
333                quote! { Some(::std::boxed::Box::new(#from_node)) }
334            } else {
335                quote! { Some(#from_node) }
336            };
337            quote! {
338                #name: match node.child_by_field_name(#raw_name) {
339                    Some(child) => #some_expr,
340                    None => None,
341                },
342            }
343        }
344        FieldType::Repeated(type_ref) => {
345            let from_node = emit_from_node_call(type_ref);
346            quote! {
347                #name: {
348                    let mut cursor = node.walk();
349                    let mut items = ::std::vec::Vec::new();
350                    for child in node.children_by_field_name(#raw_name, &mut cursor) {
351                        items.push(#from_node);
352                    }
353                    items
354                },
355            }
356        }
357    }
358}
359
360/// Emits a helper expression that collects non-field named children.
361/// Filters by: no field name, is named, and not extra (comments).
362/// Some grammars list anonymous nodes in children types (e.g., Rust `_` pattern),
363/// but these are rare and handled by the `Direct` children parser which falls back
364/// to trying all non-field children when no named children are found.
365fn emit_non_field_children_collector() -> TokenStream {
366    quote! {
367        #[allow(clippy::suspicious_else_formatting)]
368        let non_field_children = {
369            let mut cursor = node.walk();
370            let mut result = ::std::vec::Vec::new();
371            if cursor.goto_first_child() {
372                loop {
373                    if cursor.field_name().is_none() && cursor.node().is_named() && !cursor.node().is_extra() {
374                        result.push(cursor.node());
375                    }
376                    if !cursor.goto_next_sibling() {
377                        break;
378                    }
379                }
380            }
381            result
382        };
383    }
384}
385
386fn emit_children_parser(children: &ChildrenDef, parent_type: &proc_macro2::Ident) -> TokenStream {
387    let collector = emit_non_field_children_collector();
388    match &children.field_type {
389        FieldType::Direct(type_ref) => {
390            let from_node = emit_from_node_call_named_children(type_ref);
391            let self_ref = is_self_referential(type_ref, parent_type);
392            let value_expr = if self_ref {
393                quote! { ::std::boxed::Box::new(#from_node) }
394            } else {
395                from_node
396            };
397            // First try named non-field children (the common case). If none exist,
398            // fall back to trying all non-field, non-extra children including anonymous
399            // ones and pick the first that successfully parses. This handles grammars
400            // where supertypes (e.g., `expression`) include anonymous subtypes
401            // (e.g., `this`, `base` in C#) that `is_named()` would skip.
402            quote! {
403                children: {
404                    #collector
405                    let child = if let Some(&c) = non_field_children.first() {
406                        c
407                    } else {
408                        let mut fallback_cursor = node.walk();
409                        let mut fallback_child = None;
410                        if fallback_cursor.goto_first_child() {
411                            loop {
412                                if fallback_cursor.field_name().is_none() && !fallback_cursor.node().is_extra() {
413                                    let candidate = fallback_cursor.node();
414                                    #[allow(clippy::needless_question_mark)]
415                                    if (|| -> ::core::result::Result<_, ::treesitter_types::ParseError> {
416                                        let child = candidate;
417                                        Ok(#value_expr)
418                                    })().is_ok() {
419                                        fallback_child = Some(candidate);
420                                        break;
421                                    }
422                                }
423                                if !fallback_cursor.goto_next_sibling() {
424                                    break;
425                                }
426                            }
427                        }
428                        // Second fallback: try children WITH field names.
429                        // Some grammars (e.g., Haskell) have children that node-types.json
430                        // lists as unnamed, but tree-sitter assigns inherited field names at
431                        // runtime, causing the first fallback to skip them.
432                        if fallback_child.is_none() {
433                            let mut cursor2 = node.walk();
434                            if cursor2.goto_first_child() {
435                                loop {
436                                    if cursor2.node().is_named() && !cursor2.node().is_extra() {
437                                        let candidate = cursor2.node();
438                                        #[allow(clippy::needless_question_mark)]
439                                        if (|| -> ::core::result::Result<_, ::treesitter_types::ParseError> {
440                                            let child = candidate;
441                                            Ok(#value_expr)
442                                        })().is_ok() {
443                                            fallback_child = Some(candidate);
444                                            break;
445                                        }
446                                    }
447                                    if !cursor2.goto_next_sibling() {
448                                        break;
449                                    }
450                                }
451                            }
452                        }
453                        fallback_child.ok_or_else(|| ::treesitter_types::ParseError::missing_field("children", node))?
454                    };
455                    #value_expr
456                },
457            }
458        }
459        FieldType::Optional(type_ref) => {
460            let from_node = emit_from_node_call_named_children(type_ref);
461            let self_ref = is_self_referential(type_ref, parent_type);
462            let some_expr = if self_ref {
463                quote! { Some(::std::boxed::Box::new(#from_node)) }
464            } else {
465                quote! { Some(#from_node) }
466            };
467            quote! {
468                children: {
469                    #collector
470                    match non_field_children.first() {
471                        Some(&child) => #some_expr,
472                        None => None,
473                    }
474                },
475            }
476        }
477        FieldType::Repeated(type_ref) => {
478            let from_node = emit_from_node_call_named_children(type_ref);
479            quote! {
480                children: {
481                    #collector
482                    let mut items = ::std::vec::Vec::new();
483                    for child in non_field_children {
484                        items.push(#from_node);
485                    }
486                    items
487                },
488            }
489        }
490    }
491}
492
493fn emit_from_node_call(type_ref: &TypeReference) -> TokenStream {
494    match type_ref {
495        TypeReference::Named(ident) => {
496            quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#ident as ::treesitter_types::FromNode>::from_node(child, src))? }
497        }
498        TypeReference::Alternation(alt) => {
499            let name = &alt.type_name;
500            quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#name as ::treesitter_types::FromNode>::from_node(child, src))? }
501        }
502    }
503}
504
505fn emit_from_node_call_named_children(type_ref: &TypeReference) -> TokenStream {
506    match type_ref {
507        TypeReference::Named(ident) => {
508            quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#ident as ::treesitter_types::FromNode>::from_node(child, src))? }
509        }
510        TypeReference::Alternation(alt) => {
511            let name = &alt.type_name;
512            quote! { ::treesitter_types::runtime::maybe_grow_stack(|| <#name as ::treesitter_types::FromNode>::from_node(child, src))? }
513        }
514    }
515}
516
517fn emit_leaf_struct(def: &LeafStructDef) -> TokenStream {
518    let type_name = &def.type_name;
519    let kind_str = &def.kind;
520
521    quote! {
522        #[derive(Debug, Clone, PartialEq, Eq)]
523        pub struct #type_name<'tree> {
524            pub span: ::treesitter_types::Span,
525            text: &'tree str,
526        }
527
528        impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name<'tree> {
529            fn from_node(
530                node: ::treesitter_types::tree_sitter::Node<'tree>,
531                src: &'tree [u8],
532            ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
533                debug_assert_eq!(node.kind(), #kind_str);
534                Ok(Self {
535                    span: ::treesitter_types::Span::from(node),
536                    text: node.utf8_text(src)?,
537                })
538            }
539        }
540
541        impl<'tree> ::treesitter_types::LeafNode<'tree> for #type_name<'tree> {
542            fn text(&self) -> &'tree str {
543                self.text
544            }
545        }
546
547        impl ::treesitter_types::Spanned for #type_name<'_> {
548            fn span(&self) -> ::treesitter_types::Span {
549                self.span
550            }
551        }
552    }
553}
554
555/// Deduplicate variants with the same name. When multiple anonymous nodes
556/// produce the same variant name (e.g., "A" and "a" both → `A`), keep only
557/// one variant and aggregate the extra kinds so we can match them all.
558fn deduplicate_variants(variants: &[VariantDef]) -> Vec<VariantDef> {
559    // Key includes both variant name and named flag, since anonymous variants
560    // (Span payload) and named variants (Box<T> payload) are incompatible.
561    let mut seen: std::collections::HashMap<(String, bool), usize> =
562        std::collections::HashMap::new();
563    let mut result: Vec<VariantDef> = Vec::new();
564    let mut used_names: std::collections::HashSet<String> = std::collections::HashSet::new();
565    for v in variants {
566        let key = (v.variant_name.to_string(), v.named);
567        if let Some(&idx) = seen.get(&key) {
568            // Merge: same name and same named flag — can safely combine.
569            result[idx].extra_kinds.push(v.kind.clone());
570        } else {
571            let mut v = v.clone();
572            // If the name is already used by a variant with a different named flag,
573            // disambiguate by appending "Kw" for the anonymous one
574            let name_str = v.variant_name.to_string();
575            if used_names.contains(&name_str) && !v.named {
576                v.variant_name = format_ident!("{}Kw", name_str);
577            }
578            let actual_name = v.variant_name.to_string();
579            seen.insert(key, result.len());
580            used_names.insert(actual_name);
581            used_names.insert(name_str);
582            result.push(v);
583        }
584    }
585    result
586}
587
588fn emit_alternation_enum(
589    def: &AlternationEnumDef,
590    no_lifetime_types: &std::collections::HashSet<String>,
591) -> TokenStream {
592    emit_enum_common(&def.type_name, &def.variants, no_lifetime_types)
593}
594
595fn emit_supertype_enum(
596    def: &SupertypeEnumDef,
597    no_lifetime_types: &std::collections::HashSet<String>,
598) -> TokenStream {
599    emit_enum_common(&def.type_name, &def.variants, no_lifetime_types)
600}
601
602fn emit_enum_common(
603    type_name: &proc_macro2::Ident,
604    variants: &[VariantDef],
605    no_lifetime_types: &std::collections::HashSet<String>,
606) -> TokenStream {
607    // Deduplicate variant names: when multiple anonymous nodes map to the same
608    // PascalCase name (e.g., "A" and "a" both become `A`), merge them into one
609    // variant and handle both kinds in the match arm.
610    let variants = deduplicate_variants(variants);
611    let has_named = variants.iter().any(|v| v.named);
612    let variant_decls: Vec<_> = variants
613        .iter()
614        .map(|v| emit_enum_variant_decl(v, no_lifetime_types))
615        .collect();
616
617    // Separate concrete variants (direct kind match) from supertype variants
618    // (kinds starting with `_` — tree-sitter never emits these as node kinds,
619    // they represent abstract supertypes whose concrete subtypes appear at runtime)
620    let (supertype_variants, concrete_variants): (Vec<_>, Vec<_>) =
621        variants.iter().partition(|v| v.is_supertype);
622
623    let concrete_arms: Vec<_> = concrete_variants
624        .iter()
625        .map(|v| emit_enum_match_arm(v))
626        .collect();
627    // Build a chained if/else-if for supertype fallbacks to avoid
628    // clippy::suspicious_else_formatting on consecutive `if let` blocks
629    let fallback_branch = if supertype_variants.is_empty() {
630        quote! { other => Err(::treesitter_types::ParseError::unexpected_kind(other, node)) }
631    } else {
632        let mut chain = quote! {
633            Err(::treesitter_types::ParseError::unexpected_kind(_other, node))
634        };
635        for v in supertype_variants.iter().rev() {
636            let name = &v.variant_name;
637            let type_name = format_ident!("{}", name);
638            chain = quote! {
639                if let Ok(v) = ::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src)) {
640                    Ok(Self::#name(::std::boxed::Box::new(v)))
641                } else {
642                    #chain
643                }
644            };
645        }
646        quote! { _other => { #chain } }
647    };
648
649    // When all variants are supertypes, skip the match and emit the fallback directly
650    let from_node_body = if concrete_arms.is_empty() {
651        // All variants are supertypes — no concrete kind matches needed
652        let mut chain = quote! {
653            Err(::treesitter_types::ParseError::unexpected_kind(node.kind(), node))
654        };
655        for v in supertype_variants.iter().rev() {
656            let name = &v.variant_name;
657            let type_name = format_ident!("{}", name);
658            chain = quote! {
659                if let Ok(v) = ::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src)) {
660                    Ok(Self::#name(::std::boxed::Box::new(v)))
661                } else {
662                    #chain
663                }
664            };
665        }
666        chain
667    } else {
668        quote! {
669            match node.kind() {
670                #(#concrete_arms)*
671                #fallback_branch
672            }
673        }
674    };
675
676    let spanned_arms: Vec<_> = variants.iter().map(emit_enum_spanned_arm).collect();
677
678    if has_named {
679        quote! {
680            #[derive(Debug, Clone, PartialEq, Eq)]
681            pub enum #type_name<'tree> {
682                #(#variant_decls)*
683            }
684
685            impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name<'tree> {
686                #[allow(clippy::collapsible_else_if)]
687                fn from_node(
688                    node: ::treesitter_types::tree_sitter::Node<'tree>,
689                    src: &'tree [u8],
690                ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
691                    #from_node_body
692                }
693            }
694
695            impl ::treesitter_types::Spanned for #type_name<'_> {
696                fn span(&self) -> ::treesitter_types::Span {
697                    match self {
698                        #(#spanned_arms)*
699                    }
700                }
701            }
702        }
703    } else {
704        // All variants are anonymous — no lifetime needed.
705        // Use `src` if supertype fallbacks need it, otherwise `_src`.
706        let has_supertypes = !supertype_variants.is_empty();
707        let src_param = if has_supertypes {
708            quote! { src: &'tree [u8] }
709        } else {
710            quote! { _src: &'tree [u8] }
711        };
712        quote! {
713            #[derive(Debug, Clone, PartialEq, Eq)]
714            pub enum #type_name {
715                #(#variant_decls)*
716            }
717
718            impl<'tree> ::treesitter_types::FromNode<'tree> for #type_name {
719                #[allow(clippy::collapsible_else_if)]
720                fn from_node(
721                    node: ::treesitter_types::tree_sitter::Node<'tree>,
722                    #src_param,
723                ) -> ::core::result::Result<Self, ::treesitter_types::ParseError> {
724                    #from_node_body
725                }
726            }
727
728            impl ::treesitter_types::Spanned for #type_name {
729                fn span(&self) -> ::treesitter_types::Span {
730                    match self {
731                        #(#spanned_arms)*
732                    }
733                }
734            }
735        }
736    }
737}
738
739fn emit_enum_variant_decl(
740    variant: &VariantDef,
741    no_lifetime_types: &std::collections::HashSet<String>,
742) -> TokenStream {
743    let name = &variant.variant_name;
744    if variant.named {
745        let type_name = format_ident!("{}", name);
746        if no_lifetime_types.contains(&type_name.to_string()) {
747            quote! { #name(::std::boxed::Box<#type_name>), }
748        } else {
749            quote! { #name(::std::boxed::Box<#type_name<'tree>>), }
750        }
751    } else {
752        // Anonymous variants carry a Span so consumers can locate them in source
753        quote! { #name(::treesitter_types::Span), }
754    }
755}
756
757fn emit_enum_match_arm(variant: &VariantDef) -> TokenStream {
758    let kind_str = &variant.kind;
759    let name = &variant.variant_name;
760    let extra = &variant.extra_kinds;
761    if variant.named {
762        let type_name = format_ident!("{}", name);
763        quote! {
764            #kind_str #(| #extra)* => Ok(Self::#name(
765                ::std::boxed::Box::new(::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src))?)
766            )),
767        }
768    } else {
769        quote! {
770            #kind_str #(| #extra)* => Ok(Self::#name(::treesitter_types::Span::from(node))),
771        }
772    }
773}
774
775fn emit_enum_spanned_arm(variant: &VariantDef) -> TokenStream {
776    let name = &variant.variant_name;
777    if variant.named {
778        quote! { Self::#name(inner) => inner.span(), }
779    } else {
780        quote! { Self::#name(span) => *span, }
781    }
782}
783
784fn emit_any_node(
785    decisions: &[TypeDecision],
786    no_lifetime_types: &std::collections::HashSet<String>,
787) -> TokenStream {
788    let mut variant_decls = Vec::new();
789    let mut match_arms = Vec::new();
790    let mut spanned_arms = Vec::new();
791
792    for decision in decisions {
793        let (type_name, kind_str) = match decision {
794            TypeDecision::Struct(def) => (&def.type_name, &def.kind),
795            TypeDecision::LeafStruct(def) => (&def.type_name, &def.kind),
796            TypeDecision::SupertypeEnum(def) => (&def.type_name, &def.kind),
797        };
798        let needs_lifetime = !no_lifetime_types.contains(&type_name.to_string());
799
800        if needs_lifetime {
801            variant_decls.push(quote! {
802                #type_name(#type_name<'tree>),
803            });
804        } else {
805            variant_decls.push(quote! {
806                #type_name(#type_name),
807            });
808        }
809
810        match_arms.push(quote! {
811            #kind_str => ::treesitter_types::runtime::maybe_grow_stack(|| <#type_name as ::treesitter_types::FromNode>::from_node(node, src))
812                .map(Self::#type_name)
813                .unwrap_or(Self::Unknown(node)),
814        });
815
816        spanned_arms.push(quote! {
817            Self::#type_name(inner) => inner.span(),
818        });
819    }
820
821    quote! {
822        #[derive(Debug, Clone, PartialEq, Eq)]
823        pub enum AnyNode<'tree> {
824            #(#variant_decls)*
825            Unknown(::treesitter_types::tree_sitter::Node<'tree>),
826        }
827
828        impl<'tree> AnyNode<'tree> {
829            pub fn from_node(node: ::treesitter_types::tree_sitter::Node<'tree>, src: &'tree [u8]) -> Self {
830                match node.kind() {
831                    #(#match_arms)*
832                    _ => Self::Unknown(node),
833                }
834            }
835        }
836
837        impl ::treesitter_types::Spanned for AnyNode<'_> {
838            fn span(&self) -> ::treesitter_types::Span {
839                match self {
840                    #(#spanned_arms)*
841                    Self::Unknown(node) => ::treesitter_types::Span::from(*node),
842                }
843            }
844        }
845    }
846}
847
848#[cfg(test)]
849mod tests {
850    use super::*;
851    use crate::codegen::grammar_ir::parse_node_types;
852    use crate::codegen::type_mapper::map_types;
853
854    fn generate_and_stringify(json: &str) -> String {
855        let nodes = parse_node_types(json).unwrap();
856        let decisions = map_types(&nodes);
857        let tokens = emit(&decisions);
858        tokens.to_string()
859    }
860
861    #[test]
862    fn test_emit_leaf_struct() {
863        let code = generate_and_stringify(r#"[{"type": "identifier", "named": true}]"#);
864        assert!(code.contains("pub struct Identifier"));
865        assert!(code
866            .contains("impl < 'tree > :: treesitter_types :: LeafNode < 'tree > for Identifier"));
867        assert!(code.contains("fn text"));
868    }
869
870    #[test]
871    fn test_emit_struct_with_required_field() {
872        let json = r#"[
873            {"type": "identifier", "named": true},
874            {
875                "type": "import_spec",
876                "named": true,
877                "fields": {
878                    "path": {
879                        "multiple": false,
880                        "required": true,
881                        "types": [{"type": "identifier", "named": true}]
882                    }
883                }
884            }
885        ]"#;
886        let code = generate_and_stringify(json);
887        assert!(code.contains("pub struct ImportSpec"));
888        assert!(code.contains("pub path : Identifier < 'tree >"));
889        assert!(code.contains("child_by_field_name (\"path\")"));
890    }
891
892    #[test]
893    fn test_emit_struct_with_optional_field() {
894        let json = r#"[
895            {"type": "identifier", "named": true},
896            {
897                "type": "import_spec",
898                "named": true,
899                "fields": {
900                    "name": {
901                        "multiple": false,
902                        "required": false,
903                        "types": [{"type": "identifier", "named": true}]
904                    }
905                }
906            }
907        ]"#;
908        let code = generate_and_stringify(json);
909        assert!(code.contains("Option < Identifier < 'tree > >"));
910    }
911
912    #[test]
913    fn test_emit_alternation_enum() {
914        let json = r#"[
915            {"type": "identifier", "named": true},
916            {
917                "type": "import_spec",
918                "named": true,
919                "fields": {
920                    "name": {
921                        "multiple": false,
922                        "required": false,
923                        "types": [
924                            {"type": ".", "named": false},
925                            {"type": "identifier", "named": true}
926                        ]
927                    }
928                }
929            }
930        ]"#;
931        let code = generate_and_stringify(json);
932        assert!(code.contains("pub enum ImportSpecName"));
933        assert!(code.contains("Dot"));
934        assert!(code.contains("Identifier (Identifier < 'tree >)"));
935    }
936
937    #[test]
938    fn test_emit_supertype_enum() {
939        let json = r#"[
940            {"type": "identifier", "named": true},
941            {"type": "binary_expression", "named": true},
942            {
943                "type": "_expression",
944                "named": true,
945                "subtypes": [
946                    {"type": "binary_expression", "named": true},
947                    {"type": "identifier", "named": true}
948                ]
949            }
950        ]"#;
951        let code = generate_and_stringify(json);
952        assert!(code.contains("pub enum Expression"));
953        assert!(code.contains("BinaryExpression (BinaryExpression < 'tree >)"));
954        assert!(code.contains("Identifier (Identifier < 'tree >)"));
955    }
956
957    #[test]
958    fn test_emit_any_node() {
959        let json = r#"[
960            {"type": "identifier", "named": true},
961            {"type": ".", "named": false}
962        ]"#;
963        let code = generate_and_stringify(json);
964        assert!(code.contains("pub enum AnyNode"));
965        assert!(code.contains("Identifier (Identifier < 'tree >)"));
966        assert!(code.contains("Unknown (:: treesitter_types :: tree_sitter :: Node < 'tree >)"));
967    }
968}