Skip to main content

cairo_lang_syntax/node/
helpers.rs

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