Skip to main content

cairo_lang_syntax/node/
helpers.rs

1use std::ops::ControlFlow;
2
3use cairo_lang_filesystem::ids::{FileLongId, SmolStrId, Tracked};
4use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
5use cairo_lang_utils::require;
6use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
7use itertools::{Itertools, chain};
8use salsa::Database;
9
10use super::ast::{
11    self, FunctionDeclaration, FunctionDeclarationGreen, FunctionWithBody, FunctionWithBodyPtr,
12    ImplItem, ItemConstant, ItemEnum, ItemExternFunction, ItemExternFunctionPtr, ItemExternType,
13    ItemImpl, ItemImplAlias, ItemInlineMacro, ItemMacroDeclaration, ItemModule, ItemStruct,
14    ItemTrait, ItemTypeAlias, ItemUse, Member, Modifier, ModuleItem, OptionArgListParenthesized,
15    Statement, StatementBreak, StatementContinue, StatementExpr, StatementLet, StatementReturn,
16    TerminalIdentifier, TerminalIdentifierGreen, TokenIdentifierGreen, TraitItem,
17    TraitItemConstant, TraitItemFunction, TraitItemFunctionPtr, TraitItemImpl, TraitItemType,
18    UsePathLeaf, Variant, WrappedArgList,
19};
20use super::ids::SyntaxStablePtrId;
21use super::kind::SyntaxKind;
22use super::{SyntaxNode, Terminal, TypedStablePtr, TypedSyntaxNode};
23use crate::node::ast::{Attribute, AttributeList};
24use crate::node::green::GreenNodeDetails;
25
26#[cfg(test)]
27#[path = "helpers_test.rs"]
28mod test;
29
30pub trait GetIdentifier<'a> {
31    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a>;
32}
33impl<'a> ast::UsePathLeafPtr<'a> {
34    pub fn name_green(&self, _syntax_db: &dyn Database) -> Self {
35        *self
36    }
37}
38impl<'a> GetIdentifier<'a> for ast::UsePathLeafPtr<'a> {
39    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
40        let alias_clause_green = self.alias_clause_green(db).0;
41        let green_node = alias_clause_green.long(db);
42        let children = match &green_node.details {
43            GreenNodeDetails::Node { children, width: _ } => children,
44            _ => panic!("Unexpected token"),
45        };
46        if !children.is_empty() {
47            return ast::TerminalIdentifierGreen(children[ast::AliasClause::INDEX_ALIAS])
48                .identifier(db);
49        }
50        let ident_green = self.ident_green(db);
51        let ident = ident_green.identifier(db);
52        if ident.long(db) != "self" {
53            return ident;
54        }
55        let mut node = self.0.lookup(db);
56        loop {
57            node = if let Some(parent) = node.parent(db) {
58                parent
59            } else {
60                return ident;
61            };
62            if matches!(node.kind(db), SyntaxKind::UsePathSingle) {
63                return ast::UsePathSingle::from_syntax_node(db, node).ident(db).identifier(db);
64            }
65        }
66    }
67}
68impl<'a> GetIdentifier<'a> for ast::PathSegmentGreen<'a> {
69    /// Retrieves the text of the last identifier in the path.
70    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
71        let green_node = self.0.long(db);
72        let children = match &green_node.details {
73            GreenNodeDetails::Node { children, width: _ } => children,
74            _ => panic!("Unexpected token"),
75        };
76        let identifier = ast::TerminalIdentifierGreen(children[0]);
77        identifier.identifier(db)
78    }
79}
80impl<'a> GetIdentifier<'a> for ast::ExprPathGreen<'a> {
81    /// Retrieves the text of the last identifier in the path.
82    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
83        let green_node = self.0.long(db);
84        let children = match &green_node.details {
85            GreenNodeDetails::Node { children, width: _ } => children,
86            _ => panic!("Unexpected token"),
87        };
88        let segment_green = ast::ExprPathInnerGreen(*children.last().unwrap());
89        segment_green.identifier(db)
90    }
91}
92
93impl<'a> GetIdentifier<'a> for ast::ExprPathInnerGreen<'a> {
94    /// Retrieves the text of the last identifier in the path.
95    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
96        let green_node = self.0.long(db);
97        let children = match &green_node.details {
98            GreenNodeDetails::Node { children, width: _ } => children,
99            _ => panic!("Unexpected token"),
100        };
101        assert_eq!(children.len() & 1, 1, "Expected an odd number of elements in the path.");
102        let segment_green = ast::PathSegmentGreen(*children.last().unwrap());
103        segment_green.identifier(db)
104    }
105}
106impl<'a> GetIdentifier<'a> for ast::TerminalIdentifierGreen<'a> {
107    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
108        match &self.0.long(db).details {
109            GreenNodeDetails::Token(_) => panic!("Unexpected token"),
110            GreenNodeDetails::Node { children, width: _ } => {
111                TokenIdentifierGreen(children[1]).text(db)
112            }
113        }
114    }
115}
116impl<'a> GetIdentifier<'a> for ast::ExprPath<'a> {
117    /// Retrieves the identifier of the last segment of the path.
118    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
119        self.segments(db).elements(db).next_back().unwrap().identifier(db)
120    }
121}
122
123/// Helper trait for ast::PathSegment.
124pub trait PathSegmentEx<'a> {
125    fn identifier_ast(&self, db: &'a dyn Database) -> ast::TerminalIdentifier<'a>;
126    fn generic_args(&self, db: &'a dyn Database) -> Option<Vec<ast::GenericArg<'a>>>;
127}
128impl<'a> PathSegmentEx<'a> for ast::PathSegment<'a> {
129    /// Retrieves the identifier ast of a path segment.
130    fn identifier_ast(&self, db: &'a dyn Database) -> ast::TerminalIdentifier<'a> {
131        match self {
132            ast::PathSegment::Simple(segment) => segment.ident(db),
133            ast::PathSegment::WithGenericArgs(segment) => segment.ident(db),
134        }
135    }
136    fn generic_args(&self, db: &'a dyn Database) -> Option<Vec<ast::GenericArg<'a>>> {
137        match self {
138            ast::PathSegment::Simple(_) => None,
139            ast::PathSegment::WithGenericArgs(segment) => {
140                Some(segment.generic_args(db).generic_args(db).elements_vec(db))
141            }
142        }
143    }
144}
145impl<'a> GetIdentifier<'a> for ast::PathSegment<'a> {
146    /// Retrieves the text of the segment (without the generic args).
147    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
148        match self {
149            ast::PathSegment::Simple(segment) => segment.identifier(db),
150            ast::PathSegment::WithGenericArgs(segment) => segment.identifier(db),
151        }
152    }
153}
154impl<'a> GetIdentifier<'a> for ast::PathSegmentSimple<'a> {
155    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
156        let green_node = self.as_syntax_node().green_node(db);
157        let GreenNodeDetails::Node { children, .. } = &green_node.details else {
158            panic!("Unexpected token");
159        };
160        TerminalIdentifierGreen(children[0]).identifier(db)
161    }
162}
163impl<'a> GetIdentifier<'a> for ast::PathSegmentWithGenericArgs<'a> {
164    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
165        let green_node = self.as_syntax_node().green_node(db);
166        let GreenNodeDetails::Node { children, .. } = &green_node.details else {
167            panic!("Unexpected token");
168        };
169        TerminalIdentifierGreen(children[0]).identifier(db)
170    }
171}
172
173impl<'a> GetIdentifier<'a> for ast::Modifier<'a> {
174    fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
175        match self {
176            Modifier::Ref(r) => r.text(db),
177            Modifier::Mut(m) => m.text(db),
178        }
179    }
180}
181
182/// Trait for ast object with a name terminal.
183pub trait NameGreen<'a> {
184    /// Returns the TerminalIdentifierGreen of the `name` node.
185    fn name_green(self, db: &'a dyn Database) -> TerminalIdentifierGreen<'a>;
186}
187
188impl<'a> NameGreen<'a> for FunctionDeclarationGreen<'a> {
189    fn name_green(self, db: &'a dyn Database) -> TerminalIdentifierGreen<'a> {
190        TerminalIdentifierGreen(self.0.long(db).children()[FunctionDeclaration::INDEX_NAME])
191    }
192}
193
194impl<'a> NameGreen<'a> for FunctionWithBodyPtr<'a> {
195    fn name_green(self, db: &'a dyn Database) -> TerminalIdentifierGreen<'a> {
196        self.declaration_green(db).name_green(db)
197    }
198}
199
200impl<'a> NameGreen<'a> for ItemExternFunctionPtr<'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 TraitItemFunctionPtr<'a> {
207    fn name_green(self, db: &'a dyn Database) -> TerminalIdentifierGreen<'a> {
208        self.declaration_green(db).name_green(db)
209    }
210}
211
212/// Provides methods to extract a _name_ of AST objects.
213pub trait HasName<'a> {
214    /// Gets a [`TerminalIdentifier`] that represents a _name_ of this AST object.
215    fn name(&self, db: &'a dyn Database) -> ast::TerminalIdentifier<'a>;
216}
217
218impl<'a> HasName<'a> for FunctionWithBody<'a> {
219    fn name(&self, db: &'a dyn Database) -> TerminalIdentifier<'a> {
220        self.declaration(db).name(db)
221    }
222}
223
224impl<'a> HasName<'a> for ItemExternFunction<'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 TraitItemFunction<'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 UsePathLeaf<'a> {
237    fn name(&self, db: &'a dyn Database) -> TerminalIdentifier<'a> {
238        match self.alias_clause(db) {
239            ast::OptionAliasClause::Empty(_) => self.ident(db).identifier_ast(db),
240            ast::OptionAliasClause::AliasClause(alias) => alias.alias(db),
241        }
242    }
243}
244
245pub trait GenericParamEx<'a> {
246    /// Returns the name of a generic param if one exists.
247    fn name(&self, db: &'a dyn Database) -> Option<ast::TerminalIdentifier<'a>>;
248}
249impl<'a> GenericParamEx<'a> for ast::GenericParam<'a> {
250    fn name(&self, db: &'a dyn Database) -> Option<ast::TerminalIdentifier<'a>> {
251        match self {
252            ast::GenericParam::Type(t) => Some(t.name(db)),
253            ast::GenericParam::Const(c) => Some(c.name(db)),
254            ast::GenericParam::ImplNamed(i) => Some(i.name(db)),
255            ast::GenericParam::ImplAnonymous(_) => None,
256            ast::GenericParam::NegativeImpl(_) => None,
257        }
258    }
259}
260
261/// Checks if the given attribute has a single argument with the given name.
262pub fn is_single_arg_attr(db: &dyn Database, attr: &Attribute<'_>, arg_name: &str) -> bool {
263    match attr.arguments(db) {
264        OptionArgListParenthesized::ArgListParenthesized(args) => {
265            matches!(args.arguments(db).elements(db).exactly_one(),
266                    Ok(arg) if arg.as_syntax_node().get_text_without_trivia(db).long(db) == arg_name)
267        }
268        OptionArgListParenthesized::Empty(_) => false,
269    }
270}
271
272/// Trait for querying attributes of AST items.
273pub trait QueryAttrs<'a> {
274    /// Generic call `self.attributes(db).elements(db)`, wrapped with `Option` for cases where the
275    /// type does not support attributes.
276    ///
277    /// Implementation detail, should not be used by this trait's users.
278    #[doc(hidden)]
279    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>>;
280
281    /// Generic call to `self.attributes(db).elements(db)`.
282    fn attributes_elements(&self, db: &'a dyn Database) -> impl Iterator<Item = Attribute<'a>> {
283        self.try_attributes(db).into_iter().flat_map(move |attrs| attrs.elements(db))
284    }
285
286    /// Collect all attributes named exactly `attr` attached to this node.
287    fn query_attr(
288        &self,
289        db: &'a dyn Database,
290        attr: &'a str,
291    ) -> impl Iterator<Item = Attribute<'a>> {
292        self.attributes_elements(db).filter(move |a| {
293            a.attr(db).as_syntax_node().get_text_without_trivia(db).long(db) == attr
294        })
295    }
296
297    /// Find first attribute named exactly `attr` attached to this node.
298    fn find_attr(&self, db: &'a dyn Database, attr: &'a str) -> Option<Attribute<'a>> {
299        self.query_attr(db, attr).next()
300    }
301
302    /// Checks if this node has an attribute named exactly `attr`.
303    fn has_attr(&self, db: &'a dyn Database, attr: &'a str) -> bool {
304        self.find_attr(db, attr).is_some()
305    }
306
307    /// Checks if the given object has an attribute with the given name and argument.
308    fn has_attr_with_arg(&self, db: &'a dyn Database, attr_name: &'a str, arg_name: &str) -> bool {
309        self.query_attr(db, attr_name).any(|attr| is_single_arg_attr(db, &attr, arg_name))
310    }
311}
312
313impl<'a> QueryAttrs<'a> for ItemConstant<'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 ItemModule<'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 FunctionWithBody<'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 ItemUse<'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 ItemExternFunction<'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 ItemExternType<'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 ItemTrait<'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 ItemImpl<'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 ItemImplAlias<'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 ItemStruct<'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 ItemEnum<'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 ItemTypeAlias<'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 ItemMacroDeclaration<'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 TraitItemFunction<'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 TraitItemType<'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 TraitItemConstant<'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 TraitItemImpl<'a> {
394    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
395        Some(self.attributes(db))
396    }
397}
398impl<'a> QueryAttrs<'a> for TraitItem<'a> {
399    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
400        match self {
401            TraitItem::Function(item) => Some(item.attributes(db)),
402            TraitItem::Type(item) => Some(item.attributes(db)),
403            TraitItem::Constant(item) => Some(item.attributes(db)),
404            TraitItem::Impl(item) => Some(item.attributes(db)),
405            TraitItem::Missing(_) => None,
406        }
407    }
408}
409
410impl<'a> QueryAttrs<'a> for ItemInlineMacro<'a> {
411    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
412        Some(self.attributes(db))
413    }
414}
415
416impl<'a> QueryAttrs<'a> for ModuleItem<'a> {
417    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
418        match self {
419            ModuleItem::Constant(item) => Some(item.attributes(db)),
420            ModuleItem::Module(item) => Some(item.attributes(db)),
421            ModuleItem::FreeFunction(item) => Some(item.attributes(db)),
422            ModuleItem::Use(item) => Some(item.attributes(db)),
423            ModuleItem::ExternFunction(item) => Some(item.attributes(db)),
424            ModuleItem::ExternType(item) => Some(item.attributes(db)),
425            ModuleItem::Trait(item) => Some(item.attributes(db)),
426            ModuleItem::Impl(item) => Some(item.attributes(db)),
427            ModuleItem::ImplAlias(item) => Some(item.attributes(db)),
428            ModuleItem::Struct(item) => Some(item.attributes(db)),
429            ModuleItem::Enum(item) => Some(item.attributes(db)),
430            ModuleItem::TypeAlias(item) => Some(item.attributes(db)),
431            ModuleItem::InlineMacro(item) => Some(item.attributes(db)),
432            ModuleItem::MacroDeclaration(macro_declaration) => {
433                Some(macro_declaration.attributes(db))
434            }
435            ModuleItem::Missing(_) => None,
436            ModuleItem::HeaderDoc(_) => None,
437        }
438    }
439}
440
441impl<'a> QueryAttrs<'a> for ImplItem<'a> {
442    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
443        match self {
444            ImplItem::Function(item) => Some(item.attributes(db)),
445            ImplItem::Type(item) => Some(item.attributes(db)),
446            ImplItem::Constant(item) => Some(item.attributes(db)),
447            ImplItem::Impl(item) => Some(item.attributes(db)),
448            ImplItem::Module(item) => Some(item.attributes(db)),
449            ImplItem::Use(item) => Some(item.attributes(db)),
450            ImplItem::ExternFunction(item) => Some(item.attributes(db)),
451            ImplItem::ExternType(item) => Some(item.attributes(db)),
452            ImplItem::Trait(item) => Some(item.attributes(db)),
453            ImplItem::Struct(item) => Some(item.attributes(db)),
454            ImplItem::Enum(item) => Some(item.attributes(db)),
455            ImplItem::Missing(_) => None,
456        }
457    }
458}
459
460impl<'a> QueryAttrs<'a> for AttributeList<'a> {
461    fn try_attributes(&self, _db: &'a dyn Database) -> Option<AttributeList<'a>> {
462        Some(self.clone())
463    }
464}
465impl<'a> QueryAttrs<'a> for Member<'a> {
466    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
467        Some(self.attributes(db))
468    }
469}
470
471impl<'a> QueryAttrs<'a> for Variant<'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 StatementBreak<'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 StatementContinue<'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 StatementReturn<'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 StatementLet<'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 StatementExpr<'a> {
502    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
503        Some(self.attributes(db))
504    }
505}
506
507/// Allows querying attributes of a syntax node, any typed node which QueryAttrs is implemented for
508/// should be added here.
509impl<'a> QueryAttrs<'a> for SyntaxNode<'a> {
510    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
511        match self.kind(db) {
512            SyntaxKind::ItemConstant => {
513                Some(ast::ItemConstant::from_syntax_node(db, *self).attributes(db))
514            }
515            SyntaxKind::ItemModule => {
516                Some(ast::ItemModule::from_syntax_node(db, *self).attributes(db))
517            }
518            SyntaxKind::FunctionWithBody => {
519                Some(ast::FunctionWithBody::from_syntax_node(db, *self).attributes(db))
520            }
521            SyntaxKind::ItemUse => Some(ast::ItemUse::from_syntax_node(db, *self).attributes(db)),
522            SyntaxKind::ItemExternFunction => {
523                Some(ast::ItemExternFunction::from_syntax_node(db, *self).attributes(db))
524            }
525            SyntaxKind::ItemExternType => {
526                Some(ast::ItemExternType::from_syntax_node(db, *self).attributes(db))
527            }
528            SyntaxKind::ItemTrait => {
529                Some(ast::ItemTrait::from_syntax_node(db, *self).attributes(db))
530            }
531            SyntaxKind::ItemImpl => Some(ast::ItemImpl::from_syntax_node(db, *self).attributes(db)),
532            SyntaxKind::ItemImplAlias => {
533                Some(ast::ItemImplAlias::from_syntax_node(db, *self).attributes(db))
534            }
535            SyntaxKind::ItemStruct => {
536                Some(ast::ItemStruct::from_syntax_node(db, *self).attributes(db))
537            }
538            SyntaxKind::ItemEnum => Some(ast::ItemEnum::from_syntax_node(db, *self).attributes(db)),
539            SyntaxKind::ItemTypeAlias => {
540                Some(ast::ItemTypeAlias::from_syntax_node(db, *self).attributes(db))
541            }
542            SyntaxKind::TraitItemFunction => {
543                Some(ast::TraitItemFunction::from_syntax_node(db, *self).attributes(db))
544            }
545            SyntaxKind::TraitItemType => {
546                Some(ast::TraitItemType::from_syntax_node(db, *self).attributes(db))
547            }
548            SyntaxKind::TraitItemConstant => {
549                Some(ast::TraitItemConstant::from_syntax_node(db, *self).attributes(db))
550            }
551            SyntaxKind::TraitItemImpl => {
552                Some(ast::TraitItemImpl::from_syntax_node(db, *self).attributes(db))
553            }
554            SyntaxKind::ItemMacroDeclaration => {
555                Some(ast::ItemMacroDeclaration::from_syntax_node(db, *self).attributes(db))
556            }
557            SyntaxKind::ItemInlineMacro => {
558                Some(ast::ItemInlineMacro::from_syntax_node(db, *self).attributes(db))
559            }
560            SyntaxKind::AttributeList => Some(ast::AttributeList::from_syntax_node(db, *self)),
561            SyntaxKind::Member => Some(ast::Member::from_syntax_node(db, *self).attributes(db)),
562            SyntaxKind::Variant => Some(ast::Variant::from_syntax_node(db, *self).attributes(db)),
563            SyntaxKind::StatementBreak => {
564                Some(ast::StatementBreak::from_syntax_node(db, *self).attributes(db))
565            }
566            SyntaxKind::StatementContinue => {
567                Some(ast::StatementContinue::from_syntax_node(db, *self).attributes(db))
568            }
569            SyntaxKind::StatementReturn => {
570                Some(ast::StatementReturn::from_syntax_node(db, *self).attributes(db))
571            }
572            SyntaxKind::StatementLet => {
573                Some(ast::StatementLet::from_syntax_node(db, *self).attributes(db))
574            }
575            SyntaxKind::StatementExpr => {
576                Some(ast::StatementExpr::from_syntax_node(db, *self).attributes(db))
577            }
578            _ => None,
579        }
580    }
581}
582
583impl<'a> QueryAttrs<'a> for Statement<'a> {
584    fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>> {
585        match self {
586            Statement::Break(statement) => Some(statement.attributes(db)),
587            Statement::Continue(statement) => Some(statement.attributes(db)),
588            Statement::Return(statement) => Some(statement.attributes(db)),
589            Statement::Let(statement) => Some(statement.attributes(db)),
590            Statement::Expr(statement) => Some(statement.attributes(db)),
591            Statement::Item(statement) => statement.item(db).try_attributes(db),
592            Statement::Missing(_) => None,
593        }
594    }
595}
596pub trait WrappedArgListHelper<'a> {
597    /// Pulls the wrapping brackets to get the argument list. Returns None if `self` is `Missing`.
598    fn arg_list(&self, db: &'a dyn Database) -> Option<ast::ArgList<'a>>;
599    /// Gets the syntax node of the right wrapping bracket.
600    fn right_bracket_syntax_node(&self, db: &'a dyn Database) -> SyntaxNode<'a>;
601    /// Gets the syntax node of the left wrapping bracket.
602    fn left_bracket_syntax_node(&self, db: &'a dyn Database) -> SyntaxNode<'a>;
603    /// Gets a stable pointer to the left wrapping bracket.
604    fn left_bracket_stable_ptr(&self, db: &'a dyn Database) -> SyntaxStablePtrId<'a>;
605}
606impl<'a> WrappedArgListHelper<'a> for WrappedArgList<'a> {
607    fn arg_list(&self, db: &'a dyn Database) -> Option<ast::ArgList<'a>> {
608        match self {
609            WrappedArgList::ParenthesizedArgList(args) => Some(args.arguments(db)),
610            WrappedArgList::BracketedArgList(args) => Some(args.arguments(db)),
611            WrappedArgList::BracedArgList(args) => Some(args.arguments(db)),
612            WrappedArgList::Missing(_) => None,
613        }
614    }
615
616    fn right_bracket_syntax_node(&self, db: &'a dyn Database) -> SyntaxNode<'a> {
617        match self {
618            WrappedArgList::ParenthesizedArgList(args) => args.rparen(db).as_syntax_node(),
619            WrappedArgList::BracketedArgList(args) => args.rbrack(db).as_syntax_node(),
620            WrappedArgList::BracedArgList(args) => args.rbrace(db).as_syntax_node(),
621            WrappedArgList::Missing(_) => self.as_syntax_node(),
622        }
623    }
624
625    fn left_bracket_syntax_node(&self, db: &'a dyn Database) -> SyntaxNode<'a> {
626        match self {
627            WrappedArgList::ParenthesizedArgList(args) => args.lparen(db).as_syntax_node(),
628            WrappedArgList::BracketedArgList(args) => args.lbrack(db).as_syntax_node(),
629            WrappedArgList::BracedArgList(args) => args.lbrace(db).as_syntax_node(),
630            WrappedArgList::Missing(_) => self.as_syntax_node(),
631        }
632    }
633
634    fn left_bracket_stable_ptr(&self, db: &'a dyn Database) -> SyntaxStablePtrId<'a> {
635        match self {
636            WrappedArgList::ParenthesizedArgList(args) => args.lparen(db).stable_ptr(db).untyped(),
637            WrappedArgList::BracketedArgList(args) => args.lbrack(db).stable_ptr(db).untyped(),
638            WrappedArgList::BracedArgList(args) => args.lbrace(db).stable_ptr(db).untyped(),
639            WrappedArgList::Missing(_) => self.stable_ptr(db).untyped(),
640        }
641    }
642}
643
644pub trait WrappedGenericParamListHelper<'a> {
645    /// Checks whether there are 0 generic parameters.
646    fn is_empty(&'a self, db: &'a dyn Database) -> bool;
647}
648impl<'a> WrappedGenericParamListHelper<'a> for ast::WrappedGenericParamList<'a> {
649    fn is_empty(&'a self, db: &'a dyn Database) -> bool {
650        self.generic_params(db).elements(db).len() == 0
651    }
652}
653
654pub trait OptionWrappedGenericParamListHelper<'a> {
655    /// Checks whether there are 0 generic parameters. True either when the generic params clause
656    /// doesn't exist or when it's empty.
657    fn is_empty(&'a self, db: &'a dyn Database) -> bool;
658}
659impl<'a> OptionWrappedGenericParamListHelper<'a> for ast::OptionWrappedGenericParamList<'a> {
660    fn is_empty(&'a self, db: &'a dyn Database) -> bool {
661        match self {
662            ast::OptionWrappedGenericParamList::Empty(_) => true,
663            ast::OptionWrappedGenericParamList::WrappedGenericParamList(
664                wrapped_generic_param_list,
665            ) => wrapped_generic_param_list.is_empty(db),
666        }
667    }
668}
669
670/// Trait for getting the items of a body-item (an item that contains items), as an iterator.
671pub trait BodyItems<'a> {
672    /// The type of an Item.
673    type Item: 'a;
674    /// Returns the items of the body-item as an iterator.
675    /// Use with caution, as this includes items that may be filtered out by plugins.
676    /// Do note that plugins that directly run on this body-item without going/checking up on the
677    /// syntax tree may assume that e.g. out-of-config items were already filtered out.
678    /// Don't use on an item that is not the original plugin's context, unless you are sure that
679    /// while traversing the AST to get to it from the original plugin's context, you did not go
680    /// through another submodule.
681    fn iter_items(&self, db: &'a dyn Database) -> impl Iterator<Item = Self::Item> + 'a;
682}
683
684impl<'a> BodyItems<'a> for ast::ModuleBody<'a> {
685    type Item = ModuleItem<'a>;
686    fn iter_items(&self, db: &'a dyn Database) -> impl Iterator<Item = Self::Item> + 'a {
687        self.items(db).elements(db)
688    }
689}
690
691impl<'a> BodyItems<'a> for ast::TraitBody<'a> {
692    type Item = TraitItem<'a>;
693    fn iter_items(&self, db: &'a dyn Database) -> impl Iterator<Item = Self::Item> + 'a {
694        self.items(db).elements(db)
695    }
696}
697
698impl<'a> BodyItems<'a> for ast::ImplBody<'a> {
699    type Item = ImplItem<'a>;
700    fn iter_items(&self, db: &'a dyn Database) -> impl Iterator<Item = Self::Item> + 'a {
701        self.items(db).elements(db)
702    }
703}
704
705/// Helper trait for ast::UsePath.
706pub trait UsePathEx<'a> {
707    /// Retrieves the item of a use path.
708    fn get_item(&self, db: &'a dyn Database) -> ast::ItemUse<'a>;
709}
710impl<'a> UsePathEx<'a> for ast::UsePath<'a> {
711    fn get_item(&self, db: &'a dyn Database) -> ast::ItemUse<'a> {
712        let mut node = self.as_syntax_node();
713        loop {
714            let Some(parent) = node.parent(db) else {
715                unreachable!("UsePath is not under an ItemUse.");
716            };
717            match parent.kind(db) {
718                SyntaxKind::ItemUse => {
719                    break ast::ItemUse::from_syntax_node(db, parent);
720                }
721                _ => node = parent,
722            }
723        }
724    }
725}
726
727impl<'a> UsePathLeaf<'a> {
728    /// Retrieves the stable pointer of the name of the leaf.
729    pub fn name_stable_ptr(&self, db: &'a dyn Database) -> SyntaxStablePtrId<'a> {
730        self.name(db).stable_ptr(db).untyped()
731    }
732}
733
734/// Helper trait for check syntactically if a type is dependent on a given identifier.
735pub trait IsDependentType<'db> {
736    /// Returns true if `self` is dependent on `identifier` in an internal type.
737    /// For example given identifier `T` will return true for:
738    /// `T`, `Array<T>`, `Array<Array<T>>`, `(T, felt252)`, `T::AssocType`.
739    /// Does not resolve type aliases or named generics. A multi-segment path is considered
740    /// dependent when its head segment is one of the identifiers (e.g. the associated type
741    /// `T::AssocType`), which is a purely syntactic check (no path resolution).
742    fn is_dependent_type(&self, db: &'db dyn Database, identifiers: &[SmolStrId<'db>]) -> bool;
743}
744
745impl<'a> IsDependentType<'a> for ast::ExprPath<'a> {
746    fn is_dependent_type(&self, db: &'a dyn Database, identifiers: &[SmolStrId<'a>]) -> bool {
747        let mut segments = self.segments(db).elements(db);
748        match segments.next() {
749            None => false,
750            Some(ast::PathSegment::Simple(simple)) if segments.len() == 0 => {
751                identifiers.contains(&simple.identifier(db))
752            }
753            Some(first) => {
754                // A path whose head segment is one of the identifiers (e.g. the associated type
755                // `T::AssocType`) depends on that identifier. Only the head segment can be a
756                // generic param; a later segment with the same name is an associated-item name.
757                identifiers.contains(&first.identifier(db))
758                    || chain!([first], segments).any(|segment| {
759                        let ast::PathSegment::WithGenericArgs(with_generics) = segment else {
760                            return false;
761                        };
762                        with_generics.generic_args(db).generic_args(db).elements(db).any(|arg| {
763                            match arg {
764                                ast::GenericArg::Named(named) => named.value(db),
765                                ast::GenericArg::Unnamed(unnamed) => unnamed.value(db),
766                            }
767                            .is_dependent_type(db, identifiers)
768                        })
769                    })
770            }
771        }
772    }
773}
774
775impl<'a> IsDependentType<'a> for ast::Expr<'a> {
776    fn is_dependent_type(&self, db: &dyn Database, identifiers: &[SmolStrId<'a>]) -> bool {
777        match self {
778            ast::Expr::Path(type_path) => type_path.is_dependent_type(db, identifiers),
779            ast::Expr::Unary(unary) => unary.expr(db).is_dependent_type(db, identifiers),
780            ast::Expr::Binary(binary) => {
781                binary.lhs(db).is_dependent_type(db, identifiers)
782                    || binary.rhs(db).is_dependent_type(db, identifiers)
783            }
784            ast::Expr::Tuple(tuple) => tuple
785                .expressions(db)
786                .elements(db)
787                .any(|expr| expr.is_dependent_type(db, identifiers)),
788            ast::Expr::FixedSizeArray(arr) => {
789                arr.exprs(db).elements(db).any(|expr| expr.is_dependent_type(db, identifiers))
790                    || match arr.size(db) {
791                        ast::OptionFixedSizeArraySize::Empty(_) => false,
792                        ast::OptionFixedSizeArraySize::FixedSizeArraySize(size) => {
793                            size.size(db).is_dependent_type(db, identifiers)
794                        }
795                    }
796            }
797            ast::Expr::Literal(_)
798            | ast::Expr::ShortString(_)
799            | ast::Expr::String(_)
800            | ast::Expr::False(_)
801            | ast::Expr::True(_)
802            | ast::Expr::Parenthesized(_)
803            | ast::Expr::FunctionCall(_)
804            | ast::Expr::StructCtorCall(_)
805            | ast::Expr::Block(_)
806            | ast::Expr::Match(_)
807            | ast::Expr::If(_)
808            | ast::Expr::Loop(_)
809            | ast::Expr::While(_)
810            | ast::Expr::For(_)
811            | ast::Expr::Closure(_)
812            | ast::Expr::ErrorPropagate(_)
813            | ast::Expr::FieldInitShorthand(_)
814            | ast::Expr::Indexed(_)
815            | ast::Expr::InlineMacro(_)
816            | ast::Expr::Missing(_)
817            | ast::Expr::Underscore(_) => false,
818        }
819    }
820}
821
822/// A kind of function the compiler generates for an expression in a function's body.
823#[derive(Clone, Copy, PartialEq, Eq, Hash)]
824enum GeneratedFunctionKind {
825    Closure,
826    Loop,
827}
828impl GeneratedFunctionKind {
829    /// Returns the kind of function generated for an expression of the given syntax kind, if any.
830    fn of(kind: SyntaxKind) -> Option<Self> {
831        match kind {
832            SyntaxKind::ExprClosure => Some(Self::Closure),
833            SyntaxKind::ExprLoop | SyntaxKind::ExprWhile | SyntaxKind::ExprFor => Some(Self::Loop),
834            _ => None,
835        }
836    }
837
838    /// Returns the name of the kind, as it appears in the path of a generated function.
839    fn name(self) -> &'static str {
840        match self {
841            Self::Closure => "closure",
842            Self::Loop => "loop",
843        }
844    }
845}
846
847/// The syntax kinds of the items owning the generated functions defined in their body.
848const GENERATED_FUNCTION_OWNER_KINDS: [SyntaxKind; 2] =
849    [SyntaxKind::FunctionWithBody, SyntaxKind::TraitItemFunction];
850
851/// Returns whether an expression of the given syntax kind opens a scope for the generated
852/// functions defined in it - either by being a generated function itself, or by being a function.
853fn opens_generated_function_scope(kind: SyntaxKind) -> bool {
854    GeneratedFunctionKind::of(kind).is_some() || GENERATED_FUNCTION_OWNER_KINDS.contains(&kind)
855}
856
857/// Returns the body of `node` if it is a `for` - the only generated function that is not lowered in
858/// its entirety, as its iterable is lowered into the function containing it.
859fn for_loop_body<'db>(db: &'db dyn Database, node: SyntaxNode<'db>) -> Option<SyntaxNode<'db>> {
860    require(node.kind(db) == SyntaxKind::ExprFor)?;
861    Some(ast::ExprFor::from_syntax_node(db, node).body(db).as_syntax_node())
862}
863
864/// Returns the scope the generated function of `generated` is defined in - the closest ancestor
865/// that is a function, or a generated function whose body contains it.
866fn generated_function_scope<'db>(
867    db: &'db dyn Database,
868    generated: SyntaxNode<'db>,
869) -> Option<SyntaxNode<'db>> {
870    let mut child = generated;
871    for ancestor in generated.ancestors(db) {
872        if opens_generated_function_scope(ancestor.kind(db))
873            && for_loop_body(db, ancestor).is_none_or(|body| body == child)
874        {
875            return Some(ancestor);
876        }
877        child = ancestor;
878    }
879    // A generated function with no enclosing function is an error, but is still named - index it
880    // within the file, so that its name stays unique.
881    generated.ancestors(db).last()
882}
883
884/// A segment of the path of a generated function - only displayable, as its shape is not meaningful
885/// to its callers.
886pub struct GeneratedFunctionPathSegment<'db>(GeneratedFunctionPathSegmentKind<'db>);
887impl std::fmt::Display for GeneratedFunctionPathSegment<'_> {
888    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
889        match self.0 {
890            GeneratedFunctionPathSegmentKind::Generated { kind, index } => {
891                write!(f, "{{{}#{index}}}", kind.name())
892            }
893            GeneratedFunctionPathSegmentKind::MacroExpansion { name, offset } => {
894                write!(f, "{{{name}@{offset}}}")
895            }
896        }
897    }
898}
899
900/// The kinds of [GeneratedFunctionPathSegment].
901enum GeneratedFunctionPathSegmentKind<'db> {
902    /// A generated function, as `{<kind>#<index>}`.
903    Generated { kind: GeneratedFunctionKind, index: usize },
904    /// The expansion of a macro, as `{<macro>@<offset of the call>}`.
905    MacroExpansion { name: &'db str, offset: u32 },
906}
907
908/// Returns a segment for each macro expansion `node` is in, from the outermost inwards.
909///
910/// A generated function in the expansion of a macro has no path relative to the function it is
911/// lowered into, as that function is in another file. The expansions leading to it name it instead,
912/// each by the macro and the offset of its call, as the module of a macro call is named.
913fn macro_expansion_segments<'db>(
914    db: &'db dyn Database,
915    node: SyntaxNode<'db>,
916) -> Vec<GeneratedFunctionPathSegment<'db>> {
917    let mut segments = vec![];
918    let mut file_id = node.stable_ptr(db).file_id(db);
919    while let FileLongId::Virtual(virtual_file) = file_id.long(db) {
920        let Some(parent) = virtual_file.parent else {
921            break;
922        };
923        segments.push(GeneratedFunctionPathSegment(
924            GeneratedFunctionPathSegmentKind::MacroExpansion {
925                name: virtual_file.name.long(db),
926                offset: parent.span.start.as_u32(),
927            },
928        ));
929        file_id = parent.file_id;
930    }
931    segments.reverse();
932    segments
933}
934
935/// Calls `handle_segment` for each segment of the path of `node`'s generated function, from the
936/// outermost segment inwards. See [generated_function_path].
937pub fn for_each_generated_function_path_segment<'db, Break>(
938    db: &'db dyn Database,
939    node: SyntaxNode<'db>,
940    handle_segment: &mut impl FnMut(GeneratedFunctionPathSegment<'db>) -> ControlFlow<Break>,
941) -> ControlFlow<Break> {
942    let Some((generated, kind)) = node
943        .ancestors_with_self(db)
944        .find_map(|node| Some((node, GeneratedFunctionKind::of(node.kind(db))?)))
945    else {
946        return ControlFlow::Continue(());
947    };
948    let scope = generated_function_scope(db, generated);
949    match scope.filter(|scope| opens_generated_function_scope(scope.kind(db))) {
950        // Within another generated function - its path is the prefix of this one's.
951        Some(outer) if GeneratedFunctionKind::of(outer.kind(db)).is_some() => {
952            for_each_generated_function_path_segment(db, outer, handle_segment)?;
953        }
954        // Directly within a function - the path is relative to it.
955        Some(_) => {}
956        // Within no function of its own file - it is in the expansion of a macro.
957        None => {
958            for segment in macro_expansion_segments(db, generated) {
959                handle_segment(segment)?;
960            }
961        }
962    }
963    let index = scope.map_or(0, |scope| generated_function_indices(db, (), scope)[&generated]);
964    handle_segment(GeneratedFunctionPathSegment(GeneratedFunctionPathSegmentKind::Generated {
965        kind,
966        index,
967    }))
968}
969
970/// Returns the path of the generated function created for `node` - or for its closest ancestor that
971/// is lowered into a generated function - relative to the function it is defined in.
972///
973/// Every level of nesting adds a `{<kind>#<index>}` segment, where the index counts, per kind, the
974/// generated functions directly within the enclosing generated function (or within the function
975/// itself). So the closures of:
976/// ```ignore
977/// fn foo() {
978///     let outer = |x| {
979///         let inner = |y| y;
980///         inner(x)
981///     };
982///     let last = |z| z;
983/// }
984/// ```
985/// are `{closure#0}`, `{closure#0}::{closure#0}` and `{closure#1}`.
986///
987/// As the path only depends on the code of the containing function, it makes the names of
988/// generated functions independent of the path of the file they are defined in, and of any code
989/// outside of their containing function.
990pub fn generated_function_path<'db>(
991    db: &'db dyn Database,
992    node: SyntaxNode<'db>,
993) -> impl std::fmt::Display + 'db {
994    GeneratedFunctionPath { db, node }
995}
996
997/// The [std::fmt::Display] of [generated_function_path].
998struct GeneratedFunctionPath<'db> {
999    db: &'db dyn Database,
1000    node: SyntaxNode<'db>,
1001}
1002impl std::fmt::Display for GeneratedFunctionPath<'_> {
1003    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1004        let mut is_first = true;
1005        for_each_generated_function_path_segment(self.db, self.node, &mut |segment| {
1006            let separator = if std::mem::take(&mut is_first) { "" } else { "::" };
1007            match write!(f, "{separator}{segment}") {
1008                Ok(()) => ControlFlow::Continue(()),
1009                Err(err) => ControlFlow::Break(err),
1010            }
1011        })
1012        .break_value()
1013        .map_or(Ok(()), Err)
1014    }
1015}
1016
1017/// Returns the index of each generated function directly within `scope`, counted per kind in the
1018/// order the functions appear in it.
1019///
1020/// Memoized per scope, so naming every generated function of a function costs one pass over its
1021/// body rather than one per name.
1022#[salsa::tracked(returns(ref))]
1023fn generated_function_indices<'db>(
1024    db: &'db dyn Database,
1025    _tracked: Tracked,
1026    scope: SyntaxNode<'db>,
1027) -> OrderedHashMap<SyntaxNode<'db>, usize> {
1028    let mut indices = OrderedHashMap::default();
1029    // Of a `for` scope, only its body is lowered into it.
1030    let lowered_scope = for_loop_body(db, scope).unwrap_or(scope);
1031    collect_generated_function_indices(db, lowered_scope, &mut Default::default(), &mut indices);
1032    indices
1033}
1034
1035/// Accumulates into `indices` the index of each generated function directly within `node`'s scope,
1036/// counting each kind in `counters`.
1037fn collect_generated_function_indices<'db>(
1038    db: &'db dyn Database,
1039    node: SyntaxNode<'db>,
1040    counters: &mut UnorderedHashMap<GeneratedFunctionKind, usize>,
1041    indices: &mut OrderedHashMap<SyntaxNode<'db>, usize>,
1042) {
1043    for child in node.get_children(db) {
1044        let child_kind = child.kind(db);
1045        // A nested function is not a generated function, and its body opens a scope of its own.
1046        if GENERATED_FUNCTION_OWNER_KINDS.contains(&child_kind) {
1047            continue;
1048        }
1049        // A nested generated function is indexed here, but its body belongs to its own scope -
1050        // except for the iterable of a `for`, which is lowered into this scope.
1051        if let Some(kind) = GeneratedFunctionKind::of(child_kind) {
1052            let counter = counters.entry(kind).or_default();
1053            indices.insert(*child, *counter);
1054            *counter += 1;
1055            if let Some(body) = for_loop_body(db, *child) {
1056                for part in child.get_children(db).iter().filter(|part| **part != body) {
1057                    collect_generated_function_indices(db, *part, counters, indices);
1058                }
1059            }
1060            continue;
1061        }
1062        collect_generated_function_indices(db, *child, counters, indices);
1063    }
1064}