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 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 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 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 fn identifier(&self, db: &'a dyn Database) -> SmolStrId<'a> {
119 self.segments(db).elements(db).next_back().unwrap().identifier(db)
120 }
121}
122
123pub 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 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 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
182pub trait NameGreen<'a> {
184 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
212pub trait HasName<'a> {
214 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 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
261pub 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
272pub trait QueryAttrs<'a> {
274 #[doc(hidden)]
279 fn try_attributes(&self, db: &'a dyn Database) -> Option<AttributeList<'a>>;
280
281 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 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 fn find_attr(&self, db: &'a dyn Database, attr: &'a str) -> Option<Attribute<'a>> {
299 self.query_attr(db, attr).next()
300 }
301
302 fn has_attr(&self, db: &'a dyn Database, attr: &'a str) -> bool {
304 self.find_attr(db, attr).is_some()
305 }
306
307 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
507impl<'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 fn arg_list(&self, db: &'a dyn Database) -> Option<ast::ArgList<'a>>;
599 fn right_bracket_syntax_node(&self, db: &'a dyn Database) -> SyntaxNode<'a>;
601 fn left_bracket_syntax_node(&self, db: &'a dyn Database) -> SyntaxNode<'a>;
603 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 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 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
670pub trait BodyItems<'a> {
672 type Item: 'a;
674 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
705pub trait UsePathEx<'a> {
707 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 pub fn name_stable_ptr(&self, db: &'a dyn Database) -> SyntaxStablePtrId<'a> {
730 self.name(db).stable_ptr(db).untyped()
731 }
732}
733
734pub trait IsDependentType<'db> {
736 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 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#[derive(Clone, Copy, PartialEq, Eq, Hash)]
824enum GeneratedFunctionKind {
825 Closure,
826 Loop,
827}
828impl GeneratedFunctionKind {
829 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 fn name(self) -> &'static str {
840 match self {
841 Self::Closure => "closure",
842 Self::Loop => "loop",
843 }
844 }
845}
846
847const GENERATED_FUNCTION_OWNER_KINDS: [SyntaxKind; 2] =
849 [SyntaxKind::FunctionWithBody, SyntaxKind::TraitItemFunction];
850
851fn opens_generated_function_scope(kind: SyntaxKind) -> bool {
854 GeneratedFunctionKind::of(kind).is_some() || GENERATED_FUNCTION_OWNER_KINDS.contains(&kind)
855}
856
857fn 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
864fn 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 generated.ancestors(db).last()
882}
883
884pub 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
900enum GeneratedFunctionPathSegmentKind<'db> {
902 Generated { kind: GeneratedFunctionKind, index: usize },
904 MacroExpansion { name: &'db str, offset: u32 },
906}
907
908fn 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
935pub 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 Some(outer) if GeneratedFunctionKind::of(outer.kind(db)).is_some() => {
952 for_each_generated_function_path_segment(db, outer, handle_segment)?;
953 }
954 Some(_) => {}
956 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
970pub 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
997struct 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#[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 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
1035fn 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 if GENERATED_FUNCTION_OWNER_KINDS.contains(&child_kind) {
1047 continue;
1048 }
1049 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}