cairo_lang_syntax/node/
helpers.rs

1use cairo_lang_utils::LookupIntern;
2use smol_str::SmolStr;
3
4use super::ast::{
5    self, FunctionDeclaration, FunctionDeclarationGreen, FunctionWithBody, FunctionWithBodyPtr,
6    ImplItem, ItemConstant, ItemEnum, ItemExternFunction, ItemExternFunctionPtr, ItemExternType,
7    ItemImpl, ItemImplAlias, ItemInlineMacro, ItemMacroDeclaration, ItemModule, ItemStruct,
8    ItemTrait, ItemTypeAlias, ItemUse, Member, Modifier, ModuleItem, OptionArgListParenthesized,
9    Statement, StatementBreak, StatementContinue, StatementExpr, StatementLet, StatementReturn,
10    TerminalIdentifier, TerminalIdentifierGreen, TokenIdentifierGreen, TraitItem,
11    TraitItemConstant, TraitItemFunction, TraitItemFunctionPtr, TraitItemImpl, TraitItemType,
12    UsePathLeaf, Variant, WrappedArgList,
13};
14use super::db::SyntaxGroup;
15use super::ids::SyntaxStablePtrId;
16use super::kind::SyntaxKind;
17use super::{SyntaxNode, Terminal, TypedStablePtr, TypedSyntaxNode};
18use crate::node::ast::{Attribute, AttributeList};
19use crate::node::green::GreenNodeDetails;
20
21#[cfg(test)]
22#[path = "helpers_test.rs"]
23mod test;
24
25pub trait GetIdentifier {
26    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr;
27}
28impl ast::UsePathLeafPtr {
29    pub fn name_green(&self, _syntax_db: &dyn SyntaxGroup) -> Self {
30        *self
31    }
32}
33impl GetIdentifier for ast::UsePathLeafPtr {
34    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
35        let alias_clause_green = self.alias_clause_green(db).0;
36        let green_node = alias_clause_green.lookup_intern(db);
37        let children = match &green_node.details {
38            GreenNodeDetails::Node { children, width: _ } => children,
39            _ => panic!("Unexpected token"),
40        };
41        if !children.is_empty() {
42            return ast::TerminalIdentifierGreen(children[ast::AliasClause::INDEX_ALIAS])
43                .identifier(db);
44        }
45        let ident_green = self.ident_green(db);
46        let ident = ident_green.identifier(db);
47        if ident != "self" {
48            return ident;
49        }
50        let mut node = self.0.lookup(db);
51        loop {
52            node = if let Some(parent) = node.parent(db) {
53                parent
54            } else {
55                return ident;
56            };
57            if matches!(node.kind(db), SyntaxKind::UsePathSingle) {
58                return ast::UsePathSingle::from_syntax_node(db, node).ident(db).identifier(db);
59            }
60        }
61    }
62}
63impl GetIdentifier for ast::PathSegmentGreen {
64    /// Retrieves the text of the last identifier in the path.
65    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
66        let green_node = self.0.lookup_intern(db);
67        let children = match &green_node.details {
68            GreenNodeDetails::Node { children, width: _ } => children,
69            _ => panic!("Unexpected token"),
70        };
71        let identifier = ast::TerminalIdentifierGreen(children[0]);
72        identifier.identifier(db)
73    }
74}
75impl GetIdentifier for ast::ExprPathGreen {
76    /// Retrieves the text of the last identifier in the path.
77    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
78        let green_node = self.0.lookup_intern(db);
79        let children = match &green_node.details {
80            GreenNodeDetails::Node { children, width: _ } => children,
81            _ => panic!("Unexpected token"),
82        };
83        let segment_green = ast::ExprPathInnerGreen(*children.last().unwrap());
84        segment_green.identifier(db)
85    }
86}
87
88impl GetIdentifier for ast::ExprPathInnerGreen {
89    /// Retrieves the text of the last identifier in the path.
90    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
91        let green_node = self.0.lookup_intern(db);
92        let children = match &green_node.details {
93            GreenNodeDetails::Node { children, width: _ } => children,
94            _ => panic!("Unexpected token"),
95        };
96        assert_eq!(children.len() & 1, 1, "Expected an odd number of elements in the path.");
97        let segment_green = ast::PathSegmentGreen(*children.last().unwrap());
98        segment_green.identifier(db)
99    }
100}
101impl GetIdentifier for ast::TerminalIdentifierGreen {
102    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
103        match &self.0.lookup_intern(db).details {
104            GreenNodeDetails::Token(_) => "Unexpected token".into(),
105            GreenNodeDetails::Node { children, width: _ } => {
106                TokenIdentifierGreen(children[1]).text(db)
107            }
108        }
109    }
110}
111impl GetIdentifier for ast::ExprPath {
112    /// Retrieves the identifier of the last segment of the path.
113    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
114        self.segments(db).elements(db).next_back().unwrap().identifier(db)
115    }
116}
117
118/// Helper trait for ast::PathSegment.
119pub trait PathSegmentEx {
120    fn identifier_ast(&self, db: &dyn SyntaxGroup) -> ast::TerminalIdentifier;
121    fn generic_args(&self, db: &dyn SyntaxGroup) -> Option<Vec<ast::GenericArg>>;
122}
123impl PathSegmentEx for ast::PathSegment {
124    /// Retrieves the identifier ast of a path segment.
125    fn identifier_ast(&self, db: &dyn SyntaxGroup) -> ast::TerminalIdentifier {
126        match self {
127            ast::PathSegment::Simple(segment) => segment.ident(db),
128            ast::PathSegment::WithGenericArgs(segment) => segment.ident(db),
129            ast::PathSegment::Missing(missing_segment) => missing_segment.ident(db),
130        }
131    }
132    fn generic_args(&self, db: &dyn SyntaxGroup) -> Option<Vec<ast::GenericArg>> {
133        match self {
134            ast::PathSegment::Simple(_) | ast::PathSegment::Missing(_) => None,
135            ast::PathSegment::WithGenericArgs(segment) => {
136                Some(segment.generic_args(db).generic_args(db).elements_vec(db))
137            }
138        }
139    }
140}
141impl GetIdentifier for ast::PathSegment {
142    /// Retrieves the text of the segment (without the generic args).
143    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
144        match self {
145            ast::PathSegment::Simple(segment) => segment.identifier(db),
146            ast::PathSegment::WithGenericArgs(segment) => segment.identifier(db),
147            ast::PathSegment::Missing(missing_segment) => missing_segment.identifier(db),
148        }
149    }
150}
151impl GetIdentifier for ast::PathSegmentSimple {
152    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
153        let green_node = self.as_syntax_node().green_node(db);
154        let GreenNodeDetails::Node { children, .. } = &green_node.details else {
155            panic!("Unexpected token");
156        };
157        TerminalIdentifierGreen(children[0]).identifier(db)
158    }
159}
160impl GetIdentifier for ast::PathSegmentWithGenericArgs {
161    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
162        let green_node = self.as_syntax_node().green_node(db);
163        let GreenNodeDetails::Node { children, .. } = &green_node.details else {
164            panic!("Unexpected token");
165        };
166        TerminalIdentifierGreen(children[0]).identifier(db)
167    }
168}
169
170impl GetIdentifier for ast::PathSegmentMissing {
171    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
172        let green_node = self.as_syntax_node().green_node(db);
173        let GreenNodeDetails::Node { children, .. } = &green_node.details else {
174            panic!("Unexpected token");
175        };
176        TerminalIdentifierGreen(children[0]).identifier(db)
177    }
178}
179
180impl GetIdentifier for ast::Modifier {
181    fn identifier(&self, db: &dyn SyntaxGroup) -> SmolStr {
182        match self {
183            Modifier::Ref(r) => r.text(db),
184            Modifier::Mut(m) => m.text(db),
185        }
186    }
187}
188
189/// Trait for ast object with a name terminal.
190pub trait NameGreen {
191    /// Returns the TerminalIdentifierGreen of the `name` node.
192    fn name_green(self, db: &dyn SyntaxGroup) -> TerminalIdentifierGreen;
193}
194
195impl NameGreen for FunctionDeclarationGreen {
196    fn name_green(self, db: &dyn SyntaxGroup) -> TerminalIdentifierGreen {
197        TerminalIdentifierGreen(
198            self.0.lookup_intern(db).children()[FunctionDeclaration::INDEX_NAME],
199        )
200    }
201}
202
203impl NameGreen for FunctionWithBodyPtr {
204    fn name_green(self, db: &dyn SyntaxGroup) -> TerminalIdentifierGreen {
205        self.declaration_green(db).name_green(db)
206    }
207}
208
209impl NameGreen for ItemExternFunctionPtr {
210    fn name_green(self, db: &dyn SyntaxGroup) -> TerminalIdentifierGreen {
211        self.declaration_green(db).name_green(db)
212    }
213}
214
215impl NameGreen for TraitItemFunctionPtr {
216    fn name_green(self, db: &dyn SyntaxGroup) -> TerminalIdentifierGreen {
217        self.declaration_green(db).name_green(db)
218    }
219}
220
221/// Provides methods to extract a _name_ of AST objects.
222pub trait HasName {
223    /// Gets a [`TerminalIdentifier`] that represents a _name_ of this AST object.
224    fn name(&self, db: &dyn SyntaxGroup) -> ast::TerminalIdentifier;
225}
226
227impl HasName for FunctionWithBody {
228    fn name(&self, db: &dyn SyntaxGroup) -> TerminalIdentifier {
229        self.declaration(db).name(db)
230    }
231}
232
233impl HasName for ItemExternFunction {
234    fn name(&self, db: &dyn SyntaxGroup) -> TerminalIdentifier {
235        self.declaration(db).name(db)
236    }
237}
238
239impl HasName for TraitItemFunction {
240    fn name(&self, db: &dyn SyntaxGroup) -> TerminalIdentifier {
241        self.declaration(db).name(db)
242    }
243}
244
245impl HasName for UsePathLeaf {
246    fn name(&self, db: &dyn SyntaxGroup) -> TerminalIdentifier {
247        match self.alias_clause(db) {
248            ast::OptionAliasClause::Empty(_) => self.ident(db).identifier_ast(db),
249            ast::OptionAliasClause::AliasClause(alias) => alias.alias(db),
250        }
251    }
252}
253
254pub trait GenericParamEx {
255    /// Returns the name of a generic param if one exists.
256    fn name(&self, db: &dyn SyntaxGroup) -> Option<ast::TerminalIdentifier>;
257}
258impl GenericParamEx for ast::GenericParam {
259    fn name(&self, db: &dyn SyntaxGroup) -> Option<ast::TerminalIdentifier> {
260        match self {
261            ast::GenericParam::Type(t) => Some(t.name(db)),
262            ast::GenericParam::Const(c) => Some(c.name(db)),
263            ast::GenericParam::ImplNamed(i) => Some(i.name(db)),
264            ast::GenericParam::ImplAnonymous(_) => None,
265            ast::GenericParam::NegativeImpl(_) => None,
266        }
267    }
268}
269
270/// Checks if the given attribute has a single argument with the given name.
271pub fn is_single_arg_attr(db: &dyn SyntaxGroup, attr: &Attribute, arg_name: &str) -> bool {
272    match attr.arguments(db) {
273        OptionArgListParenthesized::ArgListParenthesized(args) => {
274            matches!(&args.arguments(db).elements_vec(db)[..],
275                    [arg] if arg.as_syntax_node().get_text_without_trivia(db) == arg_name)
276        }
277        OptionArgListParenthesized::Empty(_) => false,
278    }
279}
280
281/// Trait for querying attributes of AST items.
282pub trait QueryAttrs {
283    /// Generic call `self.attributes(db).elements(db)`, wrapped with `Option` for cases where the
284    /// type does not support attributes.
285    ///
286    /// Implementation detail, should not be used by this trait users.
287    #[doc(hidden)]
288    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList>;
289
290    /// Generic call to `self.attributes(db).elements(db)`.
291    fn attributes_elements<'a>(
292        &self,
293        db: &'a dyn SyntaxGroup,
294    ) -> impl Iterator<Item = Attribute> + 'a {
295        self.try_attributes(db).into_iter().flat_map(move |attrs| attrs.elements(db))
296    }
297
298    /// Collect all attributes named exactly `attr` attached to this node.
299    fn query_attr<'a>(
300        &self,
301        db: &'a dyn SyntaxGroup,
302        attr: &'a str,
303    ) -> impl Iterator<Item = Attribute> + 'a {
304        self.attributes_elements(db)
305            .filter(move |a| a.attr(db).as_syntax_node().get_text_without_trivia(db) == attr)
306    }
307
308    /// Find first attribute named exactly `attr` attached do this node.
309    fn find_attr(&self, db: &dyn SyntaxGroup, attr: &str) -> Option<Attribute> {
310        self.query_attr(db, attr).next()
311    }
312
313    /// Check if this node has an attribute named exactly `attr`.
314    fn has_attr(&self, db: &dyn SyntaxGroup, attr: &str) -> bool {
315        self.find_attr(db, attr).is_some()
316    }
317
318    /// Checks if the given object has an attribute with the given name and argument.
319    fn has_attr_with_arg(&self, db: &dyn SyntaxGroup, attr_name: &str, arg_name: &str) -> bool {
320        self.query_attr(db, attr_name).any(|attr| is_single_arg_attr(db, &attr, arg_name))
321    }
322}
323
324impl QueryAttrs for ItemConstant {
325    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
326        Some(self.attributes(db))
327    }
328}
329impl QueryAttrs for ItemModule {
330    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
331        Some(self.attributes(db))
332    }
333}
334impl QueryAttrs for FunctionWithBody {
335    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
336        Some(self.attributes(db))
337    }
338}
339impl QueryAttrs for ItemUse {
340    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
341        Some(self.attributes(db))
342    }
343}
344impl QueryAttrs for ItemExternFunction {
345    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
346        Some(self.attributes(db))
347    }
348}
349impl QueryAttrs for ItemExternType {
350    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
351        Some(self.attributes(db))
352    }
353}
354impl QueryAttrs for ItemTrait {
355    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
356        Some(self.attributes(db))
357    }
358}
359impl QueryAttrs for ItemImpl {
360    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
361        Some(self.attributes(db))
362    }
363}
364impl QueryAttrs for ItemImplAlias {
365    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
366        Some(self.attributes(db))
367    }
368}
369impl QueryAttrs for ItemStruct {
370    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
371        Some(self.attributes(db))
372    }
373}
374impl QueryAttrs for ItemEnum {
375    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
376        Some(self.attributes(db))
377    }
378}
379impl QueryAttrs for ItemTypeAlias {
380    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
381        Some(self.attributes(db))
382    }
383}
384impl QueryAttrs for ItemMacroDeclaration {
385    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
386        Some(self.attributes(db))
387    }
388}
389impl QueryAttrs for TraitItemFunction {
390    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
391        Some(self.attributes(db))
392    }
393}
394impl QueryAttrs for TraitItemType {
395    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
396        Some(self.attributes(db))
397    }
398}
399impl QueryAttrs for TraitItemConstant {
400    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
401        Some(self.attributes(db))
402    }
403}
404impl QueryAttrs for TraitItemImpl {
405    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
406        Some(self.attributes(db))
407    }
408}
409impl QueryAttrs for TraitItem {
410    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
411        match self {
412            TraitItem::Function(item) => Some(item.attributes(db)),
413            TraitItem::Type(item) => Some(item.attributes(db)),
414            TraitItem::Constant(item) => Some(item.attributes(db)),
415            TraitItem::Impl(item) => Some(item.attributes(db)),
416            TraitItem::Missing(_) => None,
417        }
418    }
419}
420
421impl QueryAttrs for ItemInlineMacro {
422    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
423        Some(self.attributes(db))
424    }
425}
426
427impl QueryAttrs for ModuleItem {
428    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
429        match self {
430            ModuleItem::Constant(item) => Some(item.attributes(db)),
431            ModuleItem::Module(item) => Some(item.attributes(db)),
432            ModuleItem::FreeFunction(item) => Some(item.attributes(db)),
433            ModuleItem::Use(item) => Some(item.attributes(db)),
434            ModuleItem::ExternFunction(item) => Some(item.attributes(db)),
435            ModuleItem::ExternType(item) => Some(item.attributes(db)),
436            ModuleItem::Trait(item) => Some(item.attributes(db)),
437            ModuleItem::Impl(item) => Some(item.attributes(db)),
438            ModuleItem::ImplAlias(item) => Some(item.attributes(db)),
439            ModuleItem::Struct(item) => Some(item.attributes(db)),
440            ModuleItem::Enum(item) => Some(item.attributes(db)),
441            ModuleItem::TypeAlias(item) => Some(item.attributes(db)),
442            ModuleItem::InlineMacro(item) => Some(item.attributes(db)),
443            ModuleItem::MacroDeclaration(macro_declaration) => {
444                Some(macro_declaration.attributes(db))
445            }
446            ModuleItem::Missing(_) => None,
447            ModuleItem::HeaderDoc(_) => None,
448        }
449    }
450}
451
452impl QueryAttrs for ImplItem {
453    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
454        match self {
455            ImplItem::Function(item) => Some(item.attributes(db)),
456            ImplItem::Type(item) => Some(item.attributes(db)),
457            ImplItem::Constant(item) => Some(item.attributes(db)),
458            ImplItem::Impl(item) => Some(item.attributes(db)),
459            ImplItem::Module(item) => Some(item.attributes(db)),
460            ImplItem::Use(item) => Some(item.attributes(db)),
461            ImplItem::ExternFunction(item) => Some(item.attributes(db)),
462            ImplItem::ExternType(item) => Some(item.attributes(db)),
463            ImplItem::Trait(item) => Some(item.attributes(db)),
464            ImplItem::Struct(item) => Some(item.attributes(db)),
465            ImplItem::Enum(item) => Some(item.attributes(db)),
466            ImplItem::Missing(_) => None,
467        }
468    }
469}
470
471impl QueryAttrs for AttributeList {
472    fn try_attributes(&self, _db: &dyn SyntaxGroup) -> Option<AttributeList> {
473        Some(self.clone())
474    }
475}
476impl QueryAttrs for Member {
477    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
478        Some(self.attributes(db))
479    }
480}
481
482impl QueryAttrs for Variant {
483    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
484        Some(self.attributes(db))
485    }
486}
487
488impl QueryAttrs for StatementBreak {
489    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
490        Some(self.attributes(db))
491    }
492}
493
494impl QueryAttrs for StatementContinue {
495    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
496        Some(self.attributes(db))
497    }
498}
499
500impl QueryAttrs for StatementReturn {
501    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
502        Some(self.attributes(db))
503    }
504}
505
506impl QueryAttrs for StatementLet {
507    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
508        Some(self.attributes(db))
509    }
510}
511
512impl QueryAttrs for StatementExpr {
513    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
514        Some(self.attributes(db))
515    }
516}
517
518/// Allows querying attributes of a syntax node, any typed node which QueryAttrs is implemented for
519/// should be added here.
520impl QueryAttrs for SyntaxNode {
521    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
522        match self.kind(db) {
523            SyntaxKind::ItemConstant => {
524                Some(ast::ItemConstant::from_syntax_node(db, *self).attributes(db))
525            }
526            SyntaxKind::ItemModule => {
527                Some(ast::ItemModule::from_syntax_node(db, *self).attributes(db))
528            }
529            SyntaxKind::FunctionWithBody => {
530                Some(ast::FunctionWithBody::from_syntax_node(db, *self).attributes(db))
531            }
532            SyntaxKind::ItemUse => Some(ast::ItemUse::from_syntax_node(db, *self).attributes(db)),
533            SyntaxKind::ItemExternFunction => {
534                Some(ast::ItemExternFunction::from_syntax_node(db, *self).attributes(db))
535            }
536            SyntaxKind::ItemExternType => {
537                Some(ast::ItemExternType::from_syntax_node(db, *self).attributes(db))
538            }
539            SyntaxKind::ItemTrait => {
540                Some(ast::ItemTrait::from_syntax_node(db, *self).attributes(db))
541            }
542            SyntaxKind::ItemImpl => Some(ast::ItemImpl::from_syntax_node(db, *self).attributes(db)),
543            SyntaxKind::ItemImplAlias => {
544                Some(ast::ItemImplAlias::from_syntax_node(db, *self).attributes(db))
545            }
546            SyntaxKind::ItemStruct => {
547                Some(ast::ItemStruct::from_syntax_node(db, *self).attributes(db))
548            }
549            SyntaxKind::ItemEnum => Some(ast::ItemEnum::from_syntax_node(db, *self).attributes(db)),
550            SyntaxKind::ItemTypeAlias => {
551                Some(ast::ItemTypeAlias::from_syntax_node(db, *self).attributes(db))
552            }
553            SyntaxKind::TraitItemFunction => {
554                Some(ast::TraitItemFunction::from_syntax_node(db, *self).attributes(db))
555            }
556            SyntaxKind::ItemInlineMacro => {
557                Some(ast::ItemInlineMacro::from_syntax_node(db, *self).attributes(db))
558            }
559            SyntaxKind::AttributeList => Some(ast::AttributeList::from_syntax_node(db, *self)),
560            SyntaxKind::Member => Some(ast::Member::from_syntax_node(db, *self).attributes(db)),
561            SyntaxKind::Variant => Some(ast::Variant::from_syntax_node(db, *self).attributes(db)),
562            SyntaxKind::StatementBreak => {
563                Some(ast::StatementBreak::from_syntax_node(db, *self).attributes(db))
564            }
565            SyntaxKind::StatementContinue => {
566                Some(ast::StatementContinue::from_syntax_node(db, *self).attributes(db))
567            }
568            SyntaxKind::StatementReturn => {
569                Some(ast::StatementReturn::from_syntax_node(db, *self).attributes(db))
570            }
571            SyntaxKind::StatementLet => {
572                Some(ast::StatementLet::from_syntax_node(db, *self).attributes(db))
573            }
574            SyntaxKind::StatementExpr => {
575                Some(ast::StatementExpr::from_syntax_node(db, *self).attributes(db))
576            }
577            _ => None,
578        }
579    }
580}
581
582impl QueryAttrs for Statement {
583    fn try_attributes(&self, db: &dyn SyntaxGroup) -> Option<AttributeList> {
584        match self {
585            Statement::Break(statement) => Some(statement.attributes(db)),
586            Statement::Continue(statement) => Some(statement.attributes(db)),
587            Statement::Return(statement) => Some(statement.attributes(db)),
588            Statement::Let(statement) => Some(statement.attributes(db)),
589            Statement::Expr(statement) => Some(statement.attributes(db)),
590            Statement::Item(statement) => statement.item(db).try_attributes(db),
591            Statement::Missing(_) => None,
592        }
593    }
594}
595pub trait WrappedArgListHelper {
596    /// Pills the wrapping brackets to get the argument list. Returns None if `self` is `Missing`.
597    fn arg_list(&self, db: &dyn SyntaxGroup) -> Option<ast::ArgList>;
598    /// Gets the syntax node of the right wrapping bracket.
599    fn right_bracket_syntax_node(&self, db: &dyn SyntaxGroup) -> SyntaxNode;
600    /// Gets the syntax node of the left wrapping bracket.
601    fn left_bracket_syntax_node(&self, db: &dyn SyntaxGroup) -> SyntaxNode;
602    /// Gets a stable pointer to the left wrapping bracket.
603    fn left_bracket_stable_ptr(&self, db: &dyn SyntaxGroup) -> SyntaxStablePtrId;
604}
605impl WrappedArgListHelper for WrappedArgList {
606    fn arg_list(&self, db: &dyn SyntaxGroup) -> Option<ast::ArgList> {
607        match self {
608            WrappedArgList::ParenthesizedArgList(args) => Some(args.arguments(db)),
609            WrappedArgList::BracketedArgList(args) => Some(args.arguments(db)),
610            WrappedArgList::BracedArgList(args) => Some(args.arguments(db)),
611            WrappedArgList::Missing(_) => None,
612        }
613    }
614
615    fn right_bracket_syntax_node(&self, db: &dyn SyntaxGroup) -> SyntaxNode {
616        match self {
617            WrappedArgList::ParenthesizedArgList(args) => args.rparen(db).as_syntax_node(),
618            WrappedArgList::BracketedArgList(args) => args.rbrack(db).as_syntax_node(),
619            WrappedArgList::BracedArgList(args) => args.rbrace(db).as_syntax_node(),
620            WrappedArgList::Missing(_) => self.as_syntax_node(),
621        }
622    }
623
624    fn left_bracket_syntax_node(&self, db: &dyn SyntaxGroup) -> SyntaxNode {
625        match self {
626            WrappedArgList::ParenthesizedArgList(args) => args.lparen(db).as_syntax_node(),
627            WrappedArgList::BracketedArgList(args) => args.lbrack(db).as_syntax_node(),
628            WrappedArgList::BracedArgList(args) => args.lbrace(db).as_syntax_node(),
629            WrappedArgList::Missing(_) => self.as_syntax_node(),
630        }
631    }
632
633    fn left_bracket_stable_ptr(&self, db: &dyn SyntaxGroup) -> SyntaxStablePtrId {
634        match self {
635            WrappedArgList::ParenthesizedArgList(args) => args.lparen(db).stable_ptr(db).untyped(),
636            WrappedArgList::BracketedArgList(args) => args.lbrack(db).stable_ptr(db).untyped(),
637            WrappedArgList::BracedArgList(args) => args.lbrace(db).stable_ptr(db).untyped(),
638            WrappedArgList::Missing(_) => self.stable_ptr(db).untyped(),
639        }
640    }
641}
642
643pub trait WrappedGenericParamListHelper {
644    /// Checks whether there are 0 generic parameters
645    fn is_empty(&self, db: &dyn SyntaxGroup) -> bool;
646}
647impl WrappedGenericParamListHelper for ast::WrappedGenericParamList {
648    fn is_empty(&self, db: &dyn SyntaxGroup) -> bool {
649        self.generic_params(db).elements(db).len() == 0
650    }
651}
652
653pub trait OptionWrappedGenericParamListHelper {
654    /// Checks whether there are 0 generic parameters. True either when the generic params clause
655    /// doesn't exist or when it's empty
656    fn is_empty(&self, db: &dyn SyntaxGroup) -> bool;
657}
658impl OptionWrappedGenericParamListHelper for ast::OptionWrappedGenericParamList {
659    fn is_empty(&self, db: &dyn SyntaxGroup) -> bool {
660        match self {
661            ast::OptionWrappedGenericParamList::Empty(_) => true,
662            ast::OptionWrappedGenericParamList::WrappedGenericParamList(
663                wrapped_generic_param_list,
664            ) => wrapped_generic_param_list.is_empty(db),
665        }
666    }
667}
668
669/// Trait for getting the items of a body-item (an item that contains items), as an iterator.
670pub trait BodyItems {
671    /// The type of an Item.
672    type Item;
673    /// Returns the items of the body-item as an iterator.
674    /// Use with caution, as this includes items that may be filtered out by plugins.
675    /// Do note that plugins that directly run on this body-item without going/checking up on the
676    /// syntax tree may assume that e.g. out-of-config items were already filtered out.
677    /// Don't use on an item that is not the original plugin's context, unless you are sure that
678    /// while traversing the AST to get to it from the original plugin's context, you did not go
679    /// through another submodule.
680    fn iter_items<'a>(&self, db: &'a dyn SyntaxGroup) -> impl Iterator<Item = Self::Item> + 'a;
681}
682
683impl BodyItems for ast::ModuleBody {
684    type Item = ModuleItem;
685    fn iter_items<'a>(&self, db: &'a dyn SyntaxGroup) -> impl Iterator<Item = Self::Item> + 'a {
686        self.items(db).elements(db)
687    }
688}
689
690impl BodyItems for ast::TraitBody {
691    type Item = TraitItem;
692    fn iter_items<'a>(&self, db: &'a dyn SyntaxGroup) -> impl Iterator<Item = Self::Item> + 'a {
693        self.items(db).elements(db)
694    }
695}
696
697impl BodyItems for ast::ImplBody {
698    type Item = ImplItem;
699    fn iter_items<'a>(&self, db: &'a dyn SyntaxGroup) -> impl Iterator<Item = Self::Item> + 'a {
700        self.items(db).elements(db)
701    }
702}
703
704/// Helper trait for ast::UsePath.
705pub trait UsePathEx {
706    /// Retrieves the item of a use path.
707    fn get_item(&self, db: &dyn SyntaxGroup) -> ast::ItemUse;
708}
709impl UsePathEx for ast::UsePath {
710    fn get_item(&self, db: &dyn SyntaxGroup) -> ast::ItemUse {
711        let mut node = self.as_syntax_node();
712        loop {
713            let Some(parent) = node.parent(db) else {
714                unreachable!("UsePath is not under an ItemUse.");
715            };
716            match parent.kind(db) {
717                SyntaxKind::ItemUse => {
718                    break ast::ItemUse::from_syntax_node(db, parent);
719                }
720                _ => node = parent,
721            }
722        }
723    }
724}
725
726impl UsePathLeaf {
727    /// Retrieves the stable pointer of the name of the leaf.
728    pub fn name_stable_ptr(&self, db: &dyn SyntaxGroup) -> SyntaxStablePtrId {
729        self.name(db).stable_ptr(db).untyped()
730    }
731}
732
733/// Helper trait for check syntactically if a type is dependent on a given identifier.
734pub trait IsDependentType {
735    /// Returns true if `self` is dependent on `identifier` in an internal type.
736    /// For example given identifier `T` will return true for:
737    /// `T`, `Array<T>`, `Array<Array<T>>`, `(T, felt252)`.
738    /// Does not resolve paths, type aliases or named generics.
739    fn is_dependent_type(&self, db: &dyn SyntaxGroup, identifiers: &[&str]) -> bool;
740}
741
742impl IsDependentType for ast::ExprPath {
743    fn is_dependent_type(&self, db: &dyn SyntaxGroup, identifiers: &[&str]) -> bool {
744        let segments = self.segments(db).elements_vec(db);
745        if let [ast::PathSegment::Simple(arg_segment)] = &segments[..] {
746            identifiers.contains(&arg_segment.identifier(db).as_str())
747        } else {
748            segments.into_iter().any(|segment| {
749                let ast::PathSegment::WithGenericArgs(with_generics) = segment else {
750                    return false;
751                };
752                with_generics.generic_args(db).generic_args(db).elements(db).any(|arg| {
753                    let generic_arg_value = match arg {
754                        ast::GenericArg::Named(named) => named.value(db),
755                        ast::GenericArg::Unnamed(unnamed) => unnamed.value(db),
756                    };
757                    match generic_arg_value {
758                        ast::GenericArgValue::Expr(arg_expr) => {
759                            arg_expr.expr(db).is_dependent_type(db, identifiers)
760                        }
761                        ast::GenericArgValue::Underscore(_) => false,
762                    }
763                })
764            })
765        }
766    }
767}
768
769impl IsDependentType for ast::Expr {
770    fn is_dependent_type(&self, db: &dyn SyntaxGroup, identifiers: &[&str]) -> bool {
771        match self {
772            ast::Expr::Path(type_path) => type_path.is_dependent_type(db, identifiers),
773            ast::Expr::Unary(unary) => unary.expr(db).is_dependent_type(db, identifiers),
774            ast::Expr::Binary(binary) => {
775                binary.lhs(db).is_dependent_type(db, identifiers)
776                    || binary.rhs(db).is_dependent_type(db, identifiers)
777            }
778            ast::Expr::Tuple(tuple) => tuple
779                .expressions(db)
780                .elements(db)
781                .any(|expr| expr.is_dependent_type(db, identifiers)),
782            ast::Expr::FixedSizeArray(arr) => {
783                arr.exprs(db).elements(db).any(|expr| expr.is_dependent_type(db, identifiers))
784                    || match arr.size(db) {
785                        ast::OptionFixedSizeArraySize::Empty(_) => false,
786                        ast::OptionFixedSizeArraySize::FixedSizeArraySize(size) => {
787                            size.size(db).is_dependent_type(db, identifiers)
788                        }
789                    }
790            }
791            ast::Expr::Literal(_)
792            | ast::Expr::ShortString(_)
793            | ast::Expr::String(_)
794            | ast::Expr::False(_)
795            | ast::Expr::True(_)
796            | ast::Expr::Parenthesized(_)
797            | ast::Expr::FunctionCall(_)
798            | ast::Expr::StructCtorCall(_)
799            | ast::Expr::Block(_)
800            | ast::Expr::Match(_)
801            | ast::Expr::If(_)
802            | ast::Expr::Loop(_)
803            | ast::Expr::While(_)
804            | ast::Expr::For(_)
805            | ast::Expr::Closure(_)
806            | ast::Expr::ErrorPropagate(_)
807            | ast::Expr::FieldInitShorthand(_)
808            | ast::Expr::Indexed(_)
809            | ast::Expr::InlineMacro(_)
810            | ast::Expr::Placeholder(_)
811            | ast::Expr::Missing(_) => false,
812        }
813    }
814}