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