cairo_lang_syntax_codegen/
generator.rs

1use std::fs;
2use std::path::PathBuf;
3
4use genco::prelude::*;
5use xshell::Shell;
6
7use crate::cairo_spec::get_spec;
8use crate::spec::{Member, Node, NodeKind, Variant, Variants};
9
10pub fn project_root() -> PathBuf {
11    // This is the directory of Cargo.toml of the syntax_codegen crate.
12    let dir = env!("CARGO_MANIFEST_DIR");
13    // Pop the "/crates/cairo-lang-syntax-codegen" suffix.
14    let res = PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned();
15    assert!(res.join("Cargo.toml").exists(), "Could not find project root directory.");
16    res
17}
18
19pub fn ensure_file_content(filename: PathBuf, content: String) {
20    if let Ok(old_contents) = fs::read_to_string(&filename) {
21        if old_contents == content {
22            return;
23        }
24    }
25
26    fs::write(&filename, content).unwrap();
27}
28
29pub fn get_codes() -> Vec<(String, String)> {
30    vec![
31        (
32            "crates/cairo-lang-syntax/src/node/ast.rs".into(),
33            reformat_rust_code(generate_ast_code().to_string().unwrap()),
34        ),
35        (
36            "crates/cairo-lang-syntax/src/node/kind.rs".into(),
37            reformat_rust_code(generate_kinds_code().to_string().unwrap()),
38        ),
39        (
40            "crates/cairo-lang-syntax/src/node/key_fields.rs".into(),
41            reformat_rust_code(generate_key_fields_code().to_string().unwrap()),
42        ),
43    ]
44}
45
46pub fn reformat_rust_code(text: String) -> String {
47    // Since rustfmt is used with nightly features, it takes 2 runs to reach a fixed point.
48    reformat_rust_code_inner(reformat_rust_code_inner(text))
49}
50pub fn reformat_rust_code_inner(text: String) -> String {
51    let sh = Shell::new().unwrap();
52    let cmd = sh.cmd("rustfmt").env("RUSTUP_TOOLCHAIN", "nightly-2024-11-23");
53    let cmd_with_args = cmd.arg("--config-path").arg(project_root().join("rustfmt.toml"));
54    let mut stdout = cmd_with_args.stdin(text).read().unwrap();
55    if !stdout.ends_with('\n') {
56        stdout.push('\n');
57    }
58    stdout
59}
60
61fn generate_kinds_code() -> rust::Tokens {
62    let spec = get_spec();
63    let mut tokens = quote! {
64        $("// Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`.\n")
65        use core::fmt;
66    };
67
68    // SyntaxKind.
69    let kinds = name_tokens(&spec, |k| !matches!(k, NodeKind::Enum { .. }));
70    let token_kinds = name_tokens(&spec, |k| matches!(k, NodeKind::Token { .. }));
71    let keyword_token_kinds =
72        name_tokens(&spec, |k| matches!(k, NodeKind::Token { is_keyword } if *is_keyword));
73    let terminal_kinds = name_tokens(&spec, |k| matches!(k, NodeKind::Terminal { .. }));
74    let keyword_terminal_kinds =
75        name_tokens(&spec, |k| matches!(k, NodeKind::Terminal { is_keyword, .. } if *is_keyword));
76
77    tokens.extend(quote! {
78        #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
79        pub enum SyntaxKind {
80            $(for t in kinds => $t,)
81        }
82        impl SyntaxKind {
83            pub fn is_token(&self) -> bool {
84                matches!(
85                    *self,
86                    $(for t in token_kinds join ( | ) => SyntaxKind::$t)
87                )
88            }
89            pub fn is_terminal(&self) -> bool {
90                matches!(
91                    *self,
92                    $(for t in terminal_kinds join ( | ) => SyntaxKind::$t)
93                )
94            }
95            pub fn is_keyword_token(&self) -> bool {
96                matches!(
97                    *self,
98                    $(for t in keyword_token_kinds join ( | ) => SyntaxKind::$t)
99                )
100            }
101            pub fn is_keyword_terminal(&self) -> bool {
102                matches!(
103                    *self,
104                    $(for t in keyword_terminal_kinds join ( | ) => SyntaxKind::$t)
105                )
106            }
107        }
108        impl fmt::Display for SyntaxKind {
109            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110                write!(f, "{self:?}")
111            }
112        }
113    });
114    tokens
115}
116
117/// Returns an iterator to the names of the tokens matching `predicate`.
118fn name_tokens(spec: &[Node], predicate: impl Fn(&NodeKind) -> bool) -> impl Iterator<Item = &str> {
119    spec.iter().filter(move |n| predicate(&n.kind)).map(|n| n.name.as_str())
120}
121
122fn generate_key_fields_code() -> rust::Tokens {
123    let spec = get_spec();
124    let mut arms = rust::Tokens::new();
125
126    for Node { name, kind } in spec.into_iter() {
127        match kind {
128            NodeKind::Struct { members } | NodeKind::Terminal { members, .. } => {
129                let mut fields = rust::Tokens::new();
130                for (i, member) in members.into_iter().enumerate() {
131                    let field_name = member.name;
132                    if member.key {
133                        fields.extend(quote! { $("/*") $field_name $("*/") children[$i], });
134                    }
135                }
136                arms.extend(quote! {
137                    SyntaxKind::$name => {vec![$fields]},
138                });
139            }
140            NodeKind::List { .. } | NodeKind::SeparatedList { .. } | NodeKind::Token { .. } => {
141                arms.extend(quote! {
142                    SyntaxKind::$name => vec![],
143                });
144            }
145            NodeKind::Enum { .. } => {}
146        }
147    }
148    let tokens = quote! {
149        $("// Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`.\n")
150        use super::ids::GreenId;
151        use super::kind::SyntaxKind;
152
153        $("/// Gets the vector of children ids that are the indexing key for this SyntaxKind.\n")
154        $("///\n")
155        $("/// Each SyntaxKind has some children that are defined in the spec to be its indexing key\n")
156        $("/// for its stable pointer. See [super::stable_ptr].\n")
157        pub fn get_key_fields(kind: SyntaxKind, children: &[GreenId]) -> Vec<GreenId> {
158            match kind {
159                $arms
160            }
161        }
162    };
163    tokens
164}
165
166fn generate_ast_code() -> rust::Tokens {
167    let spec = get_spec();
168    let mut tokens = quote! {
169        $("// Autogenerated file. To regenerate, please run `cargo run --bin generate-syntax`.\n")
170        #![allow(clippy::match_single_binding)]
171        #![allow(clippy::too_many_arguments)]
172        #![allow(dead_code)]
173        #![allow(unused_variables)]
174        use std::ops::Deref;
175        use std::sync::Arc;
176
177        use cairo_lang_filesystem::span::TextWidth;
178        use cairo_lang_utils::{extract_matches, Intern, LookupIntern};
179        use smol_str::SmolStr;
180
181        use super::element_list::ElementList;
182        use super::green::GreenNodeDetails;
183        use super::kind::SyntaxKind;
184        use super::{
185            GreenId, GreenNode, SyntaxGroup, SyntaxNode, SyntaxStablePtr, SyntaxStablePtrId,
186            Terminal, Token, TypedStablePtr, TypedSyntaxNode,
187        };
188
189        #[path = "ast_ext.rs"]
190        mod ast_ext;
191    };
192    let spec_clone = spec.clone();
193    let all_tokens: Vec<_> =
194        spec_clone.iter().filter(|node| matches!(node.kind, NodeKind::Terminal { .. })).collect();
195    for Node { name, kind } in spec.into_iter() {
196        tokens.extend(match kind {
197            NodeKind::Enum { variants, missing_variant } => {
198                let variants_list = match variants {
199                    Variants::List(variants) => variants,
200                    Variants::AllTokens => all_tokens
201                        .iter()
202                        .map(|node| Variant { name: node.name.clone(), kind: node.name.clone() })
203                        .collect(),
204                };
205                gen_enum_code(name, variants_list, missing_variant)
206            }
207            NodeKind::Struct { members } => gen_struct_code(name, members, false),
208            NodeKind::Terminal { members, .. } => gen_struct_code(name, members, true),
209            NodeKind::Token { .. } => gen_token_code(name),
210            NodeKind::List { element_type } => gen_list_code(name, element_type),
211            NodeKind::SeparatedList { element_type, separator_type } => {
212                gen_separated_list_code(name, element_type, separator_type)
213            }
214        });
215    }
216    tokens
217}
218
219fn gen_list_code(name: String, element_type: String) -> rust::Tokens {
220    // TODO(spapini): Change Deref to Borrow.
221    let ptr_name = format!("{name}Ptr");
222    let green_name = format!("{name}Green");
223    let element_green_name = format!("{element_type}Green");
224    let common_code = gen_common_list_code(&name, &green_name, &ptr_name);
225    quote! {
226        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
227        pub struct $(&name)(ElementList<$(&element_type),1>);
228        impl Deref for $(&name){
229            type Target = ElementList<$(&element_type),1>;
230
231            fn deref(&self) -> &Self::Target {
232                &self.0
233            }
234        }
235        impl $(&name){
236            pub fn new_green(
237                db: &dyn SyntaxGroup, children: Vec<$(&element_green_name)>
238            ) -> $(&green_name) {
239                let width = children.iter().map(|id|
240                    id.0.lookup_intern(db).width()).sum();
241                $(&green_name)(Arc::new(GreenNode {
242                    kind: SyntaxKind::$(&name),
243                    details: GreenNodeDetails::Node {
244                        children: children.iter().map(|x| x.0).collect(),
245                        width,
246                    },
247                }).intern(db))
248            }
249        }
250        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
251        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
252        impl TypedStablePtr for $(&ptr_name) {
253            type SyntaxNode = $(&name);
254            fn untyped(&self) -> SyntaxStablePtrId {
255                self.0
256            }
257            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
258                $(&name)::from_syntax_node(db, self.0.lookup(db))
259            }
260        }
261        impl From<$(&ptr_name)> for SyntaxStablePtrId {
262            fn from(ptr: $(&ptr_name)) -> Self {
263                ptr.untyped()
264            }
265        }
266        $common_code
267    }
268}
269
270fn gen_separated_list_code(
271    name: String,
272    element_type: String,
273    separator_type: String,
274) -> rust::Tokens {
275    // TODO(spapini): Change Deref to Borrow.
276    let ptr_name = format!("{name}Ptr");
277    let green_name = format!("{name}Green");
278    let element_or_separator_green_name = format!("{name}ElementOrSeparatorGreen");
279    let element_green_name = format!("{element_type}Green");
280    let separator_green_name = format!("{separator_type}Green");
281    let common_code = gen_common_list_code(&name, &green_name, &ptr_name);
282    quote! {
283        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
284        pub struct $(&name)(ElementList<$(&element_type),2>);
285        impl Deref for $(&name){
286            type Target = ElementList<$(&element_type),2>;
287
288            fn deref(&self) -> &Self::Target {
289                &self.0
290            }
291        }
292        impl $(&name){
293            pub fn new_green(
294                db: &dyn SyntaxGroup, children: Vec<$(&element_or_separator_green_name)>
295            ) -> $(&green_name) {
296                let width = children.iter().map(|id|
297                    id.id().lookup_intern(db).width()).sum();
298                $(&green_name)(Arc::new(GreenNode {
299                    kind: SyntaxKind::$(&name),
300                    details: GreenNodeDetails::Node {
301                        children: children.iter().map(|x| x.id()).collect(),
302                        width,
303                    },
304                }).intern(db))
305            }
306        }
307        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
308        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
309        impl TypedStablePtr for $(&ptr_name) {
310            type SyntaxNode = $(&name);
311            fn untyped(&self) -> SyntaxStablePtrId {
312                self.0
313            }
314            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
315                $(&name)::from_syntax_node(db, self.0.lookup(db))
316            }
317        }
318        impl From<$(&ptr_name)> for SyntaxStablePtrId {
319            fn from(ptr: $(&ptr_name)) -> Self {
320                ptr.untyped()
321            }
322        }
323        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
324        pub enum $(&element_or_separator_green_name) {
325            Separator($(&separator_green_name)),
326            Element($(&element_green_name)),
327        }
328        impl From<$(&separator_green_name)> for $(&element_or_separator_green_name) {
329            fn from(value: $(&separator_green_name)) -> Self {
330                $(&element_or_separator_green_name)::Separator(value)
331            }
332        }
333        impl From<$(&element_green_name)> for $(&element_or_separator_green_name) {
334            fn from(value: $(&element_green_name)) -> Self {
335                $(&element_or_separator_green_name)::Element(value)
336            }
337        }
338        impl $(&element_or_separator_green_name) {
339            fn id(&self) -> GreenId {
340                match self {
341                    $(&element_or_separator_green_name)::Separator(green) => green.0,
342                    $(&element_or_separator_green_name)::Element(green) => green.0,
343                }
344            }
345        }
346        $common_code
347    }
348}
349
350fn gen_common_list_code(name: &str, green_name: &str, ptr_name: &str) -> rust::Tokens {
351    quote! {
352        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
353        pub struct $green_name(pub GreenId);
354        impl TypedSyntaxNode for $name {
355            const OPTIONAL_KIND: Option<SyntaxKind> = Some(SyntaxKind::$name);
356            type StablePtr = $ptr_name;
357            type Green = $green_name;
358            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
359                $green_name(Arc::new(
360                    GreenNode {
361                        kind: SyntaxKind::$name,
362                        details: GreenNodeDetails::Node { children: vec![], width: TextWidth::default() },
363                    }).intern(db)
364                )
365            }
366            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
367                Self(ElementList::new(node))
368            }
369            fn as_syntax_node(&self) -> SyntaxNode {
370                self.node.clone()
371            }
372            fn stable_ptr(&self) -> Self::StablePtr {
373                $ptr_name(self.node.0.stable_ptr)
374            }
375        }
376        impl From<&$name> for SyntaxStablePtrId {
377            fn from(node: &$name) -> Self {
378                node.stable_ptr().untyped()
379            }
380        }
381    }
382}
383
384fn gen_enum_code(
385    name: String,
386    variants: Vec<Variant>,
387    missing_variant: Option<Variant>,
388) -> rust::Tokens {
389    let ptr_name = format!("{name}Ptr");
390    let green_name = format!("{name}Green");
391    let mut enum_body = quote! {};
392    let mut from_node_body = quote! {};
393    let mut ptr_conversions = quote! {};
394    let mut green_conversions = quote! {};
395    for variant in &variants {
396        let n = &variant.name;
397        let k = &variant.kind;
398
399        enum_body.extend(quote! {
400            $n($k),
401        });
402        from_node_body.extend(quote! {
403            SyntaxKind::$k => $(&name)::$n($k::from_syntax_node(db, node)),
404        });
405        let variant_ptr = format!("{k}Ptr");
406        ptr_conversions.extend(quote! {
407            impl From<$(&variant_ptr)> for $(&ptr_name) {
408                fn from(value: $(&variant_ptr)) -> Self {
409                    Self(value.0)
410                }
411            }
412        });
413        let variant_green = format!("{k}Green");
414        green_conversions.extend(quote! {
415            impl From<$(&variant_green)> for $(&green_name) {
416                fn from(value: $(&variant_green)) -> Self {
417                    Self(value.0)
418                }
419            }
420        });
421    }
422    let missing_body = match missing_variant {
423        Some(missing) => quote! {
424            $(&green_name)($(missing.kind)::missing(db).0)
425        },
426        None => quote! {
427            panic!("No missing variant.");
428        },
429    };
430    quote! {
431        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
432        pub enum $(&name){
433            $enum_body
434        }
435        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
436        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
437        impl TypedStablePtr for $(&ptr_name) {
438            type SyntaxNode = $(&name);
439            fn untyped(&self) -> SyntaxStablePtrId {
440                self.0
441            }
442            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
443                $(&name)::from_syntax_node(db, self.0.lookup(db))
444            }
445        }
446        impl From<$(&ptr_name)> for SyntaxStablePtrId {
447            fn from(ptr: $(&ptr_name)) -> Self {
448                ptr.untyped()
449            }
450        }
451        $ptr_conversions
452        $green_conversions
453        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
454        pub struct $(&green_name)(pub GreenId);
455        impl TypedSyntaxNode for $(&name){
456            const OPTIONAL_KIND: Option<SyntaxKind> = None;
457            type StablePtr = $(&ptr_name);
458            type Green = $(&green_name);
459            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
460                $missing_body
461            }
462            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
463                let kind = node.kind(db);
464                match kind{
465                    $from_node_body
466                    _ => panic!(
467                        "Unexpected syntax kind {:?} when constructing {}.",
468                        kind,
469                        $[str]($[const](&name))),
470                }
471            }
472            fn as_syntax_node(&self) -> SyntaxNode {
473                match self {
474                    $(for v in &variants => $(&name)::$(&v.name)(x) => x.as_syntax_node(),)
475                }
476            }
477            fn stable_ptr(&self) -> Self::StablePtr {
478                $(&ptr_name)(self.as_syntax_node().0.stable_ptr)
479            }
480        }
481        impl From<&$(&name)> for SyntaxStablePtrId {
482            fn from(node: &$(&name)) -> Self {
483                node.stable_ptr().untyped()
484            }
485        }
486        impl $(&name) {
487            $("/// Checks if a kind of a variant of [")$(&name)$("].\n")
488            pub fn is_variant(kind: SyntaxKind) -> bool {
489                matches!(kind, $(for v in &variants join (|) => SyntaxKind::$(&v.kind)))
490            }
491        }
492    }
493}
494
495fn gen_token_code(name: String) -> rust::Tokens {
496    let green_name = format!("{name}Green");
497    let ptr_name = format!("{name}Ptr");
498
499    quote! {
500        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
501        pub struct $(&name) {
502            node: SyntaxNode,
503        }
504        impl Token for $(&name) {
505            fn new_green(db: &dyn SyntaxGroup, text: SmolStr) -> Self::Green {
506                $(&green_name)(Arc::new(GreenNode {
507                    kind: SyntaxKind::$(&name),
508                    details: GreenNodeDetails::Token(text),
509                }).intern(db))
510            }
511            fn text(&self, db: &dyn SyntaxGroup) -> SmolStr {
512                extract_matches!(&self.node.0.green.lookup_intern(db).details,
513                    GreenNodeDetails::Token).clone()
514            }
515        }
516        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
517        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
518        impl TypedStablePtr for $(&ptr_name) {
519            type SyntaxNode = $(&name);
520            fn untyped(&self) -> SyntaxStablePtrId {
521                self.0
522            }
523            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
524                $(&name)::from_syntax_node(db, self.0.lookup(db))
525            }
526        }
527        impl From<$(&ptr_name)> for SyntaxStablePtrId {
528            fn from(ptr: $(&ptr_name)) -> Self {
529                ptr.untyped()
530            }
531        }
532        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
533        pub struct $(&green_name)(pub GreenId);
534        impl $(&green_name) {
535            pub fn text(&self, db: &dyn SyntaxGroup) -> SmolStr {
536                extract_matches!(
537                    &self.0.lookup_intern(db).details, GreenNodeDetails::Token).clone()
538            }
539        }
540        impl TypedSyntaxNode for $(&name){
541            const OPTIONAL_KIND: Option<SyntaxKind> = Some(SyntaxKind::$(&name));
542            type StablePtr = $(&ptr_name);
543            type Green = $(&green_name);
544            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
545                $(&green_name)(Arc::new(GreenNode {
546                    kind: SyntaxKind::TokenMissing,
547                    details: GreenNodeDetails::Token("".into()),
548                }).intern(db))
549            }
550            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
551                match node.0.green.lookup_intern(db).details {
552                    GreenNodeDetails::Token(_) => Self { node },
553                    GreenNodeDetails::Node { .. } => panic!(
554                        "Expected a token {:?}, not an internal node",
555                        SyntaxKind::$(&name)
556                    ),
557                }
558            }
559            fn as_syntax_node(&self) -> SyntaxNode {
560                self.node.clone()
561            }
562            fn stable_ptr(&self) -> Self::StablePtr {
563                $(&ptr_name)(self.node.0.stable_ptr)
564            }
565        }
566        impl From<&$(&name)> for SyntaxStablePtrId {
567            fn from(node: &$(&name)) -> Self {
568                node.stable_ptr().untyped()
569            }
570        }
571    }
572}
573
574fn gen_struct_code(name: String, members: Vec<Member>, is_terminal: bool) -> rust::Tokens {
575    let green_name = format!("{name}Green");
576    let mut body = rust::Tokens::new();
577    let mut field_indices = quote! {};
578    let mut args = quote! {};
579    let mut params = quote! {};
580    let mut args_for_missing = quote! {};
581    let mut ptr_getters = quote! {};
582    let mut key_field_index: usize = 0;
583    for (i, Member { name, kind, key }) in members.iter().enumerate() {
584        let index_name = format!("INDEX_{}", name.to_uppercase());
585        field_indices.extend(quote! {
586            pub const $index_name : usize = $i;
587        });
588        let key_name_green = format!("{name}_green");
589        args.extend(quote! {$name.0,});
590        // TODO(spapini): Validate that children SyntaxKinds are as expected.
591
592        let child_green = format!("{kind}Green");
593        params.extend(quote! {$name: $(&child_green),});
594        body.extend(quote! {
595            pub fn $name(&self, db: &dyn SyntaxGroup) -> $kind {
596                $kind::from_syntax_node(db, self.children[$i].clone())
597            }
598        });
599        args_for_missing.extend(quote! {$kind::missing(db).0,});
600
601        if *key {
602            ptr_getters.extend(quote! {
603                pub fn $(&key_name_green)(self, db: &dyn SyntaxGroup) -> $(&child_green) {
604                    let ptr = self.0.lookup_intern(db);
605                    if let SyntaxStablePtr::Child { key_fields, .. } = ptr {
606                        $(&child_green)(key_fields[$key_field_index])
607                    } else {
608                        panic!("Unexpected key field query on root.");
609                    }
610                }
611            });
612            key_field_index += 1;
613        }
614    }
615    let ptr_name = format!("{name}Ptr");
616    let new_green_impl = if is_terminal {
617        let token_name = name.replace("Terminal", "Token");
618        quote! {
619            impl Terminal for $(&name) {
620                const KIND: SyntaxKind = SyntaxKind::$(&name);
621                type TokenType = $(&token_name);
622                fn new_green(
623                    db: &dyn SyntaxGroup,
624                    leading_trivia: TriviaGreen,
625                    token: <<$(&name) as Terminal>::TokenType as TypedSyntaxNode>::Green,
626                    trailing_trivia: TriviaGreen
627                ) -> Self::Green {
628                    let children: Vec<GreenId> = vec![$args];
629                    let width = children.iter().copied().map(|id|
630                        id.lookup_intern(db).width()).sum();
631                    $(&green_name)(Arc::new(GreenNode {
632                        kind: SyntaxKind::$(&name),
633                        details: GreenNodeDetails::Node { children, width },
634                    }).intern(db))
635                }
636                fn text(&self, db: &dyn SyntaxGroup) -> SmolStr {
637                    self.token(db).text(db)
638                }
639            }
640        }
641    } else {
642        quote! {
643            impl $(&name) {
644                $field_indices
645                pub fn new_green(db: &dyn SyntaxGroup, $params) -> $(&green_name) {
646                    let children: Vec<GreenId> = vec![$args];
647                    let width = children.iter().copied().map(|id|
648                        id.lookup_intern(db).width()).sum();
649                    $(&green_name)(Arc::new(GreenNode {
650                        kind: SyntaxKind::$(&name),
651                        details: GreenNodeDetails::Node { children, width },
652                    }).intern(db))
653                }
654            }
655        }
656    };
657    quote! {
658        #[derive(Clone, Debug, Eq, Hash, PartialEq)]
659        pub struct $(&name) {
660            node: SyntaxNode,
661            children: Arc<[SyntaxNode]>,
662        }
663        $new_green_impl
664        impl $(&name) {
665            $body
666        }
667        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
668        pub struct $(&ptr_name)(pub SyntaxStablePtrId);
669        impl $(&ptr_name) {
670            $ptr_getters
671        }
672        impl TypedStablePtr for $(&ptr_name) {
673            type SyntaxNode = $(&name);
674            fn untyped(&self) -> SyntaxStablePtrId {
675                self.0
676            }
677            fn lookup(&self, db: &dyn SyntaxGroup) -> $(&name) {
678                $(&name)::from_syntax_node(db, self.0.lookup(db))
679            }
680        }
681        impl From<$(&ptr_name)> for SyntaxStablePtrId {
682            fn from(ptr: $(&ptr_name)) -> Self {
683                ptr.untyped()
684            }
685        }
686        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
687        pub struct $(&green_name)(pub GreenId);
688        impl TypedSyntaxNode for $(&name) {
689            const OPTIONAL_KIND: Option<SyntaxKind> = Some(SyntaxKind::$(&name));
690            type StablePtr = $(&ptr_name);
691            type Green = $(&green_name);
692            fn missing(db: &dyn SyntaxGroup) -> Self::Green {
693                // Note: A missing syntax element should result in an internal green node
694                // of width 0, with as much structure as possible.
695                $(&green_name)(Arc::new(GreenNode {
696                    kind: SyntaxKind::$(&name),
697                    details: GreenNodeDetails::Node {
698                        children: vec![$args_for_missing],
699                        width: TextWidth::default(),
700                    },
701                }).intern(db))
702            }
703            fn from_syntax_node(db: &dyn SyntaxGroup, node: SyntaxNode) -> Self {
704                let kind = node.kind(db);
705                assert_eq!(kind, SyntaxKind::$(&name), "Unexpected SyntaxKind {:?}. Expected {:?}.", kind, SyntaxKind::$(&name));
706                let children = db.get_children(node.clone());
707                Self { node, children }
708            }
709            fn as_syntax_node(&self) -> SyntaxNode {
710                self.node.clone()
711            }
712            fn stable_ptr(&self) -> Self::StablePtr {
713                $(&ptr_name)(self.node.0.stable_ptr)
714            }
715        }
716        impl From<&$(&name)> for SyntaxStablePtrId {
717            fn from(node: &$(&name)) -> Self {
718                node.stable_ptr().untyped()
719            }
720        }
721    }
722}