1use std::collections::VecDeque;
2use std::sync::Arc;
3
4use cairo_lang_diagnostics::{
5 DiagnosticNote, Maybe, MaybeAsRef, PluginFileDiagnosticNotes, ToMaybe, skip_diagnostic,
6};
7use cairo_lang_filesystem::db::{ExtAsVirtual, FilesGroup, files_group_input};
8use cairo_lang_filesystem::ids::{
9 BlobId, CrateId, CrateInput, Directory, FileId, FileKind, FileLongId, SmolStrId, Tracked,
10 VirtualFile,
11};
12use cairo_lang_parser::db::ParserGroup;
13use cairo_lang_syntax::attribute::consts::{
14 ALLOW_ATTR, ALLOW_ATTR_ATTR, DEPRECATED_ATTR, FEATURE_ATTR, FMT_SKIP_ATTR,
15 IMPLICIT_PRECEDENCE_ATTR, INLINE_ATTR, INTERNAL_ATTR, MUST_USE_ATTR, PATH_ATTR, PHANTOM_ATTR,
16 STARKNET_INTERFACE_ATTR, UNSTABLE_ATTR,
17};
18use cairo_lang_syntax::attribute::structured::AttributeStructurize;
19use cairo_lang_syntax::node::ast::MaybeModuleBody;
20use cairo_lang_syntax::node::helpers::QueryAttrs;
21use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
22use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
23use cairo_lang_utils::Intern;
24use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
25use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
26use itertools::{Itertools, chain};
27use salsa::{Database, Setter};
28
29use crate::cache::{
30 CacheBlobReader, DefCacheLoadingData, EXTERNAL_CACHE_SECTION, ExternalFileContentCached,
31 ExternalFileKey, load_cached_crate_modules,
32};
33use crate::ids::*;
34use crate::plugin::{DynGeneratedFileAuxData, MacroPlugin, MacroPluginMetadata, PluginDiagnostic};
35use crate::plugin_utils::try_extract_unnamed_arg;
36
37#[salsa::input]
38pub struct DefsGroupInput {
39 #[returns(ref)]
40 pub default_macro_plugins: Option<Vec<MacroPluginLongId>>,
41 #[returns(ref)]
42 pub macro_plugin_overrides: Option<OrderedHashMap<CrateInput, Arc<[MacroPluginLongId]>>>,
43 #[returns(ref)]
44 pub default_inline_macro_plugins: Option<OrderedHashMap<String, InlineMacroExprPluginLongId>>,
45 #[returns(ref)]
46 pub inline_macro_plugin_overrides: Option<
47 OrderedHashMap<CrateInput, Arc<OrderedHashMap<String, InlineMacroExprPluginLongId>>>,
48 >,
49}
50
51#[salsa::tracked(returns(ref))]
54pub fn defs_group_input(db: &dyn Database) -> DefsGroupInput {
55 DefsGroupInput::new(db, None, None, None, None)
56}
57
58pub trait DefsGroup: Database {
61 fn default_macro_plugins_input(&self) -> &[MacroPluginLongId] {
62 defs_group_input(self.as_dyn_database()).default_macro_plugins(self).as_ref().unwrap()
63 }
64
65 fn macro_plugin_overrides_input(
66 &self,
67 ) -> &OrderedHashMap<CrateInput, Arc<[MacroPluginLongId]>> {
68 defs_group_input(self.as_dyn_database()).macro_plugin_overrides(self).as_ref().unwrap()
69 }
70
71 fn inline_macro_plugin_overrides_input(
72 &self,
73 ) -> &OrderedHashMap<CrateInput, Arc<OrderedHashMap<String, InlineMacroExprPluginLongId>>> {
74 defs_group_input(self.as_dyn_database())
75 .inline_macro_plugin_overrides(self)
76 .as_ref()
77 .unwrap()
78 }
79
80 fn default_inline_macro_plugins_input(
81 &self,
82 ) -> &OrderedHashMap<String, InlineMacroExprPluginLongId> {
83 defs_group_input(self.as_dyn_database())
84 .default_inline_macro_plugins(self)
85 .as_ref()
86 .unwrap()
87 }
88
89 fn default_macro_plugins<'db>(&'db self) -> &'db [MacroPluginId<'db>] {
94 default_macro_plugins(self.as_dyn_database())
95 }
96
97 fn macro_plugin_overrides<'db>(
99 &'db self,
100 ) -> &'db OrderedHashMap<CrateId<'db>, Vec<MacroPluginId<'db>>> {
101 macro_plugin_overrides(self.as_dyn_database())
102 }
103
104 fn crate_macro_plugins<'db>(&'db self, crate_id: CrateId<'db>) -> &'db [MacroPluginId<'db>] {
109 crate_macro_plugins(self.as_dyn_database(), crate_id)
110 }
111
112 fn default_inline_macro_plugins<'db>(
114 &'db self,
115 ) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
116 default_inline_macro_plugins(self.as_dyn_database())
117 }
118
119 fn inline_macro_plugin_overrides<'db>(
121 &'db self,
122 ) -> &'db OrderedHashMap<CrateId<'db>, OrderedHashMap<String, InlineMacroExprPluginId<'db>>>
123 {
124 inline_macro_plugin_overrides(self.as_dyn_database())
125 }
126
127 fn crate_inline_macro_plugins<'db>(
132 &'db self,
133 crate_id: CrateId<'db>,
134 ) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
135 crate_inline_macro_plugins(self.as_dyn_database(), crate_id)
136 }
137
138 fn allowed_attributes<'db>(
141 &'db self,
142 crate_id: CrateId<'db>,
143 ) -> &'db OrderedHashSet<SmolStrId<'db>> {
144 allowed_attributes(self.as_dyn_database(), crate_id)
145 }
146
147 fn allowed_statement_attributes<'db>(&'db self) -> &'db OrderedHashSet<SmolStrId<'db>> {
150 allowed_statement_attributes(self.as_dyn_database())
151 }
152
153 fn declared_derives<'db>(
156 &'db self,
157 crate_id: CrateId<'db>,
158 ) -> &'db OrderedHashSet<SmolStrId<'db>> {
159 declared_derives(self.as_dyn_database(), crate_id)
160 }
161
162 fn declared_phantom_type_attributes<'db>(
165 &'db self,
166 crate_id: CrateId<'db>,
167 ) -> &'db OrderedHashSet<SmolStrId<'db>> {
168 declared_phantom_type_attributes(self.as_dyn_database(), crate_id)
169 }
170
171 fn is_submodule_inline<'db>(&self, submodule_id: SubmoduleId<'db>) -> bool {
173 is_submodule_inline(self.as_dyn_database(), submodule_id)
174 }
175
176 fn module_main_file<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<FileId<'db>> {
180 module_main_file(self.as_dyn_database(), module_id)
181 }
182 fn module_files<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [FileId<'db>]> {
184 module_files(self.as_dyn_database(), module_id)
185 }
186 fn module_dir<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db Directory<'db>> {
188 module_dir(self.as_dyn_database(), module_id)
189 }
190
191 fn crate_modules<'db>(&'db self, crate_id: CrateId<'db>) -> &'db [ModuleId<'db>] {
193 crate_modules(self.as_dyn_database(), crate_id)
194 }
195 fn file_modules<'db>(&'db self, file_id: FileId<'db>) -> Maybe<&'db Vec<ModuleId<'db>>> {
196 file_modules(self.as_dyn_database(), file_id).maybe_as_ref()
197 }
198
199 fn cached_crate_modules<'db>(
202 &'db self,
203 crate_id: CrateId<'db>,
204 ) -> Option<ModuleDataCacheAndLoadingData<'db>> {
205 cached_crate_modules(self.as_dyn_database(), crate_id)
206 }
207 fn module_submodules_ids<'db>(
208 &'db self,
209 module_id: ModuleId<'db>,
210 ) -> Maybe<&'db [SubmoduleId<'db>]> {
211 module_submodules_ids(self.as_dyn_database(), module_id)
212 }
213 fn module_constants_ids<'db>(
214 &'db self,
215 module_id: ModuleId<'db>,
216 ) -> Maybe<&'db [ConstantId<'db>]> {
217 module_constants_ids(self.as_dyn_database(), module_id)
218 }
219 fn module_constant_by_id<'db>(
220 &'db self,
221 constant_data: ConstantId<'db>,
222 ) -> Maybe<ast::ItemConstant<'db>> {
223 module_constant_by_id(self.as_dyn_database(), constant_data)
224 }
225 fn module_free_functions_ids<'db>(
226 &'db self,
227 module_id: ModuleId<'db>,
228 ) -> Maybe<&'db [FreeFunctionId<'db>]> {
229 module_free_functions_ids(self.as_dyn_database(), module_id)
230 }
231 fn module_free_function_by_id<'db>(
232 &'db self,
233 free_function_id: FreeFunctionId<'db>,
234 ) -> Maybe<ast::FunctionWithBody<'db>> {
235 module_free_function_by_id(self.as_dyn_database(), free_function_id)
236 }
237 fn module_item_name_stable_ptr<'db>(
239 &'db self,
240 module_id: ModuleId<'db>,
241 item_id: ModuleItemId<'db>,
242 ) -> Maybe<SyntaxStablePtrId<'db>> {
243 module_item_name_stable_ptr(self.as_dyn_database(), module_id, item_id)
244 }
245 fn module_uses_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [UseId<'db>]> {
246 module_uses_ids(self.as_dyn_database(), module_id)
247 }
248 fn module_use_by_id<'db>(&'db self, use_id: UseId<'db>) -> Maybe<ast::UsePathLeaf<'db>> {
249 module_use_by_id(self.as_dyn_database(), use_id)
250 }
251 fn module_global_use_by_id<'db>(
252 &'db self,
253 global_use_id: GlobalUseId<'db>,
254 ) -> Maybe<ast::UsePathStar<'db>> {
255 module_global_use_by_id(self.as_dyn_database(), global_use_id)
256 }
257 fn module_structs_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [StructId<'db>]> {
258 module_structs_ids(self.as_dyn_database(), module_id)
259 }
260 fn module_struct_by_id<'db>(
261 &'db self,
262 struct_id: StructId<'db>,
263 ) -> Maybe<ast::ItemStruct<'db>> {
264 module_struct_by_id(self.as_dyn_database(), struct_id)
265 }
266 fn module_enums_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [EnumId<'db>]> {
267 module_enums_ids(self.as_dyn_database(), module_id)
268 }
269 fn module_enum_by_id<'db>(&'db self, enum_id: EnumId<'db>) -> Maybe<ast::ItemEnum<'db>> {
270 module_enum_by_id(self.as_dyn_database(), enum_id)
271 }
272 fn module_type_aliases_ids<'db>(
273 &'db self,
274 module_id: ModuleId<'db>,
275 ) -> Maybe<&'db [ModuleTypeAliasId<'db>]> {
276 module_type_aliases_ids(self.as_dyn_database(), module_id)
277 }
278 fn module_type_alias_by_id<'db>(
279 &'db self,
280 module_type_alias_id: ModuleTypeAliasId<'db>,
281 ) -> Maybe<ast::ItemTypeAlias<'db>> {
282 module_type_alias_by_id(self.as_dyn_database(), module_type_alias_id)
283 }
284 fn module_impl_aliases_ids<'db>(
285 &'db self,
286 module_id: ModuleId<'db>,
287 ) -> Maybe<&'db [ImplAliasId<'db>]> {
288 module_impl_aliases_ids(self.as_dyn_database(), module_id)
289 }
290 fn module_impl_alias_by_id<'db>(
291 &'db self,
292 impl_alias_id: ImplAliasId<'db>,
293 ) -> Maybe<ast::ItemImplAlias<'db>> {
294 module_impl_alias_by_id(self.as_dyn_database(), impl_alias_id)
295 }
296 fn module_traits_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [TraitId<'db>]> {
297 module_traits_ids(self.as_dyn_database(), module_id)
298 }
299 fn module_trait_by_id<'db>(&'db self, trait_id: TraitId<'db>) -> Maybe<ast::ItemTrait<'db>> {
300 module_trait_by_id(self.as_dyn_database(), trait_id)
301 }
302 fn module_impls_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [ImplDefId<'db>]> {
303 module_impls_ids(self.as_dyn_database(), module_id)
304 }
305 fn module_impl_by_id<'db>(&'db self, impl_id: ImplDefId<'db>) -> Maybe<ast::ItemImpl<'db>> {
306 module_impl_by_id(self.as_dyn_database(), impl_id)
307 }
308 fn module_extern_types_ids<'db>(
309 &'db self,
310 module_id: ModuleId<'db>,
311 ) -> Maybe<&'db [ExternTypeId<'db>]> {
312 module_extern_types_ids(self.as_dyn_database(), module_id)
313 }
314 fn module_extern_type_by_id<'db>(
315 &'db self,
316 extern_type_id: ExternTypeId<'db>,
317 ) -> Maybe<ast::ItemExternType<'db>> {
318 module_extern_type_by_id(self.as_dyn_database(), extern_type_id)
319 }
320 fn module_extern_functions_ids<'db>(
321 &'db self,
322 module_id: ModuleId<'db>,
323 ) -> Maybe<&'db [ExternFunctionId<'db>]> {
324 module_extern_functions_ids(self.as_dyn_database(), module_id)
325 }
326 fn module_extern_function_by_id<'db>(
327 &'db self,
328 extern_function_id: ExternFunctionId<'db>,
329 ) -> Maybe<ast::ItemExternFunction<'db>> {
330 module_extern_function_by_id(self.as_dyn_database(), extern_function_id)
331 }
332
333 fn module_macro_declarations_ids<'db>(
335 &'db self,
336 module_id: ModuleId<'db>,
337 ) -> Maybe<&'db [MacroDeclarationId<'db>]> {
338 module_macro_declarations_ids(self.as_dyn_database(), module_id)
339 }
340 fn module_macro_declaration_by_id<'db>(
342 &'db self,
343 macro_declaration_id: MacroDeclarationId<'db>,
344 ) -> Maybe<ast::ItemMacroDeclaration<'db>> {
345 module_macro_declaration_by_id(self.as_dyn_database(), macro_declaration_id)
346 }
347
348 fn module_macro_calls_ids<'db>(
350 &'db self,
351 module_id: ModuleId<'db>,
352 ) -> Maybe<&'db [MacroCallId<'db>]> {
353 module_macro_calls_ids(self.as_dyn_database(), module_id)
354 }
355 fn module_macro_call_by_id<'db>(
357 &'db self,
358 macro_call_id: MacroCallId<'db>,
359 ) -> Maybe<ast::ItemInlineMacro<'db>> {
360 module_macro_call_by_id(self.as_dyn_database(), macro_call_id)
361 }
362 fn module_ancestors<'db>(
364 &'db self,
365 module_id: ModuleId<'db>,
366 ) -> &'db OrderedHashSet<ModuleId<'db>> {
367 module_ancestors_helper(self.as_dyn_database(), (), module_id)
368 }
369 fn module_perceived_module<'db>(&'db self, module_id: ModuleId<'db>) -> ModuleId<'db> {
373 module_perceived_module_helper(self.as_dyn_database(), (), module_id)
374 }
375}
376
377impl<T: Database + ?Sized> DefsGroup for T {}
378
379pub fn init_defs_group(db: &mut dyn Database) {
381 defs_group_input(db).set_macro_plugin_overrides(db).to(Some(OrderedHashMap::default()));
382 defs_group_input(db).set_inline_macro_plugin_overrides(db).to(Some(OrderedHashMap::default()));
383}
384
385#[salsa::tracked(returns(ref))]
386fn default_macro_plugins_helper<'db>(db: &'db dyn Database) -> Vec<MacroPluginId<'db>> {
387 db.default_macro_plugins_input()
388 .iter()
389 .map(|plugin| MacroPluginId::new(db, plugin.clone()))
390 .collect()
391}
392
393pub fn default_macro_plugins<'db>(db: &'db dyn Database) -> &'db [MacroPluginId<'db>] {
394 default_macro_plugins_helper(db)
395}
396
397#[salsa::tracked(returns(ref))]
398pub fn macro_plugin_overrides<'db>(
399 db: &'db dyn Database,
400) -> OrderedHashMap<CrateId<'db>, Vec<MacroPluginId<'db>>> {
401 let inp = db.macro_plugin_overrides_input();
402 inp.iter()
403 .map(|(crate_id, plugins)| {
404 (
405 crate_id.clone().into_crate_long_id(db).intern(db),
406 plugins.iter().map(|plugin| plugin.clone().intern(db)).collect(),
407 )
408 })
409 .collect()
410}
411
412#[salsa::tracked(returns(ref))]
413pub fn inline_macro_plugin_overrides<'db>(
414 db: &'db dyn Database,
415) -> OrderedHashMap<CrateId<'db>, OrderedHashMap<String, InlineMacroExprPluginId<'db>>> {
416 let inp = db.inline_macro_plugin_overrides_input();
417 inp.iter()
418 .map(|(crate_id, plugins)| {
419 (
420 crate_id.clone().into_crate_long_id(db).intern(db),
421 plugins
422 .iter()
423 .map(|(name, plugin)| (name.clone(), plugin.clone().intern(db)))
424 .collect(),
425 )
426 })
427 .collect()
428}
429
430#[salsa::tracked(returns(ref))]
431pub fn default_inline_macro_plugins<'db>(
432 db: &'db dyn Database,
433) -> OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
434 let inp = db.default_inline_macro_plugins_input();
435 inp.iter().map(|(name, plugin)| (name.clone(), plugin.clone().intern(db))).collect()
436}
437
438fn crate_macro_plugins<'db>(
439 db: &'db dyn Database,
440 crate_id: CrateId<'db>,
441) -> &'db [MacroPluginId<'db>] {
442 macro_plugin_overrides(db).get(&crate_id).unwrap_or_else(|| default_macro_plugins_helper(db))
443}
444
445fn crate_inline_macro_plugins<'db>(
446 db: &'db dyn Database,
447 crate_id: CrateId<'db>,
448) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
449 db.inline_macro_plugin_overrides()
450 .get(&crate_id)
451 .unwrap_or_else(|| db.default_inline_macro_plugins())
452}
453
454#[salsa::tracked(returns(ref))]
455fn allowed_attributes<'db>(
456 db: &'db dyn Database,
457 crate_id: CrateId<'db>,
458) -> OrderedHashSet<SmolStrId<'db>> {
459 let base_attrs = [
460 INLINE_ATTR,
461 MUST_USE_ATTR,
462 UNSTABLE_ATTR,
463 DEPRECATED_ATTR,
464 INTERNAL_ATTR,
465 ALLOW_ATTR,
466 ALLOW_ATTR_ATTR,
467 FEATURE_ATTR,
468 PHANTOM_ATTR,
469 IMPLICIT_PRECEDENCE_ATTR,
470 FMT_SKIP_ATTR,
471 PATH_ATTR,
472 STARKNET_INTERFACE_ATTR,
474 ];
475
476 let crate_plugins = db.crate_macro_plugins(crate_id);
477
478 OrderedHashSet::from_iter(chain!(
479 base_attrs.map(|attr| SmolStrId::from(db, attr)),
480 crate_plugins.iter().flat_map(|plugin| plugin.long(db).declared_attributes(db))
481 ))
482}
483
484#[salsa::tracked(returns(ref))]
486fn allowed_statement_attributes<'db>(db: &'db dyn Database) -> OrderedHashSet<SmolStrId<'db>> {
487 let all_attributes = [FMT_SKIP_ATTR, ALLOW_ATTR, FEATURE_ATTR];
488 OrderedHashSet::from_iter(all_attributes.map(|attr| SmolStrId::from(db, attr)))
489}
490
491#[salsa::tracked(returns(ref))]
492fn declared_derives<'db>(
493 db: &'db dyn Database,
494 crate_id: CrateId<'db>,
495) -> OrderedHashSet<SmolStrId<'db>> {
496 OrderedHashSet::from_iter(
497 db.crate_macro_plugins(crate_id)
498 .iter()
499 .flat_map(|plugin| plugin.long(db).declared_derives(db)),
500 )
501}
502
503#[salsa::tracked(returns(ref))]
504fn declared_phantom_type_attributes<'db>(
505 db: &'db dyn Database,
506 crate_id: CrateId<'db>,
507) -> OrderedHashSet<SmolStrId<'db>> {
508 let crate_plugins = db.crate_macro_plugins(crate_id);
509
510 OrderedHashSet::from_iter(chain!(
511 [SmolStrId::from(db, PHANTOM_ATTR)],
512 crate_plugins.iter().flat_map(|plugin| plugin.long(db).phantom_type_attributes(db))
513 ))
514}
515
516#[salsa::tracked]
517fn is_submodule_inline<'db>(db: &'db dyn Database, submodule_id: SubmoduleId<'db>) -> bool {
518 match submodule_id.stable_ptr(db).lookup(db).body(db) {
519 MaybeModuleBody::Some(_) => true,
520 MaybeModuleBody::None(_) => false,
521 }
522}
523
524#[salsa::tracked]
525fn module_main_file_helper<'db>(
526 db: &'db dyn Database,
527 _tracked: Tracked,
528 module_id: ModuleId<'db>,
529) -> Maybe<FileId<'db>> {
530 Ok(match module_id {
531 ModuleId::CrateRoot(crate_id) => {
532 db.crate_config(crate_id).to_maybe()?.root.file(db, "lib.cairo")
533 }
534 ModuleId::Submodule(submodule_id) => {
535 let parent = submodule_id.parent_module(db);
536 if db.is_submodule_inline(submodule_id) {
537 submodule_id.stable_ptr(db).untyped().file_id(db)
541 } else if let Some(path_override) = submodule_path_override(db, submodule_id) {
542 path_override
543 } else {
544 db.module_dir(parent)?
545 .file(db, &format!("{}.cairo", submodule_id.name(db).long(db)))
546 }
547 }
548 ModuleId::MacroCall { generated_file_id, .. } => generated_file_id,
550 })
551}
552
553fn module_main_file<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<FileId<'db>> {
554 module_main_file_helper(db, (), module_id)
555}
556
557fn module_files<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<&'db [FileId<'db>]> {
558 Ok(module_id.module_data(db)?.files(db))
559}
560
561#[salsa::tracked(returns(ref))]
563fn module_dir_helper<'db>(
564 db: &'db dyn Database,
565 _tracked: Tracked,
566 module_id: ModuleId<'db>,
567) -> Maybe<Directory<'db>> {
568 match module_id {
569 ModuleId::CrateRoot(crate_id) => {
570 db.crate_config(crate_id).to_maybe().map(|config| config.root.clone())
571 }
572 ModuleId::Submodule(submodule_id) => {
573 let parent = submodule_id.parent_module(db);
574 let name = submodule_id.name(db);
575 Ok(db.module_dir(parent)?.subdir(name.long(db)))
576 }
577 ModuleId::MacroCall { id, .. } => db.module_dir(id.parent_module(db)).cloned(),
580 }
581}
582
583fn submodule_path_override<'db>(
587 db: &'db dyn Database,
588 submodule_id: SubmoduleId<'db>,
589) -> Option<FileId<'db>> {
590 let parent = submodule_id.parent_module(db);
592 let parent_data = parent.module_data(db).ok()?;
593 let item_module_ast = parent_data.submodules(db).get(&submodule_id)?.clone();
594
595 let attr_ast = item_module_ast.find_attr(db, PATH_ATTR)?;
596 let terminal = extract_path_arg(db, &attr_ast.arguments(db))?;
597 let path = terminal.string_value(db)?;
598 fn find_base<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> ModuleId<'db> {
600 match module_id {
601 ModuleId::CrateRoot(crate_id) => ModuleId::CrateRoot(crate_id),
602 ModuleId::Submodule(submodule_id) => submodule_id.parent_module(db),
603 ModuleId::MacroCall { id, .. } => find_base(db, id.parent_module(db)),
604 }
605 }
606 let base = find_base(db, parent);
607 Some(db.module_dir(base).ok()?.file(db, &path))
608}
609
610fn module_dir<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<&'db Directory<'db>> {
611 module_dir_helper(db, (), module_id).maybe_as_ref()
612}
613
614fn collect_modules_under<'db>(
616 db: &'db dyn Database,
617 modules: &mut Vec<ModuleId<'db>>,
618 module_id: ModuleId<'db>,
619) {
620 modules.push(module_id);
621 if let Ok(submodule_ids) = db.module_submodules_ids(module_id) {
622 for submodule_module_id in submodule_ids.iter().copied() {
623 collect_modules_under(db, modules, ModuleId::Submodule(submodule_module_id));
624 }
625 }
626}
627
628#[salsa::tracked(returns(ref))]
630fn crate_modules<'db>(db: &'db dyn Database, crate_id: CrateId<'db>) -> Vec<ModuleId<'db>> {
631 let mut modules = Vec::new();
632 collect_modules_under(db, &mut modules, ModuleId::CrateRoot(crate_id));
633 modules
634}
635
636#[salsa::tracked(returns(ref))]
638fn file_to_module_mapping<'db>(
639 db: &'db dyn Database,
640) -> OrderedHashMap<FileId<'db>, Vec<ModuleId<'db>>> {
641 let mut mapping = OrderedHashMap::<FileId<'db>, Vec<ModuleId<'db>>>::default();
642 for crate_id in db.crates() {
643 for module_id in db.crate_modules(*crate_id).iter().copied() {
644 if let Ok(files) = db.module_files(module_id) {
645 for file_id in files.iter().copied() {
646 match mapping.get_mut(&file_id) {
647 Some(file_modules) => {
648 file_modules.push(module_id);
649 }
650 None => {
651 mapping.insert(file_id, vec![module_id]);
652 }
653 }
654 }
655 }
656 }
657 }
658 mapping
659}
660
661#[salsa::tracked(returns(ref))]
662fn file_modules<'db>(db: &'db dyn Database, file_id: FileId<'db>) -> Maybe<Vec<ModuleId<'db>>> {
663 file_to_module_mapping(db).get(&file_id).cloned().to_maybe()
664}
665
666#[salsa::tracked]
667pub struct ModuleData<'db> {
668 #[returns(ref)]
671 pub items: Vec<ModuleItemId<'db>>,
672
673 #[returns(ref)]
676 types_data: ModuleTypesData<'db>,
677 #[returns(ref)]
679 named_items_data: ModuleNamedItemsData<'db>,
680 #[returns(ref)]
682 unnamed_items_data: ModuleUnnamedItemsData<'db>,
683 #[returns(ref)]
685 files_data: ModuleFilesData<'db>,
686}
687
688#[salsa::tracked]
689pub struct ModuleTypesData<'db> {
690 #[returns(ref)]
692 structs: OrderedHashMap<StructId<'db>, ast::ItemStruct<'db>>,
693 #[returns(ref)]
695 enums: OrderedHashMap<EnumId<'db>, ast::ItemEnum<'db>>,
696 #[returns(ref)]
698 type_aliases: OrderedHashMap<ModuleTypeAliasId<'db>, ast::ItemTypeAlias<'db>>,
699 #[returns(ref)]
701 extern_types: OrderedHashMap<ExternTypeId<'db>, ast::ItemExternType<'db>>,
702}
703
704#[salsa::tracked]
705pub struct ModuleNamedItemsData<'db> {
706 #[returns(ref)]
708 constants: OrderedHashMap<ConstantId<'db>, ast::ItemConstant<'db>>,
709 #[returns(ref)]
713 submodules: OrderedHashMap<SubmoduleId<'db>, ast::ItemModule<'db>>,
714 #[returns(ref)]
716 uses: OrderedHashMap<UseId<'db>, ast::UsePathLeaf<'db>>,
717 #[returns(ref)]
718 free_functions: OrderedHashMap<FreeFunctionId<'db>, ast::FunctionWithBody<'db>>,
719 #[returns(ref)]
721 impl_aliases: OrderedHashMap<ImplAliasId<'db>, ast::ItemImplAlias<'db>>,
722 #[returns(ref)]
724 traits: OrderedHashMap<TraitId<'db>, ast::ItemTrait<'db>>,
725 #[returns(ref)]
727 impls: OrderedHashMap<ImplDefId<'db>, ast::ItemImpl<'db>>,
728 #[returns(ref)]
730 extern_functions: OrderedHashMap<ExternFunctionId<'db>, ast::ItemExternFunction<'db>>,
731 #[returns(ref)]
733 macro_declarations: OrderedHashMap<MacroDeclarationId<'db>, ast::ItemMacroDeclaration<'db>>,
734}
735#[salsa::tracked]
736pub struct ModuleUnnamedItemsData<'db> {
737 #[returns(ref)]
739 global_uses: OrderedHashMap<GlobalUseId<'db>, ast::UsePathStar<'db>>,
740 #[returns(ref)]
742 macro_calls: OrderedHashMap<MacroCallId<'db>, ast::ItemInlineMacro<'db>>,
743}
744
745#[salsa::tracked]
746pub struct ModuleFilesData<'db> {
747 #[returns(ref)]
748 files: Vec<FileId<'db>>,
749 #[returns(ref)]
751 generated_file_aux_data: OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>>,
752 #[returns(ref)]
753 plugin_diagnostics: Vec<(ModuleId<'db>, PluginDiagnostic<'db>)>,
754 #[returns(ref)]
758 diagnostics_notes: PluginFileDiagnosticNotes<'db>,
759}
760
761impl<'db> ModuleData<'db> {
762 pub fn constants(
764 &self,
765 db: &'db dyn Database,
766 ) -> &'db OrderedHashMap<ConstantId<'db>, ast::ItemConstant<'db>> {
767 self.named_items_data(db).constants(db)
768 }
769 pub fn submodules(
771 &self,
772 db: &'db dyn Database,
773 ) -> &'db OrderedHashMap<SubmoduleId<'db>, ast::ItemModule<'db>> {
774 self.named_items_data(db).submodules(db)
775 }
776 pub fn uses(
778 &self,
779 db: &'db dyn Database,
780 ) -> &'db OrderedHashMap<UseId<'db>, ast::UsePathLeaf<'db>> {
781 self.named_items_data(db).uses(db)
782 }
783 pub fn free_functions(
785 &self,
786 db: &'db dyn Database,
787 ) -> &'db OrderedHashMap<FreeFunctionId<'db>, ast::FunctionWithBody<'db>> {
788 self.named_items_data(db).free_functions(db)
789 }
790 pub fn structs(
792 &self,
793 db: &'db dyn Database,
794 ) -> &'db OrderedHashMap<StructId<'db>, ast::ItemStruct<'db>> {
795 self.types_data(db).structs(db)
796 }
797
798 pub fn enums(
800 &self,
801 db: &'db dyn Database,
802 ) -> &'db OrderedHashMap<EnumId<'db>, ast::ItemEnum<'db>> {
803 self.types_data(db).enums(db)
804 }
805
806 pub fn type_aliases(
808 &self,
809 db: &'db dyn Database,
810 ) -> &'db OrderedHashMap<ModuleTypeAliasId<'db>, ast::ItemTypeAlias<'db>> {
811 self.types_data(db).type_aliases(db)
812 }
813
814 pub fn impl_aliases(
816 &self,
817 db: &'db dyn Database,
818 ) -> &'db OrderedHashMap<ImplAliasId<'db>, ast::ItemImplAlias<'db>> {
819 self.named_items_data(db).impl_aliases(db)
820 }
821 pub fn traits(
823 &self,
824 db: &'db dyn Database,
825 ) -> &'db OrderedHashMap<TraitId<'db>, ast::ItemTrait<'db>> {
826 self.named_items_data(db).traits(db)
827 }
828 pub fn impls(
830 &self,
831 db: &'db dyn Database,
832 ) -> &'db OrderedHashMap<ImplDefId<'db>, ast::ItemImpl<'db>> {
833 self.named_items_data(db).impls(db)
834 }
835 pub fn extern_types(
837 &self,
838 db: &'db dyn Database,
839 ) -> &'db OrderedHashMap<ExternTypeId<'db>, ast::ItemExternType<'db>> {
840 self.types_data(db).extern_types(db)
841 }
842 pub fn extern_functions(
844 &self,
845 db: &'db dyn Database,
846 ) -> &'db OrderedHashMap<ExternFunctionId<'db>, ast::ItemExternFunction<'db>> {
847 self.named_items_data(db).extern_functions(db)
848 }
849 pub fn macro_declarations(
851 &self,
852 db: &'db dyn Database,
853 ) -> &'db OrderedHashMap<MacroDeclarationId<'db>, ast::ItemMacroDeclaration<'db>> {
854 self.named_items_data(db).macro_declarations(db)
855 }
856 pub fn global_uses(
858 &self,
859 db: &'db dyn Database,
860 ) -> &'db OrderedHashMap<GlobalUseId<'db>, ast::UsePathStar<'db>> {
861 self.unnamed_items_data(db).global_uses(db)
862 }
863 pub fn macro_calls(
865 &self,
866 db: &'db dyn Database,
867 ) -> &'db OrderedHashMap<MacroCallId<'db>, ast::ItemInlineMacro<'db>> {
868 self.unnamed_items_data(db).macro_calls(db)
869 }
870
871 pub fn files(&self, db: &'db dyn Database) -> &'db Vec<FileId<'db>> {
873 self.files_data(db).files(db)
874 }
875
876 pub fn generated_file_aux_data(
878 &self,
879 db: &'db dyn Database,
880 ) -> &'db OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>> {
881 self.files_data(db).generated_file_aux_data(db)
882 }
883
884 pub fn plugin_diagnostics(
885 &self,
886 db: &'db dyn Database,
887 ) -> &'db Vec<(ModuleId<'db>, PluginDiagnostic<'db>)> {
888 self.files_data(db).plugin_diagnostics(db)
889 }
890
891 pub fn diagnostics_notes(&self, db: &'db dyn Database) -> &'db PluginFileDiagnosticNotes<'db> {
895 self.files_data(db).diagnostics_notes(db)
896 }
897}
898
899#[derive(Clone, Debug, Eq, PartialEq, salsa::Update)]
901pub struct PrivModuleSubFiles<'db> {
902 files: OrderedHashMap<FileId<'db>, VirtualFile<'db>>,
904 aux_data: OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>>,
906 items: Vec<ast::ModuleItem<'db>>,
908 plugin_diagnostics: Vec<PluginDiagnostic<'db>>,
910 diagnostics_notes: PluginFileDiagnosticNotes<'db>,
914}
915
916#[salsa::tracked]
917fn priv_module_data_helper<'db>(
918 db: &'db dyn Database,
919 _tracked: Tracked,
920 module_id: ModuleId<'db>,
921) -> Maybe<ModuleData<'db>> {
922 let crate_id = module_id.owning_crate(db);
923
924 if let Some((map, _)) = db.cached_crate_modules(crate_id) {
925 if let Some(module_data) = map.get(&module_id) {
926 return Ok(*module_data);
927 } else {
928 panic!("module not found in cached modules_data {:?}", module_id.name(db));
929 }
930 };
931
932 let module_file = db.module_main_file(module_id)?;
933 let main_file_aux_data = if let ModuleId::Submodule(submodule_id) = module_id {
934 let parent_module_data = submodule_id.module_data(db)?;
935 let item_module_ast = &parent_module_data.submodules(db)[&submodule_id];
936 if matches!(item_module_ast.body(db), MaybeModuleBody::Some(_)) {
937 parent_module_data.generated_file_aux_data(db)
944 [&item_module_ast.stable_ptr(db).untyped().file_id(db)]
945 .clone()
946 } else {
947 None
948 }
949 } else {
950 None
951 };
952 let mut file_queue = VecDeque::new();
953 file_queue.push_back(module_file);
954 let mut constants = OrderedHashMap::default();
955 let mut submodules = OrderedHashMap::default();
956 let mut uses = OrderedHashMap::default();
957 let mut free_functions = OrderedHashMap::default();
958 let mut structs = OrderedHashMap::default();
959 let mut enums = OrderedHashMap::default();
960 let mut type_aliases = OrderedHashMap::default();
961 let mut impl_aliases = OrderedHashMap::default();
962 let mut traits = OrderedHashMap::default();
963 let mut impls = OrderedHashMap::default();
964 let mut extern_types = OrderedHashMap::default();
965 let mut extern_functions = OrderedHashMap::default();
966 let mut macro_declarations = OrderedHashMap::default();
967 let mut macro_calls = OrderedHashMap::default();
968 let mut global_uses = OrderedHashMap::default();
969 let mut aux_data = OrderedHashMap::default();
970 let mut files = Vec::new();
971 let mut plugin_diagnostics = Vec::new();
972 let mut diagnostics_notes = OrderedHashMap::default();
973
974 let mut items = vec![];
975 aux_data.insert(module_file, main_file_aux_data);
976 while let Some(file_id) = file_queue.pop_front() {
977 files.push(file_id);
978
979 let priv_module_data = module_sub_files(db, module_id, file_id).maybe_as_ref()?;
980 diagnostics_notes
981 .extend(priv_module_data.diagnostics_notes.iter().map(|(k, v)| (*k, v.clone())));
982 file_queue.extend(priv_module_data.files.keys().copied());
983 plugin_diagnostics
984 .extend(priv_module_data.plugin_diagnostics.iter().map(|v| (module_id, v.clone())));
985 aux_data.extend(priv_module_data.aux_data.iter().map(|(k, v)| (*k, v.clone())));
986 for item_ast in &priv_module_data.items {
987 match item_ast.clone() {
988 ast::ModuleItem::Constant(constant) => {
989 let item_id = ConstantLongId(module_id, constant.stable_ptr(db)).intern(db);
990 constants.insert(item_id, constant);
991 items.push(ModuleItemId::Constant(item_id));
992 }
993 ast::ModuleItem::Module(module) => {
994 let item_id = SubmoduleLongId(module_id, module.stable_ptr(db)).intern(db);
995 submodules.insert(item_id, module);
996 items.push(ModuleItemId::Submodule(item_id));
997 }
998 ast::ModuleItem::Use(us) => {
999 for leaf in get_all_path_leaves(db, &us) {
1000 let id = UseLongId(module_id, leaf.stable_ptr(db)).intern(db);
1001 uses.insert(id, leaf);
1002 items.push(ModuleItemId::Use(id));
1003 }
1004 for star in get_all_path_stars(db, &us) {
1005 let id = GlobalUseLongId(module_id, star.stable_ptr(db)).intern(db);
1006 global_uses.insert(id, star);
1007 }
1008 }
1009 ast::ModuleItem::FreeFunction(function) => {
1010 let item_id = FreeFunctionLongId(module_id, function.stable_ptr(db)).intern(db);
1011 free_functions.insert(item_id, function);
1012 items.push(ModuleItemId::FreeFunction(item_id));
1013 }
1014 ast::ModuleItem::ExternFunction(extern_function) => {
1015 let item_id =
1016 ExternFunctionLongId(module_id, extern_function.stable_ptr(db)).intern(db);
1017 extern_functions.insert(item_id, extern_function);
1018 items.push(ModuleItemId::ExternFunction(item_id));
1019 }
1020 ast::ModuleItem::ExternType(extern_type) => {
1021 let item_id =
1022 ExternTypeLongId(module_id, extern_type.stable_ptr(db)).intern(db);
1023 extern_types.insert(item_id, extern_type);
1024 items.push(ModuleItemId::ExternType(item_id));
1025 }
1026 ast::ModuleItem::Trait(trt) => {
1027 let item_id = TraitLongId(module_id, trt.stable_ptr(db)).intern(db);
1028 traits.insert(item_id, trt);
1029 items.push(ModuleItemId::Trait(item_id));
1030 }
1031 ast::ModuleItem::Impl(imp) => {
1032 let item_id = ImplDefLongId(module_id, imp.stable_ptr(db)).intern(db);
1033 impls.insert(item_id, imp);
1034 items.push(ModuleItemId::Impl(item_id));
1035 }
1036 ast::ModuleItem::Struct(structure) => {
1037 let item_id = StructLongId(module_id, structure.stable_ptr(db)).intern(db);
1038 structs.insert(item_id, structure);
1039 items.push(ModuleItemId::Struct(item_id));
1040 }
1041 ast::ModuleItem::Enum(enm) => {
1042 let item_id = EnumLongId(module_id, enm.stable_ptr(db)).intern(db);
1043 enums.insert(item_id, enm);
1044 items.push(ModuleItemId::Enum(item_id));
1045 }
1046 ast::ModuleItem::TypeAlias(type_alias) => {
1047 let item_id =
1048 ModuleTypeAliasLongId(module_id, type_alias.stable_ptr(db)).intern(db);
1049 type_aliases.insert(item_id, type_alias);
1050 items.push(ModuleItemId::TypeAlias(item_id));
1051 }
1052 ast::ModuleItem::ImplAlias(impl_alias) => {
1053 let item_id = ImplAliasLongId(module_id, impl_alias.stable_ptr(db)).intern(db);
1054 impl_aliases.insert(item_id, impl_alias);
1055 items.push(ModuleItemId::ImplAlias(item_id));
1056 }
1057 ast::ModuleItem::MacroDeclaration(macro_declaration) => {
1058 let item_id =
1059 MacroDeclarationLongId(module_id, macro_declaration.stable_ptr(db))
1060 .intern(db);
1061 macro_declarations.insert(item_id, macro_declaration);
1062 items.push(ModuleItemId::MacroDeclaration(item_id));
1063 }
1064 ast::ModuleItem::InlineMacro(inline_macro_ast) => {
1065 let item_id =
1066 MacroCallLongId(module_id, inline_macro_ast.stable_ptr(db)).intern(db);
1067 macro_calls.insert(item_id, inline_macro_ast.clone());
1068 }
1069 ast::ModuleItem::HeaderDoc(_) => {}
1070 ast::ModuleItem::Missing(_) => {}
1071 }
1072 }
1073 }
1074 let types_data = ModuleTypesData::new(db, structs, enums, type_aliases, extern_types);
1075 let named_items_data = ModuleNamedItemsData::new(
1076 db,
1077 constants,
1078 submodules,
1079 uses,
1080 free_functions,
1081 impl_aliases,
1082 traits,
1083 impls,
1084 extern_functions,
1085 macro_declarations,
1086 );
1087 let unnamed_items_data = ModuleUnnamedItemsData::new(db, global_uses, macro_calls);
1088 let files_data =
1089 ModuleFilesData::new(db, files, aux_data, plugin_diagnostics, diagnostics_notes);
1090 Ok(ModuleData::new(db, items, types_data, named_items_data, unnamed_items_data, files_data))
1091}
1092
1093pub(crate) fn module_data<'db>(
1094 db: &'db dyn Database,
1095 module_id: ModuleId<'db>,
1096) -> Maybe<ModuleData<'db>> {
1097 priv_module_data_helper(db, (), module_id)
1098}
1099
1100pub type ModuleDataCacheAndLoadingData<'db> =
1101 (Arc<OrderedHashMap<ModuleId<'db>, ModuleData<'db>>>, Arc<DefCacheLoadingData<'db>>);
1102
1103#[salsa::tracked]
1104fn cached_crate_modules<'db>(
1105 db: &'db dyn Database,
1106 crate_id: CrateId<'db>,
1107) -> Option<ModuleDataCacheAndLoadingData<'db>> {
1108 load_cached_crate_modules(db, crate_id)
1109}
1110
1111pub fn init_external_files<T: DefsGroup>(db: &mut T) {
1112 let ext_as_virtual_impl: ExtAsVirtual =
1113 Arc::new(|db: &dyn Database, external_id: salsa::Id| ext_as_virtual_impl(db, external_id));
1114 files_group_input(db).set_ext_as_virtual_obj(db).to(Some(ext_as_virtual_impl));
1115}
1116
1117#[salsa::tracked(returns(ref))]
1120fn external_file_contents<'db>(
1121 db: &'db dyn Database,
1122 blob_id: BlobId<'db>,
1123 crate_id: CrateId<'db>,
1124) -> OrderedHashMap<ExternalFileKey, ExternalFileContentCached> {
1125 let Some(content) = db.blob_content(blob_id) else {
1126 return Default::default();
1127 };
1128
1129 CacheBlobReader::read_section(db, content, EXTERNAL_CACHE_SECTION, crate_id)
1130}
1131
1132#[salsa::tracked(returns(ref))]
1135fn cached_external_virtual_file<'db>(
1136 db: &'db dyn Database,
1137 external_file: FileId<'db>,
1138) -> Option<VirtualFile<'db>> {
1139 let FileLongId::External(raw_external_id) = external_file.long(db) else {
1140 return None;
1141 };
1142 let long_id = PluginGeneratedFileId::from_intern_id(*raw_external_id).long(db);
1143 let crate_id = long_id.module_id.owning_crate(db);
1144 let blob_id = db.crate_config(crate_id)?.cache_file?;
1145 let key = ExternalFileKey::new(db, external_file);
1147 let content = external_file_contents(db, blob_id, crate_id).get(&key)?;
1148 Some(content.to_virtual_file(db, &long_id.name, Some(long_id.stable_ptr.span_in_file(db))))
1149}
1150
1151pub fn ext_as_virtual_impl<'db>(
1153 db: &'db dyn Database,
1154 external_id: salsa::Id,
1155) -> &'db VirtualFile<'db> {
1156 let file_id = FileLongId::External(external_id).intern(db);
1157 if let Some(virtual_file) = cached_external_virtual_file(db, file_id) {
1159 return virtual_file;
1160 }
1161 let long_id = PluginGeneratedFileId::from_intern_id(external_id).long(db);
1162 if let Some(blob_id) =
1166 db.crate_config(long_id.module_id.owning_crate(db)).and_then(|config| config.cache_file)
1167 {
1168 assert!(
1169 db.blob_content(blob_id).is_none(),
1170 "external file missing from a loaded crate cache: {:?}",
1171 file_id.long(db)
1172 );
1173 }
1174 let data =
1175 module_sub_files(db, long_id.module_id, long_id.stable_ptr.file_id(db)).as_ref().unwrap();
1176 &data.files[&file_id]
1177}
1178
1179#[salsa::tracked(returns(ref))]
1180fn module_sub_files<'db>(
1182 db: &'db dyn Database,
1183 module_id: ModuleId<'db>,
1184 file_id: FileId<'db>,
1185) -> Maybe<PrivModuleSubFiles<'db>> {
1186 let module_main_file = db.module_main_file(module_id)?;
1187 let file_syntax = db.file_module_syntax(file_id)?;
1188 let item_asts = if module_main_file == file_id {
1189 if let ModuleId::Submodule(submodule_id) = module_id {
1190 let data = submodule_id.module_data(db)?;
1191 if let MaybeModuleBody::Some(body) = data.submodules(db)[&submodule_id].body(db) {
1192 Some(body.items(db))
1193 } else {
1194 None
1195 }
1196 } else {
1197 None
1198 }
1199 } else {
1200 None
1201 }
1202 .unwrap_or_else(|| file_syntax.items(db));
1203
1204 let crate_id = module_id.owning_crate(db);
1205
1206 let allowed_attributes = db.allowed_attributes(crate_id);
1207 let declared_derives = db.declared_derives(crate_id);
1208 let module_features = module_allowed_features(db, (), module_id);
1211
1212 let cfg_set = db
1213 .crate_config(crate_id)
1214 .and_then(|cfg| cfg.settings.cfg_set.as_ref())
1215 .unwrap_or(db.cfg_set());
1216 let edition = db
1217 .crate_config(module_id.owning_crate(db))
1218 .map(|cfg| cfg.settings.edition)
1219 .unwrap_or_default();
1220
1221 let mut files = OrderedHashMap::<_, _>::default();
1222 let mut aux_data = OrderedHashMap::default();
1223 let mut items = Vec::new();
1224 let mut plugin_diagnostics = Vec::new();
1225 let mut diagnostics_notes = OrderedHashMap::default();
1226 for item_ast in item_asts.elements(db) {
1227 let mut remove_original_item = false;
1228 let mut item_allowed_features = extract_allowed_features(db, &item_ast);
1231 let allowed_features = if item_allowed_features.is_empty() {
1232 module_features
1233 } else {
1234 item_allowed_features.extend(module_features.iter().copied());
1235 &item_allowed_features
1236 };
1237 let metadata = MacroPluginMetadata { cfg_set, declared_derives, allowed_features, edition };
1238 for plugin_id in db.crate_macro_plugins(crate_id).iter() {
1242 let plugin = plugin_id.long(db);
1243
1244 let result = plugin.generate_code(db, item_ast.clone(), &metadata);
1245 plugin_diagnostics.extend(result.diagnostics);
1246 if result.remove_original_item {
1247 remove_original_item = true;
1248 }
1249
1250 if let Some(generated) = result.code {
1251 let stable_ptr = item_ast.stable_ptr(db).untyped();
1252 let generated_file_id = FileLongId::External(
1253 PluginGeneratedFileLongId {
1254 module_id,
1255 stable_ptr,
1256 name: generated.name.clone(),
1257 }
1258 .intern(db)
1259 .as_intern_id(),
1260 )
1261 .intern(db);
1262 if let Some(text) = generated.diagnostics_note {
1263 diagnostics_notes
1264 .insert(generated_file_id, DiagnosticNote { text, location: None });
1265 }
1266 files.insert(
1267 generated_file_id,
1268 VirtualFile {
1269 parent: Some(stable_ptr.span_in_file(db)),
1270 name: SmolStrId::from(db, generated.name),
1271 content: SmolStrId::from(db, generated.content),
1272 code_mappings: generated.code_mappings.into(),
1273 kind: FileKind::Module,
1274 original_item_removed: remove_original_item,
1275 },
1276 );
1277 aux_data.insert(generated_file_id, generated.aux_data);
1278 }
1279 if remove_original_item {
1280 break;
1281 }
1282 }
1283 if remove_original_item {
1284 continue;
1286 }
1287 validate_attributes(db, allowed_attributes, &item_ast, &mut plugin_diagnostics);
1288 items.push(item_ast);
1289 }
1290 Ok(PrivModuleSubFiles { files, aux_data, items, plugin_diagnostics, diagnostics_notes })
1291}
1292
1293fn collect_extra_allowed_attributes<'db>(
1295 db: &'db dyn Database,
1296 item: &impl QueryAttrs<'db>,
1297 plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1298) -> OrderedHashSet<SmolStrId<'db>> {
1299 let mut extra_allowed_attributes = OrderedHashSet::default();
1300 for attr in item.query_attr(db, ALLOW_ATTR_ATTR) {
1301 let args = attr.clone().structurize(db).args;
1302 if args.is_empty() {
1303 plugin_diagnostics.push(PluginDiagnostic::error(
1304 attr.stable_ptr(db),
1305 "Expected arguments.".to_string(),
1306 ));
1307 continue;
1308 }
1309 for arg in args {
1310 if let Some(ast::Expr::Path(path)) = try_extract_unnamed_arg(db, &arg.arg)
1311 && let Ok(ast::PathSegment::Simple(segment)) =
1312 path.segments(db).elements(db).exactly_one()
1313 {
1314 extra_allowed_attributes.insert(segment.ident(db).text(db));
1315 continue;
1316 }
1317 plugin_diagnostics.push(PluginDiagnostic::error(
1318 arg.arg.stable_ptr(db),
1319 "Expected simple identifier.".to_string(),
1320 ));
1321 }
1322 }
1323 extra_allowed_attributes
1324}
1325
1326pub fn validate_attributes_flat<'db, Item: QueryAttrs<'db> + TypedSyntaxNode<'db>>(
1328 db: &'db dyn Database,
1329 allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1330 extra_allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1331 item: &Item,
1332 plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1333) {
1334 let local_extra_attributes = collect_extra_allowed_attributes(db, item, plugin_diagnostics);
1335 for attr in item.attributes_elements(db) {
1336 let attr_text = attr.attr(db).as_syntax_node().get_text_without_trivia(db);
1337 if !(allowed_attributes.contains(&attr_text)
1338 || extra_allowed_attributes.contains(&attr_text)
1339 || local_extra_attributes.contains(&attr_text))
1340 {
1341 plugin_diagnostics.push(PluginDiagnostic::error(
1342 attr.stable_ptr(db),
1343 "Unsupported attribute.".to_string(),
1344 ));
1345 }
1346 }
1347
1348 for attr in item.query_attr(db, PATH_ATTR) {
1350 let node = item.as_syntax_node();
1351 let Some(item_module) = ast::ItemModule::cast(db, node) else {
1352 plugin_diagnostics.push(PluginDiagnostic::error(
1353 attr.stable_ptr(db),
1354 "`#[path(..)]` is only allowed on module declarations.".to_string(),
1355 ));
1356 continue;
1357 };
1358 if matches!(item_module.body(db), MaybeModuleBody::Some(_)) {
1360 plugin_diagnostics.push(PluginDiagnostic::error(
1361 attr.stable_ptr(db),
1362 "`#[path(..)]` requires a file-based module: use `mod name;` with a semicolon."
1363 .to_string(),
1364 ));
1365 continue;
1366 }
1367
1368 let args = attr.arguments(db);
1369 if extract_path_arg(db, &args).is_none() {
1370 plugin_diagnostics.push(PluginDiagnostic::error(
1371 args.stable_ptr(db),
1372 "`#[path(..)]` expects exactly one string literal argument.".to_string(),
1373 ));
1374 }
1375 }
1376}
1377
1378fn extract_path_arg<'db>(
1380 db: &'db dyn Database,
1381 args: &ast::OptionArgListParenthesized<'db>,
1382) -> Option<ast::TerminalString<'db>> {
1383 match args {
1384 ast::OptionArgListParenthesized::Empty(_) => None,
1385 ast::OptionArgListParenthesized::ArgListParenthesized(args) => {
1386 let arg = args.arguments(db).elements(db).exactly_one().ok()?;
1387 if let ast::Expr::String(path) = try_extract_unnamed_arg(db, &arg)? {
1388 Some(path)
1389 } else {
1390 None
1391 }
1392 }
1393 }
1394}
1395
1396fn validate_attributes_element_list<'db, Item: QueryAttrs<'db> + TypedSyntaxNode<'db>>(
1399 db: &'db dyn Database,
1400 allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1401 extra_allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1402 items: impl Iterator<Item = Item>,
1403 plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1404) {
1405 for item in items {
1406 validate_attributes_flat(
1407 db,
1408 allowed_attributes,
1409 extra_allowed_attributes,
1410 &item,
1411 plugin_diagnostics,
1412 );
1413 }
1414}
1415
1416fn validate_attributes<'db>(
1419 db: &'db dyn Database,
1420 allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
1421 item_ast: &ast::ModuleItem<'db>,
1422 plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
1423) {
1424 let extra_allowed_attributes =
1425 collect_extra_allowed_attributes(db, item_ast, plugin_diagnostics);
1426 validate_attributes_flat(
1427 db,
1428 allowed_attributes,
1429 &extra_allowed_attributes,
1430 item_ast,
1431 plugin_diagnostics,
1432 );
1433
1434 match item_ast {
1435 ast::ModuleItem::Trait(item) => {
1436 if let ast::MaybeTraitBody::Some(body) = item.body(db) {
1437 validate_attributes_element_list(
1438 db,
1439 allowed_attributes,
1440 &extra_allowed_attributes,
1441 body.items(db).elements(db),
1442 plugin_diagnostics,
1443 );
1444 }
1445 }
1446 ast::ModuleItem::Impl(item) => {
1447 if let ast::MaybeImplBody::Some(body) = item.body(db) {
1448 validate_attributes_element_list(
1449 db,
1450 allowed_attributes,
1451 &extra_allowed_attributes,
1452 body.items(db).elements(db),
1453 plugin_diagnostics,
1454 );
1455 }
1456 }
1457 ast::ModuleItem::Struct(item) => {
1458 validate_attributes_element_list(
1459 db,
1460 allowed_attributes,
1461 &extra_allowed_attributes,
1462 item.members(db).elements(db),
1463 plugin_diagnostics,
1464 );
1465 }
1466 ast::ModuleItem::Enum(item) => {
1467 validate_attributes_element_list(
1468 db,
1469 allowed_attributes,
1470 &extra_allowed_attributes,
1471 item.variants(db).elements(db),
1472 plugin_diagnostics,
1473 );
1474 }
1475 _ => {}
1476 }
1477}
1478
1479pub fn get_all_path_leaves<'db>(
1481 db: &'db dyn Database,
1482 use_item: &ast::ItemUse<'db>,
1483) -> Vec<ast::UsePathLeaf<'db>> {
1484 let mut res = vec![];
1485 let mut stack = vec![use_item.use_path(db)];
1486 while let Some(use_path) = stack.pop() {
1487 match use_path {
1488 ast::UsePath::Leaf(use_path) => res.push(use_path),
1489 ast::UsePath::Single(use_path) => stack.push(use_path.use_path(db)),
1490 ast::UsePath::Multi(use_path) => {
1491 stack.extend(use_path.use_paths(db).elements(db).rev())
1492 }
1493 ast::UsePath::Star(_) => {}
1494 }
1495 }
1496 res
1497}
1498
1499pub fn get_all_path_stars<'db>(
1501 db: &'db dyn Database,
1502 use_item: &ast::ItemUse<'db>,
1503) -> Vec<ast::UsePathStar<'db>> {
1504 let mut res = vec![];
1505 let mut stack = vec![use_item.use_path(db)];
1506 while let Some(use_path) = stack.pop() {
1507 match use_path {
1508 ast::UsePath::Leaf(_) => {}
1509 ast::UsePath::Single(use_path) => stack.push(use_path.use_path(db)),
1510 ast::UsePath::Multi(use_path) => {
1511 stack.extend(use_path.use_paths(db).elements(db).rev())
1512 }
1513 ast::UsePath::Star(use_path) => res.push(use_path),
1514 }
1515 }
1516 res
1517}
1518
1519fn extract_allowed_features<'db>(
1525 db: &'db dyn Database,
1526 item: &impl QueryAttrs<'db>,
1527) -> OrderedHashSet<SmolStrId<'db>> {
1528 let mut allowed_features = OrderedHashSet::default();
1529 for attr in item.query_attr(db, FEATURE_ATTR) {
1530 for arg in attr.structurize(db).args {
1531 if let Some(ast::Expr::String(value)) = try_extract_unnamed_arg(db, &arg.arg) {
1532 allowed_features.insert(value.text(db));
1533 }
1534 }
1535 }
1536 allowed_features
1537}
1538
1539#[salsa::tracked(returns(ref))]
1545fn module_allowed_features<'db>(
1546 db: &'db dyn Database,
1547 _tracked: Tracked,
1548 module_id: ModuleId<'db>,
1549) -> OrderedHashSet<SmolStrId<'db>> {
1550 let (parent_module, mut allowed_features) = match module_id {
1551 ModuleId::CrateRoot(_) => return Default::default(),
1552 ModuleId::Submodule(id) => {
1553 (id.parent_module(db), extract_allowed_features(db, &id.stable_ptr(db).lookup(db)))
1554 }
1555 ModuleId::MacroCall { id, .. } => {
1556 (id.parent_module(db), extract_allowed_features(db, &id.stable_ptr(db).lookup(db)))
1557 }
1558 };
1559 allowed_features.extend(module_allowed_features(db, (), parent_module).iter().copied());
1560 allowed_features
1561}
1562
1563#[salsa::tracked(returns(ref))]
1564fn module_constants_ids_helper<'db>(
1565 db: &'db dyn Database,
1566 module_data: ModuleData<'db>,
1567) -> Vec<ConstantId<'db>> {
1568 module_data.constants(db).keys().copied().collect_vec()
1569}
1570
1571pub fn module_constants_ids<'db>(
1572 db: &'db dyn Database,
1573 module_id: ModuleId<'db>,
1574) -> Maybe<&'db [ConstantId<'db>]> {
1575 Ok(module_constants_ids_helper(db, module_id.module_data(db)?))
1576}
1577
1578#[salsa::tracked]
1579pub fn module_constant_by_id<'db>(
1580 db: &'db dyn Database,
1581 constant_id: ConstantId<'db>,
1582) -> Maybe<ast::ItemConstant<'db>> {
1583 let module_constants = constant_id.module_data(db)?.constants(db);
1584 module_constants.get(&constant_id).cloned().ok_or_else(skip_diagnostic)
1585}
1586
1587#[salsa::tracked(returns(ref))]
1588fn module_submodules_ids_helper<'db>(
1589 db: &'db dyn Database,
1590 module_data: ModuleData<'db>,
1591) -> Vec<SubmoduleId<'db>> {
1592 module_data.submodules(db).keys().copied().collect_vec()
1593}
1594
1595fn module_submodules_ids<'db>(
1596 db: &'db dyn Database,
1597 module_id: ModuleId<'db>,
1598) -> Maybe<&'db [SubmoduleId<'db>]> {
1599 Ok(module_submodules_ids_helper(db, module_id.module_data(db)?))
1600}
1601
1602#[salsa::tracked]
1603pub fn module_submodule_by_id<'db>(
1604 db: &'db dyn Database,
1605 submodule_id: SubmoduleId<'db>,
1606) -> Maybe<ast::ItemModule<'db>> {
1607 let module_submodules = submodule_id.module_data(db)?.submodules(db);
1608 module_submodules.get(&submodule_id).cloned().ok_or_else(skip_diagnostic)
1609}
1610
1611#[salsa::tracked(returns(ref))]
1612fn module_free_functions_ids_helper<'db>(
1613 db: &'db dyn Database,
1614 module_data: ModuleData<'db>,
1615) -> Vec<FreeFunctionId<'db>> {
1616 module_data.free_functions(db).keys().copied().collect_vec()
1617}
1618
1619pub fn module_free_functions_ids<'db>(
1620 db: &'db dyn Database,
1621 module_id: ModuleId<'db>,
1622) -> Maybe<&'db [FreeFunctionId<'db>]> {
1623 Ok(module_free_functions_ids_helper(db, module_id.module_data(db)?))
1624}
1625
1626#[salsa::tracked]
1627pub fn module_free_function_by_id<'db>(
1628 db: &'db dyn Database,
1629 free_function_id: FreeFunctionId<'db>,
1630) -> Maybe<ast::FunctionWithBody<'db>> {
1631 let module_free_functions = free_function_id.module_data(db)?.free_functions(db);
1632 module_free_functions.get(&free_function_id).cloned().ok_or_else(skip_diagnostic)
1633}
1634
1635#[salsa::tracked(returns(ref))]
1636fn module_uses_ids_helper<'db>(
1637 db: &'db dyn Database,
1638 module_data: ModuleData<'db>,
1639) -> Vec<UseId<'db>> {
1640 module_data.uses(db).keys().copied().collect_vec()
1641}
1642pub fn module_uses_ids<'db>(
1643 db: &'db dyn Database,
1644 module_id: ModuleId<'db>,
1645) -> Maybe<&'db [UseId<'db>]> {
1646 Ok(module_uses_ids_helper(db, module_id.module_data(db)?))
1647}
1648
1649#[salsa::tracked]
1650pub fn module_use_by_id<'db>(
1651 db: &'db dyn Database,
1652 use_id: UseId<'db>,
1653) -> Maybe<ast::UsePathLeaf<'db>> {
1654 let module_uses = use_id.module_data(db)?.uses(db);
1655 module_uses.get(&use_id).cloned().ok_or_else(skip_diagnostic)
1656}
1657
1658#[salsa::tracked]
1660pub fn module_global_use_by_id<'db>(
1661 db: &'db dyn Database,
1662 global_use_id: GlobalUseId<'db>,
1663) -> Maybe<ast::UsePathStar<'db>> {
1664 let module_global_uses = global_use_id.module_data(db)?.global_uses(db);
1665 module_global_uses.get(&global_use_id).cloned().ok_or_else(skip_diagnostic)
1666}
1667
1668#[salsa::tracked(returns(ref))]
1669fn module_structs_ids_helper<'db>(
1670 db: &'db dyn Database,
1671 module_data: ModuleData<'db>,
1672) -> Vec<StructId<'db>> {
1673 module_data.structs(db).keys().copied().collect_vec()
1674}
1675
1676pub fn module_structs_ids<'db>(
1677 db: &'db dyn Database,
1678 module_id: ModuleId<'db>,
1679) -> Maybe<&'db [StructId<'db>]> {
1680 Ok(module_structs_ids_helper(db, module_id.module_data(db)?))
1681}
1682
1683#[salsa::tracked]
1684pub fn module_struct_by_id<'db>(
1685 db: &'db dyn Database,
1686 struct_id: StructId<'db>,
1687) -> Maybe<ast::ItemStruct<'db>> {
1688 let module_structs = struct_id.module_data(db)?.structs(db);
1689 module_structs.get(&struct_id).cloned().ok_or_else(skip_diagnostic)
1690}
1691
1692#[salsa::tracked(returns(ref))]
1693fn module_enums_ids_helper<'db>(
1694 db: &'db dyn Database,
1695 module_data: ModuleData<'db>,
1696) -> Vec<EnumId<'db>> {
1697 module_data.enums(db).keys().copied().collect_vec()
1698}
1699
1700pub fn module_enums_ids<'db>(
1701 db: &'db dyn Database,
1702 module_id: ModuleId<'db>,
1703) -> Maybe<&'db [EnumId<'db>]> {
1704 Ok(module_enums_ids_helper(db, module_id.module_data(db)?))
1705}
1706
1707#[salsa::tracked]
1708pub fn module_enum_by_id<'db>(
1709 db: &'db dyn Database,
1710 enum_id: EnumId<'db>,
1711) -> Maybe<ast::ItemEnum<'db>> {
1712 let module_enums = enum_id.module_data(db)?.enums(db);
1713 module_enums.get(&enum_id).cloned().ok_or_else(skip_diagnostic)
1714}
1715
1716#[salsa::tracked(returns(ref))]
1717fn module_type_aliases_ids_helper<'db>(
1718 db: &'db dyn Database,
1719 module_data: ModuleData<'db>,
1720) -> Vec<ModuleTypeAliasId<'db>> {
1721 module_data.type_aliases(db).keys().copied().collect_vec()
1722}
1723
1724pub fn module_type_aliases_ids<'db>(
1725 db: &'db dyn Database,
1726 module_id: ModuleId<'db>,
1727) -> Maybe<&'db [ModuleTypeAliasId<'db>]> {
1728 Ok(module_type_aliases_ids_helper(db, module_id.module_data(db)?))
1729}
1730
1731#[salsa::tracked]
1732pub fn module_type_alias_by_id<'db>(
1733 db: &'db dyn Database,
1734 module_type_alias_id: ModuleTypeAliasId<'db>,
1735) -> Maybe<ast::ItemTypeAlias<'db>> {
1736 let module_type_aliases = module_type_alias_id.module_data(db)?.type_aliases(db);
1737 module_type_aliases.get(&module_type_alias_id).cloned().ok_or_else(skip_diagnostic)
1738}
1739
1740#[salsa::tracked(returns(ref))]
1741fn module_impl_aliases_ids_helper<'db>(
1742 db: &'db dyn Database,
1743 module_data: ModuleData<'db>,
1744) -> Vec<ImplAliasId<'db>> {
1745 module_data.impl_aliases(db).keys().copied().collect_vec()
1746}
1747
1748pub fn module_impl_aliases_ids<'db>(
1749 db: &'db dyn Database,
1750 module_id: ModuleId<'db>,
1751) -> Maybe<&'db [ImplAliasId<'db>]> {
1752 Ok(module_impl_aliases_ids_helper(db, module_id.module_data(db)?))
1753}
1754
1755#[salsa::tracked]
1756pub fn module_impl_alias_by_id<'db>(
1757 db: &'db dyn Database,
1758 impl_alias_id: ImplAliasId<'db>,
1759) -> Maybe<ast::ItemImplAlias<'db>> {
1760 let module_impl_aliases = impl_alias_id.module_data(db)?.impl_aliases(db);
1761 module_impl_aliases.get(&impl_alias_id).cloned().ok_or_else(skip_diagnostic)
1762}
1763
1764#[salsa::tracked(returns(ref))]
1765fn module_traits_ids_helper<'db>(
1766 db: &'db dyn Database,
1767 module_data: ModuleData<'db>,
1768) -> Vec<TraitId<'db>> {
1769 module_data.traits(db).keys().copied().collect_vec()
1770}
1771
1772pub fn module_traits_ids<'db>(
1773 db: &'db dyn Database,
1774 module_id: ModuleId<'db>,
1775) -> Maybe<&'db [TraitId<'db>]> {
1776 Ok(module_traits_ids_helper(db, module_id.module_data(db)?))
1777}
1778
1779#[salsa::tracked]
1780pub fn module_trait_by_id<'db>(
1781 db: &'db dyn Database,
1782 trait_id: TraitId<'db>,
1783) -> Maybe<ast::ItemTrait<'db>> {
1784 let module_traits = trait_id.module_data(db)?.traits(db);
1785 module_traits.get(&trait_id).cloned().ok_or_else(skip_diagnostic)
1786}
1787
1788#[salsa::tracked(returns(ref))]
1789fn module_impls_ids_helper<'db>(
1790 db: &'db dyn Database,
1791 module_data: ModuleData<'db>,
1792) -> Vec<ImplDefId<'db>> {
1793 module_data.impls(db).keys().copied().collect_vec()
1794}
1795
1796pub fn module_impls_ids<'db>(
1797 db: &'db dyn Database,
1798 module_id: ModuleId<'db>,
1799) -> Maybe<&'db [ImplDefId<'db>]> {
1800 Ok(module_impls_ids_helper(db, module_id.module_data(db)?))
1801}
1802
1803#[salsa::tracked]
1804pub fn module_impl_by_id<'db>(
1805 db: &'db dyn Database,
1806 impl_def_id: ImplDefId<'db>,
1807) -> Maybe<ast::ItemImpl<'db>> {
1808 let module_impls = impl_def_id.module_data(db)?.impls(db);
1809 module_impls.get(&impl_def_id).cloned().ok_or_else(skip_diagnostic)
1810}
1811
1812#[salsa::tracked(returns(ref))]
1813fn module_extern_types_ids_helper<'db>(
1814 db: &'db dyn Database,
1815 module_data: ModuleData<'db>,
1816) -> Vec<ExternTypeId<'db>> {
1817 module_data.extern_types(db).keys().copied().collect_vec()
1818}
1819pub fn module_extern_types_ids<'db>(
1820 db: &'db dyn Database,
1821 module_id: ModuleId<'db>,
1822) -> Maybe<&'db [ExternTypeId<'db>]> {
1823 Ok(module_extern_types_ids_helper(db, module_id.module_data(db)?))
1824}
1825
1826#[salsa::tracked]
1827pub fn module_extern_type_by_id<'db>(
1828 db: &'db dyn Database,
1829 extern_type_id: ExternTypeId<'db>,
1830) -> Maybe<ast::ItemExternType<'db>> {
1831 let module_extern_types = extern_type_id.module_data(db)?.extern_types(db);
1832 module_extern_types.get(&extern_type_id).cloned().ok_or_else(skip_diagnostic)
1833}
1834
1835#[salsa::tracked(returns(ref))]
1836fn module_macro_declarations_ids_helper<'db>(
1837 db: &'db dyn Database,
1838 module_data: ModuleData<'db>,
1839) -> Vec<MacroDeclarationId<'db>> {
1840 module_data.macro_declarations(db).keys().copied().collect_vec()
1841}
1842
1843pub fn module_macro_declarations_ids<'db>(
1845 db: &'db dyn Database,
1846 module_id: ModuleId<'db>,
1847) -> Maybe<&'db [MacroDeclarationId<'db>]> {
1848 Ok(module_macro_declarations_ids_helper(db, module_id.module_data(db)?))
1849}
1850
1851#[salsa::tracked]
1853pub fn module_macro_declaration_by_id<'db>(
1854 db: &'db dyn Database,
1855 macro_declaration_id: MacroDeclarationId<'db>,
1856) -> Maybe<ast::ItemMacroDeclaration<'db>> {
1857 let module_macro_declarations = macro_declaration_id.module_data(db)?.macro_declarations(db);
1858 module_macro_declarations.get(¯o_declaration_id).cloned().ok_or_else(skip_diagnostic)
1859}
1860
1861#[salsa::tracked(returns(ref))]
1862fn module_macro_calls_ids_helper<'db>(
1863 db: &'db dyn Database,
1864 module_data: ModuleData<'db>,
1865) -> Vec<MacroCallId<'db>> {
1866 module_data.macro_calls(db).keys().copied().collect_vec()
1867}
1868
1869pub fn module_macro_calls_ids<'db>(
1870 db: &'db dyn Database,
1871 module_id: ModuleId<'db>,
1872) -> Maybe<&'db [MacroCallId<'db>]> {
1873 Ok(module_macro_calls_ids_helper(db, module_id.module_data(db)?))
1874}
1875
1876#[salsa::tracked]
1877fn module_macro_call_by_id<'db>(
1878 db: &'db dyn Database,
1879 macro_call_id: MacroCallId<'db>,
1880) -> Maybe<ast::ItemInlineMacro<'db>> {
1881 let module_macro_calls = macro_call_id.module_data(db)?.macro_calls(db);
1882 module_macro_calls.get(¯o_call_id).cloned().ok_or_else(skip_diagnostic)
1883}
1884
1885#[salsa::tracked(returns(ref))]
1886fn module_extern_functions_ids_helper<'db>(
1887 db: &'db dyn Database,
1888 module_data: ModuleData<'db>,
1889) -> Vec<ExternFunctionId<'db>> {
1890 module_data.extern_functions(db).keys().copied().collect_vec()
1891}
1892
1893pub fn module_extern_functions_ids<'db>(
1894 db: &'db dyn Database,
1895 module_id: ModuleId<'db>,
1896) -> Maybe<&'db [ExternFunctionId<'db>]> {
1897 Ok(module_extern_functions_ids_helper(db, module_id.module_data(db)?))
1898}
1899
1900#[salsa::tracked]
1901pub fn module_extern_function_by_id<'db>(
1902 db: &'db dyn Database,
1903 extern_function_id: ExternFunctionId<'db>,
1904) -> Maybe<ast::ItemExternFunction<'db>> {
1905 let module_extern_functions = extern_function_id.module_data(db)?.extern_functions(db);
1906 module_extern_functions.get(&extern_function_id).cloned().ok_or_else(skip_diagnostic)
1907}
1908
1909#[salsa::tracked(returns(ref))]
1910fn module_ancestors_helper<'db>(
1911 db: &'db dyn Database,
1912 _tracked: Tracked,
1913 module_id: ModuleId<'db>,
1914) -> OrderedHashSet<ModuleId<'db>> {
1915 let mut current = module_id;
1916 let mut ancestors = OrderedHashSet::default();
1917 loop {
1918 match current {
1919 ModuleId::CrateRoot(_) => {
1920 ancestors.insert(current);
1921 return ancestors;
1922 }
1923 ModuleId::Submodule(submodule_id) => {
1924 ancestors.insert(current);
1925 current = submodule_id.parent_module(db);
1926 }
1927 ModuleId::MacroCall { id, .. } => {
1928 current = id.parent_module(db);
1929 }
1930 }
1931 }
1932}
1933
1934#[salsa::tracked]
1935fn module_perceived_module_helper<'db>(
1936 db: &'db dyn Database,
1937 _tracked: Tracked,
1938 mut module_id: ModuleId<'db>,
1939) -> ModuleId<'db> {
1940 while let ModuleId::MacroCall { id, .. } = module_id {
1941 module_id = id.parent_module(db);
1942 }
1943 module_id
1944}
1945
1946fn module_item_name_stable_ptr<'db>(
1947 db: &'db dyn Database,
1948 module_id: ModuleId<'db>,
1949 item_id: ModuleItemId<'db>,
1950) -> Maybe<SyntaxStablePtrId<'db>> {
1951 let data = module_id.module_data(db)?;
1952 Ok(match &item_id {
1953 ModuleItemId::Constant(id) => data.constants(db)[id].name(db).stable_ptr(db).untyped(),
1954 ModuleItemId::Submodule(id) => data.submodules(db)[id].name(db).stable_ptr(db).untyped(),
1955 ModuleItemId::Use(id) => data.uses(db)[id].name_stable_ptr(db),
1956 ModuleItemId::FreeFunction(id) => {
1957 data.free_functions(db)[id].declaration(db).name(db).stable_ptr(db).untyped()
1958 }
1959 ModuleItemId::Struct(id) => data.structs(db)[id].name(db).stable_ptr(db).untyped(),
1960 ModuleItemId::Enum(id) => data.enums(db)[id].name(db).stable_ptr(db).untyped(),
1961 ModuleItemId::TypeAlias(id) => data.type_aliases(db)[id].name(db).stable_ptr(db).untyped(),
1962 ModuleItemId::ImplAlias(id) => data.impl_aliases(db)[id].name(db).stable_ptr(db).untyped(),
1963 ModuleItemId::Trait(id) => data.traits(db)[id].name(db).stable_ptr(db).untyped(),
1964 ModuleItemId::Impl(id) => data.impls(db)[id].name(db).stable_ptr(db).untyped(),
1965 ModuleItemId::ExternType(id) => data.extern_types(db)[id].name(db).stable_ptr(db).untyped(),
1966 ModuleItemId::ExternFunction(id) => {
1967 data.extern_functions(db)[id].declaration(db).name(db).stable_ptr(db).untyped()
1968 }
1969 ModuleItemId::MacroDeclaration(id) => {
1970 data.macro_declarations(db)[id].name(db).stable_ptr(db).untyped()
1971 }
1972 })
1973}
1974
1975pub trait DefsGroupEx: DefsGroup {
1976 fn set_override_crate_macro_plugins<'db>(
1980 &'db mut self,
1981 crate_id: CrateId<'db>,
1982 plugins: Arc<Vec<MacroPluginId<'db>>>,
1983 ) {
1984 let crate_input = self.crate_input(crate_id);
1985 let mut overrides = self.macro_plugin_overrides_input().clone();
1986 let plugins = plugins.iter().map(|plugin| plugin.long(self).clone()).collect();
1987 overrides.insert(crate_input.clone(), plugins);
1988 defs_group_input(self.as_dyn_database())
1989 .set_macro_plugin_overrides(self)
1990 .to(Some(overrides));
1991 }
1992
1993 fn set_override_crate_inline_macro_plugins<'db>(
1997 &'db mut self,
1998 crate_id: CrateId<'db>,
1999 plugins: Arc<OrderedHashMap<String, InlineMacroExprPluginId<'db>>>,
2000 ) {
2001 let crate_input = self.crate_input(crate_id);
2002 let mut overrides = self.inline_macro_plugin_overrides_input().clone();
2003 let plugins = Arc::new(
2004 plugins
2005 .iter()
2006 .map(|(name, plugin)| (name.clone(), plugin.long(self).clone()))
2007 .collect(),
2008 );
2009 overrides.insert(crate_input.clone(), plugins);
2010 defs_group_input(self.as_dyn_database())
2011 .set_inline_macro_plugin_overrides(self)
2012 .to(Some(overrides));
2013 }
2014}
2015
2016impl<T: DefsGroup + ?Sized> DefsGroupEx for T {}