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