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