cairo_lang_syntax/node/
helpers.rs

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