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                        // Importing a sub-package. This is a hack: we rename the package to
171                        // parent/child, which cannot be spelled in code.
172                        let origin = PathOrigin::Package(format!("{self_pkg}/{suffix_pkg}"));
173                        let components = suffix.components.clone();
174                        Self { origin, components }
175                    }
176                }
177            }
178        }
179    }
180
181    /// Whether the module path starts with a `prefix`.
182    pub fn starts_with(&self, prefix: &Self) -> bool {
183        self.origin == prefix.origin
184            && self.components.len() >= prefix.components.len()
185            && prefix
186                .components
187                .iter()
188                .zip(&self.components)
189                .all(|(a, b)| a == b)
190    }
191
192    /// Whether the module path points at the route module.
193    ///
194    /// See [`Self::new_root`].
195    pub fn is_root(&self) -> bool {
196        self.origin.is_absolute() && self.components.is_empty()
197    }
198}
199
200#[cfg(feature = "imports")]
201#[test]
202fn test_module_path_join() {
203    use std::str::FromStr;
204    // TODO: move this test and join_paths impl to ModulePath::join_path
205    let cases = [
206        ("package::m1", "package::foo", "package::foo"),
207        ("package::m1", "self::foo", "package::m1::foo"),
208        ("package::m1", "super::foo", "package::foo"),
209        ("pkg::m1::m2", "package::foo", "pkg::foo"),
210        ("pkg::m1::m2", "self::foo", "pkg::m1::m2::foo"),
211        ("pkg::m1::m2", "super::foo", "pkg::m1::foo"),
212        ("pkg::m1", "super::super::foo", "pkg::foo"),
213        ("super", "super::foo", "super::super::foo"),
214        ("super::m1::m2::m3", "super::super::m4", "super::m1::m4"),
215        ("super", "self::foo", "super::foo"),
216        ("self", "super::foo", "super::foo"),
217    ];
218
219    for (parent, child, expect) in cases {
220        let parent = ModulePath::from_str(parent).unwrap();
221        let child = ModulePath::from_str(child).unwrap();
222        let expect = ModulePath::from_str(expect).unwrap();
223        println!("testing join_paths({parent}, {child}) -> {expect}");
224        assert_eq!(parent.join_path(&child), expect);
225    }
226}
227
228#[cfg(feature = "imports")]
229#[derive(Clone, Copy, PartialEq, Eq, Debug, thiserror::Error)]
230pub enum ModulePathParseError {
231    #[error("module name cannot be empty")]
232    Empty,
233    #[error("`package` must be a prefix of the module path")]
234    MisplacedPackage,
235    #[error("`self` must be a prefix of the module path")]
236    MisplacedSelf,
237    #[error("`super` must be a prefix of the module path")]
238    MisplacedSuper,
239}
240
241#[cfg(feature = "imports")]
242impl std::str::FromStr for ModulePath {
243    type Err = ModulePathParseError;
244
245    /// Parse a WGSL string into a module path.
246    ///
247    /// Preconditions:
248    /// * The path components must be valid WESL module names.
249    fn from_str(s: &str) -> Result<Self, Self::Err> {
250        let mut parts = s.split("::").peekable();
251
252        let origin = match parts.next() {
253            Some("package") => PathOrigin::Absolute,
254            Some("self") => PathOrigin::Relative(0),
255            Some("super") => {
256                let mut n = 1;
257                while let Some(&"super") = parts.peek() {
258                    n += 1;
259                    parts.next().unwrap();
260                }
261                PathOrigin::Relative(n)
262            }
263            Some("") | None => return Err(ModulePathParseError::Empty),
264            Some(name) => PathOrigin::Package(name.to_string()),
265        };
266
267        let components = parts
268            .map(|part| match part {
269                "package" => Err(ModulePathParseError::MisplacedPackage),
270                "self" => Err(ModulePathParseError::MisplacedSelf),
271                "super" => Err(ModulePathParseError::MisplacedSuper),
272                _ => Ok(part.to_string()),
273            })
274            .collect::<Result<Vec<_>, _>>()?;
275
276        Ok(Self { origin, components })
277    }
278}
279
280#[cfg(feature = "imports")]
281#[test]
282fn test_module_path_fromstr() {
283    use std::str::FromStr;
284
285    let ok_cases = [
286        ("self", ModulePath::new(PathOrigin::Relative(0), vec![])),
287        ("super", ModulePath::new(PathOrigin::Relative(1), vec![])),
288        ("package", ModulePath::new(PathOrigin::Absolute, vec![])),
289        (
290            "a",
291            ModulePath::new(PathOrigin::Package("a".to_string()), vec![]),
292        ),
293        (
294            "super::super::a",
295            ModulePath::new(PathOrigin::Relative(2), vec!["a".to_string()]),
296        ),
297    ];
298    let err_cases = [
299        ("", ModulePathParseError::Empty),
300        ("a::super", ModulePathParseError::MisplacedSuper),
301        ("super::self", ModulePathParseError::MisplacedSelf),
302        ("self::package", ModulePathParseError::MisplacedPackage),
303    ];
304
305    for (s, m) in ok_cases {
306        assert_eq!(ModulePath::from_str(s), Ok(m))
307    }
308    for (s, e) in err_cases {
309        assert_eq!(ModulePath::from_str(s), Err(e))
310    }
311}
312
313impl GlobalDeclaration {
314    /// Remove all [`Statement::Void`]
315    pub fn remove_voids(&mut self) {
316        if let GlobalDeclaration::Function(decl) = self {
317            decl.body.remove_voids();
318        }
319    }
320}
321
322impl TypeAlias {
323    pub fn new(ident: Ident, ty: TypeExpression) -> Self {
324        Self {
325            #[cfg(feature = "attributes")]
326            attributes: Default::default(),
327            ident,
328            ty,
329        }
330    }
331}
332
333impl Struct {
334    pub fn new(ident: Ident) -> Self {
335        Self {
336            #[cfg(feature = "attributes")]
337            attributes: Default::default(),
338            ident,
339            members: Default::default(),
340        }
341    }
342}
343
344impl StructMember {
345    pub fn new(ident: Ident, ty: TypeExpression) -> Self {
346        Self {
347            attributes: Default::default(),
348            ident,
349            ty,
350        }
351    }
352}
353
354impl Function {
355    pub fn new(ident: Ident) -> Self {
356        Self {
357            attributes: Default::default(),
358            ident,
359            parameters: Default::default(),
360            return_attributes: Default::default(),
361            return_type: Default::default(),
362            body: Default::default(),
363        }
364    }
365}
366
367impl FormalParameter {
368    pub fn new(ident: Ident, ty: TypeExpression) -> Self {
369        Self {
370            attributes: Default::default(),
371            ident,
372            ty,
373        }
374    }
375}
376
377impl ConstAssert {
378    pub fn new(expression: Expression) -> Self {
379        Self {
380            #[cfg(feature = "attributes")]
381            attributes: Default::default(),
382            expression: expression.into(),
383        }
384    }
385}
386
387impl TypeExpression {
388    /// New [`TypeExpression`] with no template.
389    pub fn new(ident: Ident) -> Self {
390        Self {
391            #[cfg(feature = "imports")]
392            path: None,
393            ident,
394            template_args: None,
395        }
396    }
397}
398
399impl CompoundStatement {
400    /// Remove all [`Statement::Void`]
401    pub fn remove_voids(&mut self) {
402        self.statements.retain_mut(|stmt| match stmt.node_mut() {
403            Statement::Void => false,
404            _ => {
405                stmt.remove_voids();
406                true
407            }
408        })
409    }
410}
411
412impl Statement {
413    /// Remove all [`Statement::Void`]
414    pub fn remove_voids(&mut self) {
415        match self {
416            Statement::Compound(stmt) => {
417                stmt.remove_voids();
418            }
419            Statement::If(stmt) => {
420                stmt.if_clause.body.remove_voids();
421                for clause in &mut stmt.else_if_clauses {
422                    clause.body.remove_voids();
423                }
424                if let Some(clause) = &mut stmt.else_clause {
425                    clause.body.remove_voids();
426                }
427            }
428            Statement::Switch(stmt) => stmt
429                .clauses
430                .iter_mut()
431                .for_each(|clause| clause.body.remove_voids()),
432            Statement::Loop(stmt) => stmt.body.remove_voids(),
433            Statement::For(stmt) => stmt.body.remove_voids(),
434            Statement::While(stmt) => stmt.body.remove_voids(),
435            _ => (),
436        }
437    }
438}
439
440impl From<Ident> for TypeExpression {
441    fn from(ident: Ident) -> Self {
442        Self::new(ident)
443    }
444}
445
446impl From<ExpressionNode> for ReturnStatement {
447    fn from(expression: ExpressionNode) -> Self {
448        Self {
449            #[cfg(feature = "attributes")]
450            attributes: Default::default(),
451            expression: Some(expression),
452        }
453    }
454}
455impl From<Expression> for ReturnStatement {
456    fn from(expression: Expression) -> Self {
457        Self::from(ExpressionNode::from(expression))
458    }
459}
460
461impl From<FunctionCall> for FunctionCallStatement {
462    fn from(call: FunctionCall) -> Self {
463        Self {
464            #[cfg(feature = "attributes")]
465            attributes: Default::default(),
466            call,
467        }
468    }
469}
470
471// Transitive `From` implementations.
472// They have to be implemented manually unfortunately.
473
474macro_rules! impl_transitive_from {
475    ($from:ident => $middle:ident => $into:ident) => {
476        impl From<$from> for $into {
477            fn from(value: $from) -> Self {
478                $into::from($middle::from(value))
479            }
480        }
481    };
482}
483
484impl_transitive_from!(bool => LiteralExpression => Expression);
485impl_transitive_from!(i64 => LiteralExpression => Expression);
486impl_transitive_from!(f64 => LiteralExpression => Expression);
487impl_transitive_from!(i32 => LiteralExpression => Expression);
488impl_transitive_from!(u32 => LiteralExpression => Expression);
489impl_transitive_from!(f32 => LiteralExpression => Expression);
490impl_transitive_from!(Ident => TypeExpression => Expression);
491
492/// Trait implemented for all syntax node types.
493///
494/// This trait is useful for generic implementations over different syntax node types.
495/// Node types that do not have a span, an ident, or attributes return `None`.
496pub trait SyntaxNode {
497    /// Span of a syntax node.
498    fn span(&self) -> Option<Span> {
499        None
500    }
501
502    /// Identifier, if the syntax node is a declaration.
503    fn ident(&self) -> Option<Ident> {
504        None
505    }
506
507    /// List all attributes of a syntax node.
508    fn attributes(&self) -> &[AttributeNode] {
509        &[]
510    }
511    /// List all attributes of a syntax node.
512    fn attributes_mut(&mut self) -> &mut [AttributeNode] {
513        &mut []
514    }
515    /// Whether the node contains an attribute.
516    fn contains_attribute(&self, attribute: &Attribute) -> bool {
517        self.attributes().iter().any(|v| v.node() == attribute)
518    }
519    /// Remove attributes with predicate.
520    fn retain_attributes_mut<F>(&mut self, _predicate: F)
521    where
522        F: FnMut(&mut Attribute) -> bool,
523    {
524    }
525}
526
527impl<T: SyntaxNode> SyntaxNode for Spanned<T> {
528    fn span(&self) -> Option<Span> {
529        Some(self.span())
530    }
531
532    fn ident(&self) -> Option<Ident> {
533        self.node().ident()
534    }
535
536    fn attributes(&self) -> &[AttributeNode] {
537        self.node().attributes()
538    }
539
540    fn attributes_mut(&mut self) -> &mut [AttributeNode] {
541        self.node_mut().attributes_mut()
542    }
543
544    fn retain_attributes_mut<F>(&mut self, mut f: F)
545    where
546        F: FnMut(&mut Attribute) -> bool,
547    {
548        self.node_mut().retain_attributes_mut(|v| f(v))
549    }
550}
551
552macro_rules! impl_attrs_struct {
553    () => {
554        fn attributes(&self) -> &[AttributeNode] {
555            &self.attributes
556        }
557        fn attributes_mut(&mut self) -> &mut [AttributeNode] {
558            &mut self.attributes
559        }
560        fn retain_attributes_mut<F>(&mut self, mut f: F)
561        where
562            F: FnMut(&mut Attribute) -> bool,
563        {
564            self.attributes.retain_mut(|v| f(v))
565        }
566    };
567}
568
569macro_rules! impl_attrs_enum {
570    ($($variant: path),* $(,)?) => {
571        fn attributes(&self) -> &[AttributeNode] {
572            match self {
573                $(
574                    $variant(x) => &x.attributes,
575                )*
576                #[allow(unreachable_patterns)]
577                _ => &[]
578            }
579        }
580        fn attributes_mut(&mut self) -> &mut [AttributeNode] {
581            match self {
582                $(
583                    $variant(x) => &mut x.attributes,
584                )*
585                #[allow(unreachable_patterns)]
586                _ => &mut []
587            }
588        }
589        fn retain_attributes_mut<F>(&mut self, mut f: F)
590        where
591            F: FnMut(&mut Attribute) -> bool,
592        {
593            match self {
594                $(
595                    $variant(x) => x.attributes.retain_mut(|v| f(v)),
596                )*
597                #[allow(unreachable_patterns)]
598                _ => {}
599            }
600        }
601    };
602}
603
604#[cfg(feature = "imports")]
605impl SyntaxNode for ImportStatement {
606    #[cfg(feature = "attributes")]
607    impl_attrs_struct! {}
608}
609
610impl SyntaxNode for GlobalDirective {
611    #[cfg(feature = "attributes")]
612    impl_attrs_enum! {
613        GlobalDirective::Diagnostic,
614        GlobalDirective::Enable,
615        GlobalDirective::Requires
616    }
617}
618
619impl SyntaxNode for DiagnosticDirective {
620    #[cfg(feature = "attributes")]
621    impl_attrs_struct! {}
622}
623
624impl SyntaxNode for EnableDirective {
625    #[cfg(feature = "attributes")]
626    impl_attrs_struct! {}
627}
628
629impl SyntaxNode for RequiresDirective {
630    #[cfg(feature = "attributes")]
631    impl_attrs_struct! {}
632}
633
634impl SyntaxNode for GlobalDeclaration {
635    fn ident(&self) -> Option<Ident> {
636        match self {
637            GlobalDeclaration::Void => None,
638            GlobalDeclaration::Declaration(decl) => Some(decl.ident.clone()),
639            GlobalDeclaration::TypeAlias(decl) => Some(decl.ident.clone()),
640            GlobalDeclaration::Struct(decl) => Some(decl.ident.clone()),
641            GlobalDeclaration::Function(decl) => Some(decl.ident.clone()),
642            GlobalDeclaration::ConstAssert(_) => None,
643        }
644    }
645
646    #[cfg(feature = "attributes")]
647    impl_attrs_enum! {
648        GlobalDeclaration::Declaration,
649        GlobalDeclaration::TypeAlias,
650        GlobalDeclaration::Struct,
651        GlobalDeclaration::Function,
652        GlobalDeclaration::ConstAssert,
653    }
654}
655
656impl SyntaxNode for Declaration {
657    fn ident(&self) -> Option<Ident> {
658        Some(self.ident.clone())
659    }
660
661    impl_attrs_struct! {}
662}
663
664impl SyntaxNode for TypeAlias {
665    fn ident(&self) -> Option<Ident> {
666        Some(self.ident.clone())
667    }
668
669    #[cfg(feature = "attributes")]
670    impl_attrs_struct! {}
671}
672
673impl SyntaxNode for Struct {
674    fn ident(&self) -> Option<Ident> {
675        Some(self.ident.clone())
676    }
677
678    #[cfg(feature = "attributes")]
679    impl_attrs_struct! {}
680}
681
682impl SyntaxNode for StructMember {
683    fn ident(&self) -> Option<Ident> {
684        Some(self.ident.clone())
685    }
686
687    impl_attrs_struct! {}
688}
689
690impl SyntaxNode for Function {
691    fn ident(&self) -> Option<Ident> {
692        Some(self.ident.clone())
693    }
694
695    impl_attrs_struct! {}
696}
697
698impl SyntaxNode for FormalParameter {
699    fn ident(&self) -> Option<Ident> {
700        Some(self.ident.clone())
701    }
702
703    impl_attrs_struct! {}
704}
705
706impl SyntaxNode for ConstAssert {
707    #[cfg(feature = "attributes")]
708    impl_attrs_struct! {}
709}
710
711impl SyntaxNode for Expression {}
712impl SyntaxNode for LiteralExpression {}
713impl SyntaxNode for ParenthesizedExpression {}
714impl SyntaxNode for NamedComponentExpression {}
715impl SyntaxNode for IndexingExpression {}
716impl SyntaxNode for UnaryExpression {}
717impl SyntaxNode for BinaryExpression {}
718impl SyntaxNode for FunctionCall {}
719impl SyntaxNode for TypeExpression {}
720
721impl SyntaxNode for Statement {
722    #[cfg(feature = "attributes")]
723    impl_attrs_enum! {
724        Statement::Compound,
725        Statement::Assignment,
726        Statement::Increment,
727        Statement::Decrement,
728        Statement::If,
729        Statement::Switch,
730        Statement::Loop,
731        Statement::For,
732        Statement::While,
733        Statement::Break,
734        Statement::Continue,
735        Statement::Return,
736        Statement::Discard,
737        Statement::FunctionCall,
738        Statement::ConstAssert,
739        Statement::Declaration,
740    }
741}
742
743impl SyntaxNode for CompoundStatement {
744    impl_attrs_struct! {}
745}
746
747impl SyntaxNode for AssignmentStatement {
748    #[cfg(feature = "attributes")]
749    impl_attrs_struct! {}
750}
751
752impl SyntaxNode for IncrementStatement {
753    #[cfg(feature = "attributes")]
754    impl_attrs_struct! {}
755}
756
757impl SyntaxNode for DecrementStatement {
758    #[cfg(feature = "attributes")]
759    impl_attrs_struct! {}
760}
761
762impl SyntaxNode for IfStatement {
763    impl_attrs_struct! {}
764}
765
766impl SyntaxNode for IfClause {}
767
768impl SyntaxNode for ElseIfClause {
769    #[cfg(feature = "attributes")]
770    impl_attrs_struct! {}
771}
772
773impl SyntaxNode for ElseClause {
774    #[cfg(feature = "attributes")]
775    impl_attrs_struct! {}
776}
777
778impl SyntaxNode for SwitchStatement {
779    impl_attrs_struct! {}
780}
781
782impl SyntaxNode for SwitchClause {
783    #[cfg(feature = "attributes")]
784    impl_attrs_struct! {}
785}
786
787impl SyntaxNode for LoopStatement {
788    impl_attrs_struct! {}
789}
790
791impl SyntaxNode for ContinuingStatement {
792    #[cfg(feature = "attributes")]
793    impl_attrs_struct! {}
794}
795
796impl SyntaxNode for BreakIfStatement {
797    #[cfg(feature = "attributes")]
798    impl_attrs_struct! {}
799}
800
801impl SyntaxNode for ForStatement {
802    impl_attrs_struct! {}
803}
804
805impl SyntaxNode for WhileStatement {
806    impl_attrs_struct! {}
807}
808
809impl SyntaxNode for BreakStatement {
810    #[cfg(feature = "attributes")]
811    impl_attrs_struct! {}
812}
813
814impl SyntaxNode for ContinueStatement {
815    #[cfg(feature = "attributes")]
816    impl_attrs_struct! {}
817}
818
819impl SyntaxNode for ReturnStatement {
820    #[cfg(feature = "attributes")]
821    impl_attrs_struct! {}
822}
823
824impl SyntaxNode for DiscardStatement {
825    #[cfg(feature = "attributes")]
826    impl_attrs_struct! {}
827}
828
829impl SyntaxNode for FunctionCallStatement {
830    #[cfg(feature = "attributes")]
831    impl_attrs_struct! {}
832}