Skip to main content

wgsl_parse/
syntax_impl.rs

1use super::syntax::*;
2use crate::span::Spanned;
3
4impl TranslationUnit {
5    /// New empty [`TranslationUnit`]
6    pub fn new() -> Self {
7        Self::default()
8    }
9
10    /// Remove all [`GlobalDeclaration::Void`] and [`Statement::Void`]
11    pub fn remove_voids(&mut self) {
12        self.global_declarations
13            .retain_mut(|decl| match decl.node() {
14                GlobalDeclaration::Void => false,
15                _ => {
16                    decl.remove_voids();
17                    true
18                }
19            })
20    }
21}
22
23#[cfg(feature = "imports")]
24impl ModulePath {
25    /// Create a new module path from components.
26    ///
27    /// Precondition: the path components must be valid WGSL identifiers.
28    pub fn new(origin: PathOrigin, components: Vec<String>) -> Self {
29        Self { origin, components }
30    }
31
32    /// Create a module path that refers to the root module, i.e. `package`.
33    ///
34    /// Technically `import package;` is not a valid import statement in WESL code.
35    /// However adding an item to the path, such as `import package::foo;` points at
36    /// declaration `foo` in the root module.
37    pub fn new_root() -> Self {
38        Self::new(PathOrigin::Absolute, vec![])
39    }
40
41    /// Create a new module path from a filesystem path.
42    ///
43    /// * Paths with a root (leading `/` on Unix) produce `package::` paths.
44    /// * Relative paths (starting with `.` or `..`) produce `self::` or `super::` paths.
45    /// * The file extension is ignored.
46    /// * The path is canonicalized and to do so it does NOT follow symlinks.
47    ///
48    /// Preconditions:
49    /// * The path must not start with a prefix, like C:\ on windows.
50    /// * The path must contain at least one named component.
51    /// * Named components must be valid module names.
52    ///   (Module names are WGSL identifiers + certain reserved names, see wesl-spec#127)
53    pub fn from_path(path: impl AsRef<std::path::Path>) -> Self {
54        use std::path::Component;
55        let path = path.as_ref().with_extension("");
56        let mut parts = path.components().peekable();
57
58        let origin = match parts.next() {
59            Some(Component::Prefix(_)) => panic!("path starts with a Windows prefix"),
60            Some(Component::RootDir) => PathOrigin::Absolute,
61            Some(Component::CurDir) => PathOrigin::Relative(0),
62            Some(Component::ParentDir) => {
63                let mut n = 1;
64                while let Some(&Component::ParentDir) = parts.peek() {
65                    n += 1;
66                    parts.next().unwrap();
67                }
68                PathOrigin::Relative(n)
69            }
70            Some(Component::Normal(name)) => {
71                PathOrigin::Package(name.to_string_lossy().to_string())
72            }
73            None => panic!("path is empty"),
74        };
75
76        let components = parts
77            .map(|part| match part {
78                Component::Normal(name) => name.to_string_lossy().to_string(),
79                _ => panic!("unexpected path component"),
80            })
81            .collect::<Vec<_>>();
82
83        Self { origin, components }
84    }
85
86    /// Create a `PathBuf` from a `ModulePath`.
87    ///
88    /// * `package::` paths are rooted (start with `/`).
89    /// * self::` or `super::` are relative (starting with `.` or `..`)`.
90    /// * There is no file extension.
91    pub fn to_path_buf(&self) -> std::path::PathBuf {
92        use std::path::PathBuf;
93        let mut fs_path = match &self.origin {
94            PathOrigin::Absolute => PathBuf::from("/"),
95            PathOrigin::Relative(0) => PathBuf::from("."),
96            PathOrigin::Relative(n) => PathBuf::from_iter((0..*n).map(|_| "..")),
97            PathOrigin::Package(name) => PathBuf::from(name),
98        };
99        fs_path.extend(&self.components);
100        fs_path
101    }
102
103    /// Append a component to the path.
104    ///
105    /// Precondition: the `item` must be a valid WGSL identifier.
106    pub fn push(&mut self, item: &str) {
107        self.components.push(item.to_string());
108    }
109
110    /// Get the first component of the module path.
111    pub fn first(&self) -> Option<&str> {
112        self.components.first().map(String::as_str)
113    }
114
115    /// Get the last component of the module path.
116    pub fn last(&self) -> Option<&str> {
117        self.components.last().map(String::as_str)
118    }
119
120    /// Append `suffix` to the module path.
121    pub fn join(mut self, suffix: impl IntoIterator<Item = String>) -> Self {
122        self.components.extend(suffix);
123        self
124    }
125
126    /// Append `suffix` to the module path.
127    ///
128    /// This function produces a `ModulePath` relative to `self`, as if `suffix` was
129    /// imported from module `self`.
130    ///
131    /// * If `suffix` is relative, it appends its components to `self`.
132    /// * If `suffix` if absolute or package, it ignores `self` components.
133    /// * If both `self` and `suffix` are package paths, then `suffix` imports from a
134    ///   sub-package. The package is renamed with a slash separating package names.
135    ///   (TODO: this is a hack)
136    pub fn join_path(&self, suffix: &Self) -> Self {
137        match suffix.origin {
138            PathOrigin::Absolute => {
139                match self.origin {
140                    PathOrigin::Absolute | PathOrigin::Relative(_) => suffix.clone(),
141                    PathOrigin::Package(_) => {
142                        // absolute import from inside a package is a package import
143                        let origin = self.origin.clone();
144                        let components = suffix.components.clone();
145                        Self { origin, components }
146                    }
147                }
148            }
149            PathOrigin::Relative(n) => {
150                let to_keep = self.components.len().saturating_sub(n);
151                let components = self
152                    .components
153                    .iter()
154                    .take(to_keep)
155                    .chain(&suffix.components)
156                    .cloned()
157                    .collect::<Vec<_>>();
158                let origin = match self.origin {
159                    PathOrigin::Absolute | PathOrigin::Package(_) => self.origin.clone(),
160                    PathOrigin::Relative(m) => {
161                        PathOrigin::Relative(m + n.saturating_sub(self.components.len()))
162                    }
163                };
164                Self { origin, components }
165            }
166            PathOrigin::Package(ref suffix_pkg) => {
167                match &self.origin {
168                    PathOrigin::Absolute | PathOrigin::Relative(_) => suffix.clone(),
169                    PathOrigin::Package(self_pkg) => {
170                        if self_pkg.rsplit('/').next() == suffix_pkg.rsplit('/').next() {
171                            // Same package - just use the suffix path with the package origin
172                            let origin = self.origin.clone();
173                            let components = suffix.components.clone();
174                            Self { origin, components }
175                        } else {
176                            // Importing a sub-package. This is a hack: we rename the package to
177                            // parent/child, which cannot be spelled in code.
178                            let origin = PathOrigin::Package(format!("{self_pkg}/{suffix_pkg}"));
179                            let components = suffix.components.clone();
180                            Self { origin, components }
181                        }
182                    }
183                }
184            }
185        }
186    }
187
188    /// Whether the module path starts with a `prefix`.
189    pub fn starts_with(&self, prefix: &Self) -> bool {
190        self.origin == prefix.origin
191            && self.components.len() >= prefix.components.len()
192            && prefix
193                .components
194                .iter()
195                .zip(&self.components)
196                .all(|(a, b)| a == b)
197    }
198
199    /// Whether the module path points at the route module.
200    ///
201    /// See [`Self::new_root`].
202    pub fn is_root(&self) -> bool {
203        self.origin.is_absolute() && self.components.is_empty()
204    }
205}
206
207#[cfg(feature = "imports")]
208#[test]
209fn test_module_path_join() {
210    use std::str::FromStr;
211    let cases = [
212        ("package::m1", "package::foo", "package::foo"),
213        ("package::m1", "self::foo", "package::m1::foo"),
214        ("package::m1", "super::foo", "package::foo"),
215        ("pkg::m1::m2", "package::foo", "pkg::foo"),
216        ("pkg::m1::m2", "self::foo", "pkg::m1::m2::foo"),
217        ("pkg::m1::m2", "super::foo", "pkg::m1::foo"),
218        ("pkg::m1", "super::super::foo", "pkg::foo"),
219        ("lygia::m1", "lygia::math", "lygia::math"),
220        ("lygia::m1", "pkg/lygia", "lygia"),
221        ("pkg/lygia::m1", "lygia", "pkg/lygia"),
222        ("pkg/lygia::m1", "lygia::math", "pkg/lygia::math"),
223        ("pkg1/lygia::m1", "pkg2/lygia::math", "pkg1/lygia::math"),
224        ("super", "super::foo", "super::super::foo"),
225        ("super::m1::m2::m3", "super::super::m4", "super::m1::m4"),
226        ("super", "self::foo", "super::foo"),
227        ("self", "super::foo", "super::foo"),
228    ];
229
230    for (parent, child, expect) in cases {
231        let parent = ModulePath::from_str(parent).unwrap();
232        let child = ModulePath::from_str(child).unwrap();
233        let expect = ModulePath::from_str(expect).unwrap();
234        println!("testing ModulePath::join_path({parent}, {child}) -> {expect}");
235        assert_eq!(parent.join_path(&child), expect);
236    }
237}
238
239#[cfg(feature = "imports")]
240#[derive(Clone, Copy, PartialEq, Eq, Debug, thiserror::Error)]
241pub enum ModulePathParseError {
242    #[error("module name cannot be empty")]
243    Empty,
244    #[error("`package` must be a prefix of the module path")]
245    MisplacedPackage,
246    #[error("`self` must be a prefix of the module path")]
247    MisplacedSelf,
248    #[error("`super` must be a prefix of the module path")]
249    MisplacedSuper,
250}
251
252#[cfg(feature = "imports")]
253impl std::str::FromStr for ModulePath {
254    type Err = ModulePathParseError;
255
256    /// Parse a WGSL string into a module path.
257    ///
258    /// Preconditions:
259    /// * The path components must be valid WESL module names.
260    fn from_str(s: &str) -> Result<Self, Self::Err> {
261        let mut parts = s.split("::").peekable();
262
263        let origin = match parts.next() {
264            Some("package") => PathOrigin::Absolute,
265            Some("self") => PathOrigin::Relative(0),
266            Some("super") => {
267                let mut n = 1;
268                while let Some(&"super") = parts.peek() {
269                    n += 1;
270                    parts.next().unwrap();
271                }
272                PathOrigin::Relative(n)
273            }
274            Some("") | None => return Err(ModulePathParseError::Empty),
275            Some(name) => PathOrigin::Package(name.to_string()),
276        };
277
278        let components = parts
279            .map(|part| match part {
280                "package" => Err(ModulePathParseError::MisplacedPackage),
281                "self" => Err(ModulePathParseError::MisplacedSelf),
282                "super" => Err(ModulePathParseError::MisplacedSuper),
283                _ => Ok(part.to_string()),
284            })
285            .collect::<Result<Vec<_>, _>>()?;
286
287        Ok(Self { origin, components })
288    }
289}
290
291#[cfg(feature = "imports")]
292#[test]
293fn test_module_path_fromstr() {
294    use std::str::FromStr;
295
296    let ok_cases = [
297        ("self", ModulePath::new(PathOrigin::Relative(0), vec![])),
298        ("super", ModulePath::new(PathOrigin::Relative(1), vec![])),
299        ("package", ModulePath::new(PathOrigin::Absolute, vec![])),
300        (
301            "a",
302            ModulePath::new(PathOrigin::Package("a".to_string()), vec![]),
303        ),
304        (
305            "super::super::a",
306            ModulePath::new(PathOrigin::Relative(2), vec!["a".to_string()]),
307        ),
308    ];
309    let err_cases = [
310        ("", ModulePathParseError::Empty),
311        ("a::super", ModulePathParseError::MisplacedSuper),
312        ("super::self", ModulePathParseError::MisplacedSelf),
313        ("self::package", ModulePathParseError::MisplacedPackage),
314    ];
315
316    for (s, m) in ok_cases {
317        assert_eq!(ModulePath::from_str(s), Ok(m))
318    }
319    for (s, e) in err_cases {
320        assert_eq!(ModulePath::from_str(s), Err(e))
321    }
322}
323
324impl GlobalDeclaration {
325    /// Remove all [`Statement::Void`]
326    pub fn remove_voids(&mut self) {
327        if let GlobalDeclaration::Function(decl) = self {
328            decl.body.remove_voids();
329        }
330    }
331}
332
333impl TypeAlias {
334    pub fn new(ident: Ident, ty: TypeExpression) -> Self {
335        Self {
336            #[cfg(feature = "attributes")]
337            attributes: Default::default(),
338            ident,
339            ty,
340        }
341    }
342}
343
344impl Struct {
345    pub fn new(ident: Ident) -> Self {
346        Self {
347            #[cfg(feature = "attributes")]
348            attributes: Default::default(),
349            ident,
350            members: Default::default(),
351        }
352    }
353}
354
355impl StructMember {
356    pub fn new(ident: Ident, ty: TypeExpression) -> Self {
357        Self {
358            attributes: Default::default(),
359            ident,
360            ty,
361        }
362    }
363}
364
365impl Function {
366    pub fn new(ident: Ident) -> Self {
367        Self {
368            attributes: Default::default(),
369            ident,
370            parameters: Default::default(),
371            return_attributes: Default::default(),
372            return_type: Default::default(),
373            body: Default::default(),
374        }
375    }
376}
377
378impl FormalParameter {
379    pub fn new(ident: Ident, ty: TypeExpression) -> Self {
380        Self {
381            attributes: Default::default(),
382            ident,
383            ty,
384        }
385    }
386}
387
388impl ConstAssert {
389    pub fn new(expression: Expression) -> Self {
390        Self {
391            #[cfg(feature = "attributes")]
392            attributes: Default::default(),
393            expression: expression.into(),
394        }
395    }
396}
397
398impl TypeExpression {
399    /// New [`TypeExpression`] with no template.
400    pub fn new(ident: Ident) -> Self {
401        Self {
402            #[cfg(feature = "imports")]
403            path: None,
404            ident,
405            template_args: None,
406        }
407    }
408}
409
410impl CompoundStatement {
411    /// Remove all [`Statement::Void`]
412    pub fn remove_voids(&mut self) {
413        self.statements.retain_mut(|stmt| match stmt.node_mut() {
414            Statement::Void => false,
415            _ => {
416                stmt.remove_voids();
417                true
418            }
419        })
420    }
421}
422
423impl Statement {
424    /// Remove all [`Statement::Void`]
425    pub fn remove_voids(&mut self) {
426        match self {
427            Statement::Compound(stmt) => {
428                stmt.remove_voids();
429            }
430            Statement::If(stmt) => {
431                stmt.if_clause.body.remove_voids();
432                for clause in &mut stmt.else_if_clauses {
433                    clause.body.remove_voids();
434                }
435                if let Some(clause) = &mut stmt.else_clause {
436                    clause.body.remove_voids();
437                }
438            }
439            Statement::Switch(stmt) => stmt
440                .clauses
441                .iter_mut()
442                .for_each(|clause| clause.body.remove_voids()),
443            Statement::Loop(stmt) => stmt.body.remove_voids(),
444            Statement::For(stmt) => stmt.body.remove_voids(),
445            Statement::While(stmt) => stmt.body.remove_voids(),
446            _ => (),
447        }
448    }
449}
450
451impl From<Ident> for TypeExpression {
452    fn from(ident: Ident) -> Self {
453        Self::new(ident)
454    }
455}
456
457impl From<ExpressionNode> for ReturnStatement {
458    fn from(expression: ExpressionNode) -> Self {
459        Self {
460            #[cfg(feature = "attributes")]
461            attributes: Default::default(),
462            expression: Some(expression),
463        }
464    }
465}
466impl From<Expression> for ReturnStatement {
467    fn from(expression: Expression) -> Self {
468        Self::from(ExpressionNode::from(expression))
469    }
470}
471
472impl From<FunctionCall> for FunctionCallStatement {
473    fn from(call: FunctionCall) -> Self {
474        Self {
475            #[cfg(feature = "attributes")]
476            attributes: Default::default(),
477            call,
478        }
479    }
480}
481
482// Transitive `From` implementations.
483// They have to be implemented manually unfortunately.
484
485macro_rules! impl_transitive_from {
486    ($from:ident => $middle:ident => $into:ident) => {
487        impl From<$from> for $into {
488            fn from(value: $from) -> Self {
489                $into::from($middle::from(value))
490            }
491        }
492    };
493}
494
495impl_transitive_from!(bool => LiteralExpression => Expression);
496impl_transitive_from!(i64 => LiteralExpression => Expression);
497impl_transitive_from!(f64 => LiteralExpression => Expression);
498impl_transitive_from!(i32 => LiteralExpression => Expression);
499impl_transitive_from!(u32 => LiteralExpression => Expression);
500impl_transitive_from!(f32 => LiteralExpression => Expression);
501impl_transitive_from!(Ident => TypeExpression => Expression);
502
503/// Trait implemented for all syntax node types.
504///
505/// This trait is useful for generic implementations over different syntax node types.
506/// Node types that do not have a span, an ident, or attributes return `None`.
507pub trait SyntaxNode {
508    /// Span of a syntax node.
509    fn span(&self) -> Option<Span> {
510        None
511    }
512
513    /// Identifier, if the syntax node is a declaration.
514    fn ident(&self) -> Option<Ident> {
515        None
516    }
517
518    /// List all attributes of a syntax node.
519    fn attributes(&self) -> &[AttributeNode] {
520        &[]
521    }
522    /// List all attributes of a syntax node.
523    fn attributes_mut(&mut self) -> &mut [AttributeNode] {
524        &mut []
525    }
526    /// Whether the node contains an attribute.
527    fn contains_attribute(&self, attribute: &Attribute) -> bool {
528        self.attributes().iter().any(|v| v.node() == attribute)
529    }
530    /// Remove attributes with predicate.
531    fn retain_attributes_mut<F>(&mut self, _predicate: F)
532    where
533        F: FnMut(&mut Attribute) -> bool,
534    {
535    }
536}
537
538impl<T: SyntaxNode> SyntaxNode for Spanned<T> {
539    fn span(&self) -> Option<Span> {
540        Some(self.span())
541    }
542
543    fn ident(&self) -> Option<Ident> {
544        self.node().ident()
545    }
546
547    fn attributes(&self) -> &[AttributeNode] {
548        self.node().attributes()
549    }
550
551    fn attributes_mut(&mut self) -> &mut [AttributeNode] {
552        self.node_mut().attributes_mut()
553    }
554
555    fn retain_attributes_mut<F>(&mut self, mut f: F)
556    where
557        F: FnMut(&mut Attribute) -> bool,
558    {
559        self.node_mut().retain_attributes_mut(|v| f(v))
560    }
561}
562
563macro_rules! impl_attrs_struct {
564    () => {
565        fn attributes(&self) -> &[AttributeNode] {
566            &self.attributes
567        }
568        fn attributes_mut(&mut self) -> &mut [AttributeNode] {
569            &mut self.attributes
570        }
571        fn retain_attributes_mut<F>(&mut self, mut f: F)
572        where
573            F: FnMut(&mut Attribute) -> bool,
574        {
575            self.attributes.retain_mut(|v| f(v))
576        }
577    };
578}
579
580macro_rules! impl_attrs_enum {
581    ($($variant: path),* $(,)?) => {
582        fn attributes(&self) -> &[AttributeNode] {
583            match self {
584                $(
585                    $variant(x) => &x.attributes,
586                )*
587                #[allow(unreachable_patterns)]
588                _ => &[]
589            }
590        }
591        fn attributes_mut(&mut self) -> &mut [AttributeNode] {
592            match self {
593                $(
594                    $variant(x) => &mut x.attributes,
595                )*
596                #[allow(unreachable_patterns)]
597                _ => &mut []
598            }
599        }
600        fn retain_attributes_mut<F>(&mut self, mut f: F)
601        where
602            F: FnMut(&mut Attribute) -> bool,
603        {
604            match self {
605                $(
606                    $variant(x) => x.attributes.retain_mut(|v| f(v)),
607                )*
608                #[allow(unreachable_patterns)]
609                _ => {}
610            }
611        }
612    };
613}
614
615#[cfg(feature = "imports")]
616impl SyntaxNode for ImportStatement {
617    #[cfg(feature = "attributes")]
618    impl_attrs_struct! {}
619}
620
621impl SyntaxNode for GlobalDirective {
622    #[cfg(feature = "attributes")]
623    impl_attrs_enum! {
624        GlobalDirective::Diagnostic,
625        GlobalDirective::Enable,
626        GlobalDirective::Requires
627    }
628}
629
630impl SyntaxNode for DiagnosticDirective {
631    #[cfg(feature = "attributes")]
632    impl_attrs_struct! {}
633}
634
635impl SyntaxNode for EnableDirective {
636    #[cfg(feature = "attributes")]
637    impl_attrs_struct! {}
638}
639
640impl SyntaxNode for RequiresDirective {
641    #[cfg(feature = "attributes")]
642    impl_attrs_struct! {}
643}
644
645impl SyntaxNode for GlobalDeclaration {
646    fn ident(&self) -> Option<Ident> {
647        match self {
648            GlobalDeclaration::Void => None,
649            GlobalDeclaration::Declaration(decl) => Some(decl.ident.clone()),
650            GlobalDeclaration::TypeAlias(decl) => Some(decl.ident.clone()),
651            GlobalDeclaration::Struct(decl) => Some(decl.ident.clone()),
652            GlobalDeclaration::Function(decl) => Some(decl.ident.clone()),
653            GlobalDeclaration::ConstAssert(_) => None,
654        }
655    }
656
657    #[cfg(feature = "attributes")]
658    impl_attrs_enum! {
659        GlobalDeclaration::Declaration,
660        GlobalDeclaration::TypeAlias,
661        GlobalDeclaration::Struct,
662        GlobalDeclaration::Function,
663        GlobalDeclaration::ConstAssert,
664    }
665}
666
667impl SyntaxNode for Declaration {
668    fn ident(&self) -> Option<Ident> {
669        Some(self.ident.clone())
670    }
671
672    impl_attrs_struct! {}
673}
674
675impl SyntaxNode for TypeAlias {
676    fn ident(&self) -> Option<Ident> {
677        Some(self.ident.clone())
678    }
679
680    #[cfg(feature = "attributes")]
681    impl_attrs_struct! {}
682}
683
684impl SyntaxNode for Struct {
685    fn ident(&self) -> Option<Ident> {
686        Some(self.ident.clone())
687    }
688
689    #[cfg(feature = "attributes")]
690    impl_attrs_struct! {}
691}
692
693impl SyntaxNode for StructMember {
694    fn ident(&self) -> Option<Ident> {
695        Some(self.ident.clone())
696    }
697
698    impl_attrs_struct! {}
699}
700
701impl SyntaxNode for Function {
702    fn ident(&self) -> Option<Ident> {
703        Some(self.ident.clone())
704    }
705
706    impl_attrs_struct! {}
707}
708
709impl SyntaxNode for FormalParameter {
710    fn ident(&self) -> Option<Ident> {
711        Some(self.ident.clone())
712    }
713
714    impl_attrs_struct! {}
715}
716
717impl SyntaxNode for ConstAssert {
718    #[cfg(feature = "attributes")]
719    impl_attrs_struct! {}
720}
721
722impl SyntaxNode for Expression {}
723impl SyntaxNode for LiteralExpression {}
724impl SyntaxNode for ParenthesizedExpression {}
725impl SyntaxNode for NamedComponentExpression {}
726impl SyntaxNode for IndexingExpression {}
727impl SyntaxNode for UnaryExpression {}
728impl SyntaxNode for BinaryExpression {}
729impl SyntaxNode for FunctionCall {}
730impl SyntaxNode for TypeExpression {}
731
732impl SyntaxNode for Statement {
733    #[cfg(feature = "attributes")]
734    impl_attrs_enum! {
735        Statement::Compound,
736        Statement::Assignment,
737        Statement::Increment,
738        Statement::Decrement,
739        Statement::If,
740        Statement::Switch,
741        Statement::Loop,
742        Statement::For,
743        Statement::While,
744        Statement::Break,
745        Statement::Continue,
746        Statement::Return,
747        Statement::Discard,
748        Statement::FunctionCall,
749        Statement::ConstAssert,
750        Statement::Declaration,
751    }
752}
753
754impl SyntaxNode for CompoundStatement {
755    impl_attrs_struct! {}
756}
757
758impl SyntaxNode for AssignmentStatement {
759    #[cfg(feature = "attributes")]
760    impl_attrs_struct! {}
761}
762
763impl SyntaxNode for IncrementStatement {
764    #[cfg(feature = "attributes")]
765    impl_attrs_struct! {}
766}
767
768impl SyntaxNode for DecrementStatement {
769    #[cfg(feature = "attributes")]
770    impl_attrs_struct! {}
771}
772
773impl SyntaxNode for IfStatement {
774    impl_attrs_struct! {}
775}
776
777impl SyntaxNode for IfClause {}
778
779impl SyntaxNode for ElseIfClause {
780    #[cfg(feature = "attributes")]
781    impl_attrs_struct! {}
782}
783
784impl SyntaxNode for ElseClause {
785    #[cfg(feature = "attributes")]
786    impl_attrs_struct! {}
787}
788
789impl SyntaxNode for SwitchStatement {
790    impl_attrs_struct! {}
791}
792
793impl SyntaxNode for SwitchClause {
794    #[cfg(feature = "attributes")]
795    impl_attrs_struct! {}
796}
797
798impl SyntaxNode for LoopStatement {
799    impl_attrs_struct! {}
800}
801
802impl SyntaxNode for ContinuingStatement {
803    #[cfg(feature = "attributes")]
804    impl_attrs_struct! {}
805}
806
807impl SyntaxNode for BreakIfStatement {
808    #[cfg(feature = "attributes")]
809    impl_attrs_struct! {}
810}
811
812impl SyntaxNode for ForStatement {
813    impl_attrs_struct! {}
814}
815
816impl SyntaxNode for WhileStatement {
817    impl_attrs_struct! {}
818}
819
820impl SyntaxNode for BreakStatement {
821    #[cfg(feature = "attributes")]
822    impl_attrs_struct! {}
823}
824
825impl SyntaxNode for ContinueStatement {
826    #[cfg(feature = "attributes")]
827    impl_attrs_struct! {}
828}
829
830impl SyntaxNode for ReturnStatement {
831    #[cfg(feature = "attributes")]
832    impl_attrs_struct! {}
833}
834
835impl SyntaxNode for DiscardStatement {
836    #[cfg(feature = "attributes")]
837    impl_attrs_struct! {}
838}
839
840impl SyntaxNode for FunctionCallStatement {
841    #[cfg(feature = "attributes")]
842    impl_attrs_struct! {}
843}