use std::collections::VecDeque;
use std::sync::Arc;
use cairo_lang_diagnostics::{
DiagnosticNote, Maybe, MaybeAsRef, PluginFileDiagnosticNotes, ToMaybe, skip_diagnostic,
};
use cairo_lang_filesystem::db::{ExtAsVirtual, FilesGroup, files_group_input};
use cairo_lang_filesystem::ids::{
CrateId, CrateInput, Directory, FileId, FileKind, FileLongId, SmolStrId, Tracked, VirtualFile,
};
use cairo_lang_parser::db::ParserGroup;
use cairo_lang_syntax::attribute::consts::{
ALLOW_ATTR, ALLOW_ATTR_ATTR, DEPRECATED_ATTR, FEATURE_ATTR, FMT_SKIP_ATTR,
IMPLICIT_PRECEDENCE_ATTR, INLINE_ATTR, INTERNAL_ATTR, MUST_USE_ATTR, PATH_ATTR, PHANTOM_ATTR,
STARKNET_INTERFACE_ATTR, UNSTABLE_ATTR,
};
use cairo_lang_syntax::attribute::structured::AttributeStructurize;
use cairo_lang_syntax::node::ast::MaybeModuleBody;
use cairo_lang_syntax::node::helpers::QueryAttrs;
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
use cairo_lang_utils::Intern;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
use itertools::{Itertools, chain};
use salsa::{Database, Setter};
use crate::cache::{DefCacheLoadingData, load_cached_crate_modules};
use crate::ids::*;
use crate::plugin::{DynGeneratedFileAuxData, MacroPlugin, MacroPluginMetadata, PluginDiagnostic};
use crate::plugin_utils::try_extract_unnamed_arg;
#[salsa::input]
pub struct DefsGroupInput {
#[returns(ref)]
pub default_macro_plugins: Option<Vec<MacroPluginLongId>>,
#[returns(ref)]
pub macro_plugin_overrides: Option<OrderedHashMap<CrateInput, Arc<[MacroPluginLongId]>>>,
#[returns(ref)]
pub default_inline_macro_plugins: Option<OrderedHashMap<String, InlineMacroExprPluginLongId>>,
#[returns(ref)]
pub inline_macro_plugin_overrides: Option<
OrderedHashMap<CrateInput, Arc<OrderedHashMap<String, InlineMacroExprPluginLongId>>>,
>,
}
#[salsa::tracked(returns(ref))]
pub fn defs_group_input(db: &dyn Database) -> DefsGroupInput {
DefsGroupInput::new(db, None, None, None, None)
}
pub trait DefsGroup: Database {
fn default_macro_plugins_input(&self) -> &[MacroPluginLongId] {
defs_group_input(self.as_dyn_database()).default_macro_plugins(self).as_ref().unwrap()
}
fn macro_plugin_overrides_input(
&self,
) -> &OrderedHashMap<CrateInput, Arc<[MacroPluginLongId]>> {
defs_group_input(self.as_dyn_database()).macro_plugin_overrides(self).as_ref().unwrap()
}
fn inline_macro_plugin_overrides_input(
&self,
) -> &OrderedHashMap<CrateInput, Arc<OrderedHashMap<String, InlineMacroExprPluginLongId>>> {
defs_group_input(self.as_dyn_database())
.inline_macro_plugin_overrides(self)
.as_ref()
.unwrap()
}
fn default_inline_macro_plugins_input(
&self,
) -> &OrderedHashMap<String, InlineMacroExprPluginLongId> {
defs_group_input(self.as_dyn_database())
.default_inline_macro_plugins(self)
.as_ref()
.unwrap()
}
fn default_macro_plugins<'db>(&'db self) -> &'db [MacroPluginId<'db>] {
default_macro_plugins(self.as_dyn_database())
}
fn macro_plugin_overrides<'db>(
&'db self,
) -> &'db OrderedHashMap<CrateId<'db>, Vec<MacroPluginId<'db>>> {
macro_plugin_overrides(self.as_dyn_database())
}
fn crate_macro_plugins<'db>(&'db self, crate_id: CrateId<'db>) -> &'db [MacroPluginId<'db>] {
crate_macro_plugins(self.as_dyn_database(), crate_id)
}
fn default_inline_macro_plugins<'db>(
&'db self,
) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
default_inline_macro_plugins(self.as_dyn_database())
}
fn inline_macro_plugin_overrides<'db>(
&'db self,
) -> &'db OrderedHashMap<CrateId<'db>, OrderedHashMap<String, InlineMacroExprPluginId<'db>>>
{
inline_macro_plugin_overrides(self.as_dyn_database())
}
fn crate_inline_macro_plugins<'db>(
&'db self,
crate_id: CrateId<'db>,
) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
crate_inline_macro_plugins(self.as_dyn_database(), crate_id)
}
fn allowed_attributes<'db>(
&'db self,
crate_id: CrateId<'db>,
) -> &'db OrderedHashSet<SmolStrId<'db>> {
allowed_attributes(self.as_dyn_database(), crate_id)
}
fn allowed_statement_attributes<'db>(&'db self) -> &'db OrderedHashSet<SmolStrId<'db>> {
allowed_statement_attributes(self.as_dyn_database())
}
fn declared_derives<'db>(
&'db self,
crate_id: CrateId<'db>,
) -> &'db OrderedHashSet<SmolStrId<'db>> {
declared_derives(self.as_dyn_database(), crate_id)
}
fn declared_phantom_type_attributes<'db>(
&'db self,
crate_id: CrateId<'db>,
) -> &'db OrderedHashSet<SmolStrId<'db>> {
declared_phantom_type_attributes(self.as_dyn_database(), crate_id)
}
fn is_submodule_inline<'db>(&self, submodule_id: SubmoduleId<'db>) -> bool {
is_submodule_inline(self.as_dyn_database(), submodule_id)
}
fn module_main_file<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<FileId<'db>> {
module_main_file(self.as_dyn_database(), module_id)
}
fn module_files<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [FileId<'db>]> {
module_files(self.as_dyn_database(), module_id)
}
fn module_dir<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db Directory<'db>> {
module_dir(self.as_dyn_database(), module_id)
}
fn crate_modules<'db>(&'db self, crate_id: CrateId<'db>) -> &'db [ModuleId<'db>] {
crate_modules(self.as_dyn_database(), crate_id)
}
fn file_modules<'db>(&'db self, file_id: FileId<'db>) -> Maybe<&'db Vec<ModuleId<'db>>> {
file_modules(self.as_dyn_database(), file_id).maybe_as_ref()
}
fn cached_crate_modules<'db>(
&'db self,
crate_id: CrateId<'db>,
) -> Option<ModuleDataCacheAndLoadingData<'db>> {
cached_crate_modules(self.as_dyn_database(), crate_id)
}
fn module_submodules_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [SubmoduleId<'db>]> {
module_submodules_ids(self.as_dyn_database(), module_id)
}
fn module_constants_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ConstantId<'db>]> {
module_constants_ids(self.as_dyn_database(), module_id)
}
fn module_constant_by_id<'db>(
&'db self,
constant_data: ConstantId<'db>,
) -> Maybe<ast::ItemConstant<'db>> {
module_constant_by_id(self.as_dyn_database(), constant_data)
}
fn module_free_functions_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [FreeFunctionId<'db>]> {
module_free_functions_ids(self.as_dyn_database(), module_id)
}
fn module_free_function_by_id<'db>(
&'db self,
free_function_id: FreeFunctionId<'db>,
) -> Maybe<ast::FunctionWithBody<'db>> {
module_free_function_by_id(self.as_dyn_database(), free_function_id)
}
fn module_item_name_stable_ptr<'db>(
&'db self,
module_id: ModuleId<'db>,
item_id: ModuleItemId<'db>,
) -> Maybe<SyntaxStablePtrId<'db>> {
module_item_name_stable_ptr(self.as_dyn_database(), module_id, item_id)
}
fn module_uses_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [UseId<'db>]> {
module_uses_ids(self.as_dyn_database(), module_id)
}
fn module_use_by_id<'db>(&'db self, use_id: UseId<'db>) -> Maybe<ast::UsePathLeaf<'db>> {
module_use_by_id(self.as_dyn_database(), use_id)
}
fn module_global_use_by_id<'db>(
&'db self,
global_use_id: GlobalUseId<'db>,
) -> Maybe<ast::UsePathStar<'db>> {
module_global_use_by_id(self.as_dyn_database(), global_use_id)
}
fn module_structs_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [StructId<'db>]> {
module_structs_ids(self.as_dyn_database(), module_id)
}
fn module_struct_by_id<'db>(
&'db self,
struct_id: StructId<'db>,
) -> Maybe<ast::ItemStruct<'db>> {
module_struct_by_id(self.as_dyn_database(), struct_id)
}
fn module_enums_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [EnumId<'db>]> {
module_enums_ids(self.as_dyn_database(), module_id)
}
fn module_enum_by_id<'db>(&'db self, enum_id: EnumId<'db>) -> Maybe<ast::ItemEnum<'db>> {
module_enum_by_id(self.as_dyn_database(), enum_id)
}
fn module_type_aliases_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ModuleTypeAliasId<'db>]> {
module_type_aliases_ids(self.as_dyn_database(), module_id)
}
fn module_type_alias_by_id<'db>(
&'db self,
module_type_alias_id: ModuleTypeAliasId<'db>,
) -> Maybe<ast::ItemTypeAlias<'db>> {
module_type_alias_by_id(self.as_dyn_database(), module_type_alias_id)
}
fn module_impl_aliases_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ImplAliasId<'db>]> {
module_impl_aliases_ids(self.as_dyn_database(), module_id)
}
fn module_impl_alias_by_id<'db>(
&'db self,
impl_alias_id: ImplAliasId<'db>,
) -> Maybe<ast::ItemImplAlias<'db>> {
module_impl_alias_by_id(self.as_dyn_database(), impl_alias_id)
}
fn module_traits_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [TraitId<'db>]> {
module_traits_ids(self.as_dyn_database(), module_id)
}
fn module_trait_by_id<'db>(&'db self, trait_id: TraitId<'db>) -> Maybe<ast::ItemTrait<'db>> {
module_trait_by_id(self.as_dyn_database(), trait_id)
}
fn module_impls_ids<'db>(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [ImplDefId<'db>]> {
module_impls_ids(self.as_dyn_database(), module_id)
}
fn module_impl_by_id<'db>(&'db self, impl_id: ImplDefId<'db>) -> Maybe<ast::ItemImpl<'db>> {
module_impl_by_id(self.as_dyn_database(), impl_id)
}
fn module_extern_types_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ExternTypeId<'db>]> {
module_extern_types_ids(self.as_dyn_database(), module_id)
}
fn module_extern_type_by_id<'db>(
&'db self,
extern_type_id: ExternTypeId<'db>,
) -> Maybe<ast::ItemExternType<'db>> {
module_extern_type_by_id(self.as_dyn_database(), extern_type_id)
}
fn module_extern_functions_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ExternFunctionId<'db>]> {
module_extern_functions_ids(self.as_dyn_database(), module_id)
}
fn module_extern_function_by_id<'db>(
&'db self,
extern_function_id: ExternFunctionId<'db>,
) -> Maybe<ast::ItemExternFunction<'db>> {
module_extern_function_by_id(self.as_dyn_database(), extern_function_id)
}
fn module_macro_declarations_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [MacroDeclarationId<'db>]> {
module_macro_declarations_ids(self.as_dyn_database(), module_id)
}
fn module_macro_declaration_by_id<'db>(
&'db self,
macro_declaration_id: MacroDeclarationId<'db>,
) -> Maybe<ast::ItemMacroDeclaration<'db>> {
module_macro_declaration_by_id(self.as_dyn_database(), macro_declaration_id)
}
fn module_macro_calls_ids<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> Maybe<&'db [MacroCallId<'db>]> {
module_macro_calls_ids(self.as_dyn_database(), module_id)
}
fn module_macro_call_by_id<'db>(
&'db self,
macro_call_id: MacroCallId<'db>,
) -> Maybe<ast::ItemInlineMacro<'db>> {
module_macro_call_by_id(self.as_dyn_database(), macro_call_id)
}
fn module_ancestors<'db>(
&'db self,
module_id: ModuleId<'db>,
) -> &'db OrderedHashSet<ModuleId<'db>> {
module_ancestors_helper(self.as_dyn_database(), (), module_id)
}
fn module_perceived_module<'db>(&'db self, module_id: ModuleId<'db>) -> ModuleId<'db> {
module_perceived_module_helper(self.as_dyn_database(), (), module_id)
}
}
impl<T: Database + ?Sized> DefsGroup for T {}
pub fn init_defs_group(db: &mut dyn Database) {
defs_group_input(db).set_macro_plugin_overrides(db).to(Some(OrderedHashMap::default()));
defs_group_input(db).set_inline_macro_plugin_overrides(db).to(Some(OrderedHashMap::default()));
}
#[salsa::tracked(returns(ref))]
fn default_macro_plugins_helper<'db>(db: &'db dyn Database) -> Vec<MacroPluginId<'db>> {
db.default_macro_plugins_input()
.iter()
.map(|plugin| MacroPluginId::new(db, plugin.clone()))
.collect()
}
pub fn default_macro_plugins<'db>(db: &'db dyn Database) -> &'db [MacroPluginId<'db>] {
default_macro_plugins_helper(db)
}
#[salsa::tracked(returns(ref))]
pub fn macro_plugin_overrides<'db>(
db: &'db dyn Database,
) -> OrderedHashMap<CrateId<'db>, Vec<MacroPluginId<'db>>> {
let inp = db.macro_plugin_overrides_input();
inp.iter()
.map(|(crate_id, plugins)| {
(
crate_id.clone().into_crate_long_id(db).intern(db),
plugins.iter().map(|plugin| plugin.clone().intern(db)).collect(),
)
})
.collect()
}
#[salsa::tracked(returns(ref))]
pub fn inline_macro_plugin_overrides<'db>(
db: &'db dyn Database,
) -> OrderedHashMap<CrateId<'db>, OrderedHashMap<String, InlineMacroExprPluginId<'db>>> {
let inp = db.inline_macro_plugin_overrides_input();
inp.iter()
.map(|(crate_id, plugins)| {
(
crate_id.clone().into_crate_long_id(db).intern(db),
plugins
.iter()
.map(|(name, plugin)| (name.clone(), plugin.clone().intern(db)))
.collect(),
)
})
.collect()
}
#[salsa::tracked(returns(ref))]
pub fn default_inline_macro_plugins<'db>(
db: &'db dyn Database,
) -> OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
let inp = db.default_inline_macro_plugins_input();
inp.iter().map(|(name, plugin)| (name.clone(), plugin.clone().intern(db))).collect()
}
fn crate_macro_plugins<'db>(
db: &'db dyn Database,
crate_id: CrateId<'db>,
) -> &'db [MacroPluginId<'db>] {
macro_plugin_overrides(db).get(&crate_id).unwrap_or_else(|| default_macro_plugins_helper(db))
}
fn crate_inline_macro_plugins<'db>(
db: &'db dyn Database,
crate_id: CrateId<'db>,
) -> &'db OrderedHashMap<String, InlineMacroExprPluginId<'db>> {
db.inline_macro_plugin_overrides()
.get(&crate_id)
.unwrap_or_else(|| db.default_inline_macro_plugins())
}
#[salsa::tracked(returns(ref))]
fn allowed_attributes<'db>(
db: &'db dyn Database,
crate_id: CrateId<'db>,
) -> OrderedHashSet<SmolStrId<'db>> {
let base_attrs = [
INLINE_ATTR,
MUST_USE_ATTR,
UNSTABLE_ATTR,
DEPRECATED_ATTR,
INTERNAL_ATTR,
ALLOW_ATTR,
ALLOW_ATTR_ATTR,
FEATURE_ATTR,
PHANTOM_ATTR,
IMPLICIT_PRECEDENCE_ATTR,
FMT_SKIP_ATTR,
PATH_ATTR,
STARKNET_INTERFACE_ATTR,
];
let crate_plugins = db.crate_macro_plugins(crate_id);
OrderedHashSet::from_iter(chain!(
base_attrs.map(|attr| SmolStrId::from(db, attr)),
crate_plugins.iter().flat_map(|plugin| plugin.long(db).declared_attributes(db))
))
}
#[salsa::tracked(returns(ref))]
fn allowed_statement_attributes<'db>(db: &'db dyn Database) -> OrderedHashSet<SmolStrId<'db>> {
let all_attributes = [FMT_SKIP_ATTR, ALLOW_ATTR, FEATURE_ATTR];
OrderedHashSet::from_iter(all_attributes.map(|attr| SmolStrId::from(db, attr)))
}
#[salsa::tracked(returns(ref))]
fn declared_derives<'db>(
db: &'db dyn Database,
crate_id: CrateId<'db>,
) -> OrderedHashSet<SmolStrId<'db>> {
OrderedHashSet::from_iter(
db.crate_macro_plugins(crate_id)
.iter()
.flat_map(|plugin| plugin.long(db).declared_derives(db)),
)
}
#[salsa::tracked(returns(ref))]
fn declared_phantom_type_attributes<'db>(
db: &'db dyn Database,
crate_id: CrateId<'db>,
) -> OrderedHashSet<SmolStrId<'db>> {
let crate_plugins = db.crate_macro_plugins(crate_id);
OrderedHashSet::from_iter(chain!(
[SmolStrId::from(db, PHANTOM_ATTR)],
crate_plugins.iter().flat_map(|plugin| plugin.long(db).phantom_type_attributes(db))
))
}
#[salsa::tracked]
fn is_submodule_inline<'db>(db: &'db dyn Database, submodule_id: SubmoduleId<'db>) -> bool {
match submodule_id.stable_ptr(db).lookup(db).body(db) {
MaybeModuleBody::Some(_) => true,
MaybeModuleBody::None(_) => false,
}
}
#[salsa::tracked]
fn module_main_file_helper<'db>(
db: &'db dyn Database,
_tracked: Tracked,
module_id: ModuleId<'db>,
) -> Maybe<FileId<'db>> {
Ok(match module_id {
ModuleId::CrateRoot(crate_id) => {
db.crate_config(crate_id).to_maybe()?.root.file(db, "lib.cairo")
}
ModuleId::Submodule(submodule_id) => {
let parent = submodule_id.parent_module(db);
if db.is_submodule_inline(submodule_id) {
submodule_id.stable_ptr(db).untyped().file_id(db)
} else if let Some(path_override) = submodule_path_override(db, submodule_id) {
path_override
} else {
db.module_dir(parent)?
.file(db, &format!("{}.cairo", submodule_id.name(db).long(db)))
}
}
ModuleId::MacroCall { generated_file_id, .. } => generated_file_id,
})
}
fn module_main_file<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<FileId<'db>> {
module_main_file_helper(db, (), module_id)
}
fn module_files<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<&'db [FileId<'db>]> {
Ok(module_id.module_data(db)?.files(db))
}
#[salsa::tracked(returns(ref))]
fn module_dir_helper<'db>(
db: &'db dyn Database,
_tracked: Tracked,
module_id: ModuleId<'db>,
) -> Maybe<Directory<'db>> {
match module_id {
ModuleId::CrateRoot(crate_id) => {
db.crate_config(crate_id).to_maybe().map(|config| config.root.clone())
}
ModuleId::Submodule(submodule_id) => {
let parent = submodule_id.parent_module(db);
let name = submodule_id.name(db);
Ok(db.module_dir(parent)?.subdir(name.long(db)))
}
ModuleId::MacroCall { id, .. } => db.module_dir(id.parent_module(db)).cloned(),
}
}
fn submodule_path_override<'db>(
db: &'db dyn Database,
submodule_id: SubmoduleId<'db>,
) -> Option<FileId<'db>> {
let parent = submodule_id.parent_module(db);
let parent_data = parent.module_data(db).ok()?;
let item_module_ast = parent_data.submodules(db).get(&submodule_id)?.clone();
let attr_ast = item_module_ast.find_attr(db, PATH_ATTR)?;
let terminal = extract_path_arg(db, &attr_ast.arguments(db))?;
let path = terminal.string_value(db)?;
fn find_base<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> ModuleId<'db> {
match module_id {
ModuleId::CrateRoot(crate_id) => ModuleId::CrateRoot(crate_id),
ModuleId::Submodule(submodule_id) => submodule_id.parent_module(db),
ModuleId::MacroCall { id, .. } => find_base(db, id.parent_module(db)),
}
}
let base = find_base(db, parent);
Some(db.module_dir(base).ok()?.file(db, &path))
}
fn module_dir<'db>(db: &'db dyn Database, module_id: ModuleId<'db>) -> Maybe<&'db Directory<'db>> {
module_dir_helper(db, (), module_id).maybe_as_ref()
}
fn collect_modules_under<'db>(
db: &'db dyn Database,
modules: &mut Vec<ModuleId<'db>>,
module_id: ModuleId<'db>,
) {
modules.push(module_id);
if let Ok(submodule_ids) = db.module_submodules_ids(module_id) {
for submodule_module_id in submodule_ids.iter().copied() {
collect_modules_under(db, modules, ModuleId::Submodule(submodule_module_id));
}
}
}
#[salsa::tracked(returns(ref))]
fn crate_modules<'db>(db: &'db dyn Database, crate_id: CrateId<'db>) -> Vec<ModuleId<'db>> {
let mut modules = Vec::new();
collect_modules_under(db, &mut modules, ModuleId::CrateRoot(crate_id));
modules
}
#[salsa::tracked(returns(ref))]
fn file_to_module_mapping<'db>(
db: &'db dyn Database,
) -> OrderedHashMap<FileId<'db>, Vec<ModuleId<'db>>> {
let mut mapping = OrderedHashMap::<FileId<'db>, Vec<ModuleId<'db>>>::default();
for crate_id in db.crates() {
for module_id in db.crate_modules(*crate_id).iter().copied() {
if let Ok(files) = db.module_files(module_id) {
for file_id in files.iter().copied() {
match mapping.get_mut(&file_id) {
Some(file_modules) => {
file_modules.push(module_id);
}
None => {
mapping.insert(file_id, vec![module_id]);
}
}
}
}
}
}
mapping
}
#[salsa::tracked(returns(ref))]
fn file_modules<'db>(db: &'db dyn Database, file_id: FileId<'db>) -> Maybe<Vec<ModuleId<'db>>> {
file_to_module_mapping(db).get(&file_id).cloned().to_maybe()
}
#[salsa::tracked]
pub struct ModuleData<'db> {
#[returns(ref)]
pub items: Vec<ModuleItemId<'db>>,
#[returns(ref)]
types_data: ModuleTypesData<'db>,
#[returns(ref)]
named_items_data: ModuleNamedItemsData<'db>,
#[returns(ref)]
unnamed_items_data: ModuleUnnamedItemsData<'db>,
#[returns(ref)]
files_data: ModuleFilesData<'db>,
}
#[salsa::tracked]
pub struct ModuleTypesData<'db> {
#[returns(ref)]
structs: OrderedHashMap<StructId<'db>, ast::ItemStruct<'db>>,
#[returns(ref)]
enums: OrderedHashMap<EnumId<'db>, ast::ItemEnum<'db>>,
#[returns(ref)]
type_aliases: OrderedHashMap<ModuleTypeAliasId<'db>, ast::ItemTypeAlias<'db>>,
#[returns(ref)]
extern_types: OrderedHashMap<ExternTypeId<'db>, ast::ItemExternType<'db>>,
}
#[salsa::tracked]
pub struct ModuleNamedItemsData<'db> {
#[returns(ref)]
constants: OrderedHashMap<ConstantId<'db>, ast::ItemConstant<'db>>,
#[returns(ref)]
submodules: OrderedHashMap<SubmoduleId<'db>, ast::ItemModule<'db>>,
#[returns(ref)]
uses: OrderedHashMap<UseId<'db>, ast::UsePathLeaf<'db>>,
#[returns(ref)]
free_functions: OrderedHashMap<FreeFunctionId<'db>, ast::FunctionWithBody<'db>>,
#[returns(ref)]
impl_aliases: OrderedHashMap<ImplAliasId<'db>, ast::ItemImplAlias<'db>>,
#[returns(ref)]
traits: OrderedHashMap<TraitId<'db>, ast::ItemTrait<'db>>,
#[returns(ref)]
impls: OrderedHashMap<ImplDefId<'db>, ast::ItemImpl<'db>>,
#[returns(ref)]
extern_functions: OrderedHashMap<ExternFunctionId<'db>, ast::ItemExternFunction<'db>>,
#[returns(ref)]
macro_declarations: OrderedHashMap<MacroDeclarationId<'db>, ast::ItemMacroDeclaration<'db>>,
}
#[salsa::tracked]
pub struct ModuleUnnamedItemsData<'db> {
#[returns(ref)]
global_uses: OrderedHashMap<GlobalUseId<'db>, ast::UsePathStar<'db>>,
#[returns(ref)]
macro_calls: OrderedHashMap<MacroCallId<'db>, ast::ItemInlineMacro<'db>>,
}
#[salsa::tracked]
pub struct ModuleFilesData<'db> {
#[returns(ref)]
files: Vec<FileId<'db>>,
#[returns(ref)]
generated_file_aux_data: OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>>,
#[returns(ref)]
plugin_diagnostics: Vec<(ModuleId<'db>, PluginDiagnostic<'db>)>,
#[returns(ref)]
diagnostics_notes: PluginFileDiagnosticNotes<'db>,
}
impl<'db> ModuleData<'db> {
pub fn constants(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<ConstantId<'db>, ast::ItemConstant<'db>> {
self.named_items_data(db).constants(db)
}
pub fn submodules(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<SubmoduleId<'db>, ast::ItemModule<'db>> {
self.named_items_data(db).submodules(db)
}
pub fn uses(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<UseId<'db>, ast::UsePathLeaf<'db>> {
self.named_items_data(db).uses(db)
}
pub fn free_functions(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<FreeFunctionId<'db>, ast::FunctionWithBody<'db>> {
self.named_items_data(db).free_functions(db)
}
pub fn structs(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<StructId<'db>, ast::ItemStruct<'db>> {
self.types_data(db).structs(db)
}
pub fn enums(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<EnumId<'db>, ast::ItemEnum<'db>> {
self.types_data(db).enums(db)
}
pub fn type_aliases(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<ModuleTypeAliasId<'db>, ast::ItemTypeAlias<'db>> {
self.types_data(db).type_aliases(db)
}
pub fn impl_aliases(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<ImplAliasId<'db>, ast::ItemImplAlias<'db>> {
self.named_items_data(db).impl_aliases(db)
}
pub fn traits(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<TraitId<'db>, ast::ItemTrait<'db>> {
self.named_items_data(db).traits(db)
}
pub fn impls(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<ImplDefId<'db>, ast::ItemImpl<'db>> {
self.named_items_data(db).impls(db)
}
pub fn extern_types(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<ExternTypeId<'db>, ast::ItemExternType<'db>> {
self.types_data(db).extern_types(db)
}
pub fn extern_functions(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<ExternFunctionId<'db>, ast::ItemExternFunction<'db>> {
self.named_items_data(db).extern_functions(db)
}
pub fn macro_declarations(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<MacroDeclarationId<'db>, ast::ItemMacroDeclaration<'db>> {
self.named_items_data(db).macro_declarations(db)
}
pub fn global_uses(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<GlobalUseId<'db>, ast::UsePathStar<'db>> {
self.unnamed_items_data(db).global_uses(db)
}
pub fn macro_calls(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<MacroCallId<'db>, ast::ItemInlineMacro<'db>> {
self.unnamed_items_data(db).macro_calls(db)
}
pub fn files(&self, db: &'db dyn Database) -> &'db Vec<FileId<'db>> {
self.files_data(db).files(db)
}
pub fn generated_file_aux_data(
&self,
db: &'db dyn Database,
) -> &'db OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>> {
self.files_data(db).generated_file_aux_data(db)
}
pub fn plugin_diagnostics(
&self,
db: &'db dyn Database,
) -> &'db Vec<(ModuleId<'db>, PluginDiagnostic<'db>)> {
self.files_data(db).plugin_diagnostics(db)
}
pub fn diagnostics_notes(&self, db: &'db dyn Database) -> &'db PluginFileDiagnosticNotes<'db> {
self.files_data(db).diagnostics_notes(db)
}
}
#[derive(Clone, Debug, Eq, PartialEq, salsa::Update)]
pub struct PrivModuleSubFiles<'db> {
files: OrderedHashMap<FileId<'db>, VirtualFile<'db>>,
aux_data: OrderedHashMap<FileId<'db>, Option<DynGeneratedFileAuxData>>,
items: Vec<ast::ModuleItem<'db>>,
plugin_diagnostics: Vec<PluginDiagnostic<'db>>,
diagnostics_notes: PluginFileDiagnosticNotes<'db>,
}
#[salsa::tracked]
fn priv_module_data_helper<'db>(
db: &'db dyn Database,
_tracked: Tracked,
module_id: ModuleId<'db>,
) -> Maybe<ModuleData<'db>> {
let crate_id = module_id.owning_crate(db);
if let Some((map, _)) = db.cached_crate_modules(crate_id) {
if let Some(module_data) = map.get(&module_id) {
return Ok(*module_data);
} else {
panic!("module not found in cached modules_data {:?}", module_id.name(db));
}
};
let module_file = db.module_main_file(module_id)?;
let main_file_aux_data = if let ModuleId::Submodule(submodule_id) = module_id {
let parent_module_data = submodule_id.module_data(db)?;
let item_module_ast = &parent_module_data.submodules(db)[&submodule_id];
if matches!(item_module_ast.body(db), MaybeModuleBody::Some(_)) {
parent_module_data.generated_file_aux_data(db)
[&item_module_ast.stable_ptr(db).untyped().file_id(db)]
.clone()
} else {
None
}
} else {
None
};
let mut file_queue = VecDeque::new();
file_queue.push_back(module_file);
let mut constants = OrderedHashMap::default();
let mut submodules = OrderedHashMap::default();
let mut uses = OrderedHashMap::default();
let mut free_functions = OrderedHashMap::default();
let mut structs = OrderedHashMap::default();
let mut enums = OrderedHashMap::default();
let mut type_aliases = OrderedHashMap::default();
let mut impl_aliases = OrderedHashMap::default();
let mut traits = OrderedHashMap::default();
let mut impls = OrderedHashMap::default();
let mut extern_types = OrderedHashMap::default();
let mut extern_functions = OrderedHashMap::default();
let mut macro_declarations = OrderedHashMap::default();
let mut macro_calls = OrderedHashMap::default();
let mut global_uses = OrderedHashMap::default();
let mut aux_data = OrderedHashMap::default();
let mut files = Vec::new();
let mut plugin_diagnostics = Vec::new();
let mut diagnostics_notes = OrderedHashMap::default();
let mut items = vec![];
aux_data.insert(module_file, main_file_aux_data);
while let Some(file_id) = file_queue.pop_front() {
files.push(file_id);
let priv_module_data = module_sub_files(db, module_id, file_id).maybe_as_ref()?;
diagnostics_notes
.extend(priv_module_data.diagnostics_notes.iter().map(|(k, v)| (*k, v.clone())));
file_queue.extend(priv_module_data.files.keys().copied());
plugin_diagnostics
.extend(priv_module_data.plugin_diagnostics.iter().map(|v| (module_id, v.clone())));
aux_data.extend(priv_module_data.aux_data.iter().map(|(k, v)| (*k, v.clone())));
for item_ast in &priv_module_data.items {
match item_ast.clone() {
ast::ModuleItem::Constant(constant) => {
let item_id = ConstantLongId(module_id, constant.stable_ptr(db)).intern(db);
constants.insert(item_id, constant);
items.push(ModuleItemId::Constant(item_id));
}
ast::ModuleItem::Module(module) => {
let item_id = SubmoduleLongId(module_id, module.stable_ptr(db)).intern(db);
submodules.insert(item_id, module);
items.push(ModuleItemId::Submodule(item_id));
}
ast::ModuleItem::Use(us) => {
for leaf in get_all_path_leaves(db, &us) {
let id = UseLongId(module_id, leaf.stable_ptr(db)).intern(db);
uses.insert(id, leaf);
items.push(ModuleItemId::Use(id));
}
for star in get_all_path_stars(db, &us) {
let id = GlobalUseLongId(module_id, star.stable_ptr(db)).intern(db);
global_uses.insert(id, star);
}
}
ast::ModuleItem::FreeFunction(function) => {
let item_id = FreeFunctionLongId(module_id, function.stable_ptr(db)).intern(db);
free_functions.insert(item_id, function);
items.push(ModuleItemId::FreeFunction(item_id));
}
ast::ModuleItem::ExternFunction(extern_function) => {
let item_id =
ExternFunctionLongId(module_id, extern_function.stable_ptr(db)).intern(db);
extern_functions.insert(item_id, extern_function);
items.push(ModuleItemId::ExternFunction(item_id));
}
ast::ModuleItem::ExternType(extern_type) => {
let item_id =
ExternTypeLongId(module_id, extern_type.stable_ptr(db)).intern(db);
extern_types.insert(item_id, extern_type);
items.push(ModuleItemId::ExternType(item_id));
}
ast::ModuleItem::Trait(trt) => {
let item_id = TraitLongId(module_id, trt.stable_ptr(db)).intern(db);
traits.insert(item_id, trt);
items.push(ModuleItemId::Trait(item_id));
}
ast::ModuleItem::Impl(imp) => {
let item_id = ImplDefLongId(module_id, imp.stable_ptr(db)).intern(db);
impls.insert(item_id, imp);
items.push(ModuleItemId::Impl(item_id));
}
ast::ModuleItem::Struct(structure) => {
let item_id = StructLongId(module_id, structure.stable_ptr(db)).intern(db);
structs.insert(item_id, structure);
items.push(ModuleItemId::Struct(item_id));
}
ast::ModuleItem::Enum(enm) => {
let item_id = EnumLongId(module_id, enm.stable_ptr(db)).intern(db);
enums.insert(item_id, enm);
items.push(ModuleItemId::Enum(item_id));
}
ast::ModuleItem::TypeAlias(type_alias) => {
let item_id =
ModuleTypeAliasLongId(module_id, type_alias.stable_ptr(db)).intern(db);
type_aliases.insert(item_id, type_alias);
items.push(ModuleItemId::TypeAlias(item_id));
}
ast::ModuleItem::ImplAlias(impl_alias) => {
let item_id = ImplAliasLongId(module_id, impl_alias.stable_ptr(db)).intern(db);
impl_aliases.insert(item_id, impl_alias);
items.push(ModuleItemId::ImplAlias(item_id));
}
ast::ModuleItem::MacroDeclaration(macro_declaration) => {
let item_id =
MacroDeclarationLongId(module_id, macro_declaration.stable_ptr(db))
.intern(db);
macro_declarations.insert(item_id, macro_declaration);
items.push(ModuleItemId::MacroDeclaration(item_id));
}
ast::ModuleItem::InlineMacro(inline_macro_ast) => {
let item_id =
MacroCallLongId(module_id, inline_macro_ast.stable_ptr(db)).intern(db);
macro_calls.insert(item_id, inline_macro_ast.clone());
}
ast::ModuleItem::HeaderDoc(_) => {}
ast::ModuleItem::Missing(_) => {}
}
}
}
let types_data = ModuleTypesData::new(db, structs, enums, type_aliases, extern_types);
let named_items_data = ModuleNamedItemsData::new(
db,
constants,
submodules,
uses,
free_functions,
impl_aliases,
traits,
impls,
extern_functions,
macro_declarations,
);
let unnamed_items_data = ModuleUnnamedItemsData::new(db, global_uses, macro_calls);
let files_data =
ModuleFilesData::new(db, files, aux_data, plugin_diagnostics, diagnostics_notes);
Ok(ModuleData::new(db, items, types_data, named_items_data, unnamed_items_data, files_data))
}
pub(crate) fn module_data<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<ModuleData<'db>> {
priv_module_data_helper(db, (), module_id)
}
pub type ModuleDataCacheAndLoadingData<'db> =
(Arc<OrderedHashMap<ModuleId<'db>, ModuleData<'db>>>, Arc<DefCacheLoadingData<'db>>);
#[salsa::tracked]
fn cached_crate_modules<'db>(
db: &'db dyn Database,
crate_id: CrateId<'db>,
) -> Option<ModuleDataCacheAndLoadingData<'db>> {
load_cached_crate_modules(db, crate_id)
}
pub fn init_external_files<T: DefsGroup>(db: &mut T) {
let ext_as_virtual_impl: ExtAsVirtual =
Arc::new(|db: &dyn Database, external_id: salsa::Id| ext_as_virtual_impl(db, external_id));
files_group_input(db).set_ext_as_virtual_obj(db).to(Some(ext_as_virtual_impl));
}
pub fn ext_as_virtual_impl<'db>(
db: &'db dyn Database,
external_id: salsa::Id,
) -> &'db VirtualFile<'db> {
let long_id = PluginGeneratedFileId::from_intern_id(external_id).long(db);
let file_id = FileLongId::External(external_id).intern(db);
let data =
module_sub_files(db, long_id.module_id, long_id.stable_ptr.file_id(db)).as_ref().unwrap();
&data.files[&file_id]
}
#[salsa::tracked(returns(ref))]
fn module_sub_files<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
file_id: FileId<'db>,
) -> Maybe<PrivModuleSubFiles<'db>> {
let module_main_file = db.module_main_file(module_id)?;
let file_syntax = db.file_module_syntax(file_id)?;
let item_asts = if module_main_file == file_id {
if let ModuleId::Submodule(submodule_id) = module_id {
let data = submodule_id.module_data(db)?;
if let MaybeModuleBody::Some(body) = data.submodules(db)[&submodule_id].body(db) {
Some(body.items(db))
} else {
None
}
} else {
None
}
} else {
None
}
.unwrap_or_else(|| file_syntax.items(db));
let crate_id = module_id.owning_crate(db);
let allowed_attributes = db.allowed_attributes(crate_id);
let allowed_features = Default::default();
let cfg_set = db
.crate_config(crate_id)
.and_then(|cfg| cfg.settings.cfg_set.as_ref())
.unwrap_or(db.cfg_set());
let edition = db
.crate_config(module_id.owning_crate(db))
.map(|cfg| cfg.settings.edition)
.unwrap_or_default();
let metadata = MacroPluginMetadata {
cfg_set,
declared_derives: db.declared_derives(crate_id),
allowed_features: &allowed_features,
edition,
};
let mut files = OrderedHashMap::<_, _>::default();
let mut aux_data = OrderedHashMap::default();
let mut items = Vec::new();
let mut plugin_diagnostics = Vec::new();
let mut diagnostics_notes = OrderedHashMap::default();
for item_ast in item_asts.elements(db) {
let mut remove_original_item = false;
for plugin_id in db.crate_macro_plugins(crate_id).iter() {
let plugin = plugin_id.long(db);
let result = plugin.generate_code(db, item_ast.clone(), &metadata);
plugin_diagnostics.extend(result.diagnostics);
if result.remove_original_item {
remove_original_item = true;
}
if let Some(generated) = result.code {
let stable_ptr = item_ast.stable_ptr(db).untyped();
let generated_file_id = FileLongId::External(
PluginGeneratedFileLongId {
module_id,
stable_ptr,
name: generated.name.clone(),
}
.intern(db)
.as_intern_id(),
)
.intern(db);
if let Some(text) = generated.diagnostics_note {
diagnostics_notes
.insert(generated_file_id, DiagnosticNote { text, location: None });
}
files.insert(
generated_file_id,
VirtualFile {
parent: Some(stable_ptr.span_in_file(db)),
name: SmolStrId::from(db, generated.name),
content: SmolStrId::from(db, generated.content),
code_mappings: generated.code_mappings.into(),
kind: FileKind::Module,
original_item_removed: remove_original_item,
},
);
aux_data.insert(generated_file_id, generated.aux_data);
}
if remove_original_item {
break;
}
}
if remove_original_item {
continue;
}
validate_attributes(db, allowed_attributes, &item_ast, &mut plugin_diagnostics);
items.push(item_ast);
}
Ok(PrivModuleSubFiles { files, aux_data, items, plugin_diagnostics, diagnostics_notes })
}
fn collect_extra_allowed_attributes<'db>(
db: &'db dyn Database,
item: &impl QueryAttrs<'db>,
plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
) -> OrderedHashSet<SmolStrId<'db>> {
let mut extra_allowed_attributes = OrderedHashSet::default();
for attr in item.query_attr(db, ALLOW_ATTR_ATTR) {
let args = attr.clone().structurize(db).args;
if args.is_empty() {
plugin_diagnostics.push(PluginDiagnostic::error(
attr.stable_ptr(db),
"Expected arguments.".to_string(),
));
continue;
}
for arg in args {
if let Some(ast::Expr::Path(path)) = try_extract_unnamed_arg(db, &arg.arg)
&& let Ok(ast::PathSegment::Simple(segment)) =
path.segments(db).elements(db).exactly_one()
{
extra_allowed_attributes.insert(segment.ident(db).text(db));
continue;
}
plugin_diagnostics.push(PluginDiagnostic::error(
arg.arg.stable_ptr(db),
"Expected simple identifier.".to_string(),
));
}
}
extra_allowed_attributes
}
pub fn validate_attributes_flat<'db, Item: QueryAttrs<'db> + TypedSyntaxNode<'db>>(
db: &'db dyn Database,
allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
extra_allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
item: &Item,
plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
) {
let local_extra_attributes = collect_extra_allowed_attributes(db, item, plugin_diagnostics);
for attr in item.attributes_elements(db) {
let attr_text = attr.attr(db).as_syntax_node().get_text_without_trivia(db);
if !(allowed_attributes.contains(&attr_text)
|| extra_allowed_attributes.contains(&attr_text)
|| local_extra_attributes.contains(&attr_text))
{
plugin_diagnostics.push(PluginDiagnostic::error(
attr.stable_ptr(db),
"Unsupported attribute.".to_string(),
));
}
}
for attr in item.query_attr(db, PATH_ATTR) {
let node = item.as_syntax_node();
let Some(item_module) = ast::ItemModule::cast(db, node) else {
plugin_diagnostics.push(PluginDiagnostic::error(
attr.stable_ptr(db),
"`#[path(..)]` is only allowed on module declarations.".to_string(),
));
continue;
};
if matches!(item_module.body(db), MaybeModuleBody::Some(_)) {
plugin_diagnostics.push(PluginDiagnostic::error(
attr.stable_ptr(db),
"`#[path(..)]` requires a file-based module: use `mod name;` with a semicolon."
.to_string(),
));
continue;
}
let args = attr.arguments(db);
if extract_path_arg(db, &args).is_none() {
plugin_diagnostics.push(PluginDiagnostic::error(
args.stable_ptr(db),
"`#[path(..)]` expects exactly one string literal argument.".to_string(),
));
}
}
}
fn extract_path_arg<'db>(
db: &'db dyn Database,
args: &ast::OptionArgListParenthesized<'db>,
) -> Option<ast::TerminalString<'db>> {
match args {
ast::OptionArgListParenthesized::Empty(_) => None,
ast::OptionArgListParenthesized::ArgListParenthesized(args) => {
let arg = args.arguments(db).elements(db).exactly_one().ok()?;
if let ast::Expr::String(path) = try_extract_unnamed_arg(db, &arg)? {
Some(path)
} else {
None
}
}
}
}
fn validate_attributes_element_list<'db, Item: QueryAttrs<'db> + TypedSyntaxNode<'db>>(
db: &'db dyn Database,
allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
extra_allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
items: impl Iterator<Item = Item>,
plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
) {
for item in items {
validate_attributes_flat(
db,
allowed_attributes,
extra_allowed_attributes,
&item,
plugin_diagnostics,
);
}
}
fn validate_attributes<'db>(
db: &'db dyn Database,
allowed_attributes: &OrderedHashSet<SmolStrId<'db>>,
item_ast: &ast::ModuleItem<'db>,
plugin_diagnostics: &mut Vec<PluginDiagnostic<'db>>,
) {
let extra_allowed_attributes =
collect_extra_allowed_attributes(db, item_ast, plugin_diagnostics);
validate_attributes_flat(
db,
allowed_attributes,
&extra_allowed_attributes,
item_ast,
plugin_diagnostics,
);
match item_ast {
ast::ModuleItem::Trait(item) => {
if let ast::MaybeTraitBody::Some(body) = item.body(db) {
validate_attributes_element_list(
db,
allowed_attributes,
&extra_allowed_attributes,
body.items(db).elements(db),
plugin_diagnostics,
);
}
}
ast::ModuleItem::Impl(item) => {
if let ast::MaybeImplBody::Some(body) = item.body(db) {
validate_attributes_element_list(
db,
allowed_attributes,
&extra_allowed_attributes,
body.items(db).elements(db),
plugin_diagnostics,
);
}
}
ast::ModuleItem::Struct(item) => {
validate_attributes_element_list(
db,
allowed_attributes,
&extra_allowed_attributes,
item.members(db).elements(db),
plugin_diagnostics,
);
}
ast::ModuleItem::Enum(item) => {
validate_attributes_element_list(
db,
allowed_attributes,
&extra_allowed_attributes,
item.variants(db).elements(db),
plugin_diagnostics,
);
}
_ => {}
}
}
pub fn get_all_path_leaves<'db>(
db: &'db dyn Database,
use_item: &ast::ItemUse<'db>,
) -> Vec<ast::UsePathLeaf<'db>> {
let mut res = vec![];
let mut stack = vec![use_item.use_path(db)];
while let Some(use_path) = stack.pop() {
match use_path {
ast::UsePath::Leaf(use_path) => res.push(use_path),
ast::UsePath::Single(use_path) => stack.push(use_path.use_path(db)),
ast::UsePath::Multi(use_path) => {
stack.extend(use_path.use_paths(db).elements(db).rev())
}
ast::UsePath::Star(_) => {}
}
}
res
}
pub fn get_all_path_stars<'db>(
db: &'db dyn Database,
use_item: &ast::ItemUse<'db>,
) -> Vec<ast::UsePathStar<'db>> {
let mut res = vec![];
let mut stack = vec![use_item.use_path(db)];
while let Some(use_path) = stack.pop() {
match use_path {
ast::UsePath::Leaf(_) => {}
ast::UsePath::Single(use_path) => stack.push(use_path.use_path(db)),
ast::UsePath::Multi(use_path) => {
stack.extend(use_path.use_paths(db).elements(db).rev())
}
ast::UsePath::Star(use_path) => res.push(use_path),
}
}
res
}
#[salsa::tracked(returns(ref))]
fn module_constants_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<ConstantId<'db>> {
module_data.constants(db).keys().copied().collect_vec()
}
pub fn module_constants_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ConstantId<'db>]> {
Ok(module_constants_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_constant_by_id<'db>(
db: &'db dyn Database,
constant_id: ConstantId<'db>,
) -> Maybe<ast::ItemConstant<'db>> {
let module_constants = constant_id.module_data(db)?.constants(db);
module_constants.get(&constant_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_submodules_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<SubmoduleId<'db>> {
module_data.submodules(db).keys().copied().collect_vec()
}
fn module_submodules_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [SubmoduleId<'db>]> {
Ok(module_submodules_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_submodule_by_id<'db>(
db: &'db dyn Database,
submodule_id: SubmoduleId<'db>,
) -> Maybe<ast::ItemModule<'db>> {
let module_submodules = submodule_id.module_data(db)?.submodules(db);
module_submodules.get(&submodule_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_free_functions_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<FreeFunctionId<'db>> {
module_data.free_functions(db).keys().copied().collect_vec()
}
pub fn module_free_functions_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [FreeFunctionId<'db>]> {
Ok(module_free_functions_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_free_function_by_id<'db>(
db: &'db dyn Database,
free_function_id: FreeFunctionId<'db>,
) -> Maybe<ast::FunctionWithBody<'db>> {
let module_free_functions = free_function_id.module_data(db)?.free_functions(db);
module_free_functions.get(&free_function_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_uses_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<UseId<'db>> {
module_data.uses(db).keys().copied().collect_vec()
}
pub fn module_uses_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [UseId<'db>]> {
Ok(module_uses_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_use_by_id<'db>(
db: &'db dyn Database,
use_id: UseId<'db>,
) -> Maybe<ast::UsePathLeaf<'db>> {
let module_uses = use_id.module_data(db)?.uses(db);
module_uses.get(&use_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked]
pub fn module_global_use_by_id<'db>(
db: &'db dyn Database,
global_use_id: GlobalUseId<'db>,
) -> Maybe<ast::UsePathStar<'db>> {
let module_global_uses = global_use_id.module_data(db)?.global_uses(db);
module_global_uses.get(&global_use_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_structs_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<StructId<'db>> {
module_data.structs(db).keys().copied().collect_vec()
}
pub fn module_structs_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [StructId<'db>]> {
Ok(module_structs_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_struct_by_id<'db>(
db: &'db dyn Database,
struct_id: StructId<'db>,
) -> Maybe<ast::ItemStruct<'db>> {
let module_structs = struct_id.module_data(db)?.structs(db);
module_structs.get(&struct_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_enums_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<EnumId<'db>> {
module_data.enums(db).keys().copied().collect_vec()
}
pub fn module_enums_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [EnumId<'db>]> {
Ok(module_enums_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_enum_by_id<'db>(
db: &'db dyn Database,
enum_id: EnumId<'db>,
) -> Maybe<ast::ItemEnum<'db>> {
let module_enums = enum_id.module_data(db)?.enums(db);
module_enums.get(&enum_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_type_aliases_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<ModuleTypeAliasId<'db>> {
module_data.type_aliases(db).keys().copied().collect_vec()
}
pub fn module_type_aliases_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ModuleTypeAliasId<'db>]> {
Ok(module_type_aliases_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_type_alias_by_id<'db>(
db: &'db dyn Database,
module_type_alias_id: ModuleTypeAliasId<'db>,
) -> Maybe<ast::ItemTypeAlias<'db>> {
let module_type_aliases = module_type_alias_id.module_data(db)?.type_aliases(db);
module_type_aliases.get(&module_type_alias_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_impl_aliases_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<ImplAliasId<'db>> {
module_data.impl_aliases(db).keys().copied().collect_vec()
}
pub fn module_impl_aliases_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ImplAliasId<'db>]> {
Ok(module_impl_aliases_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_impl_alias_by_id<'db>(
db: &'db dyn Database,
impl_alias_id: ImplAliasId<'db>,
) -> Maybe<ast::ItemImplAlias<'db>> {
let module_impl_aliases = impl_alias_id.module_data(db)?.impl_aliases(db);
module_impl_aliases.get(&impl_alias_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_traits_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<TraitId<'db>> {
module_data.traits(db).keys().copied().collect_vec()
}
pub fn module_traits_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [TraitId<'db>]> {
Ok(module_traits_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_trait_by_id<'db>(
db: &'db dyn Database,
trait_id: TraitId<'db>,
) -> Maybe<ast::ItemTrait<'db>> {
let module_traits = trait_id.module_data(db)?.traits(db);
module_traits.get(&trait_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_impls_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<ImplDefId<'db>> {
module_data.impls(db).keys().copied().collect_vec()
}
pub fn module_impls_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ImplDefId<'db>]> {
Ok(module_impls_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_impl_by_id<'db>(
db: &'db dyn Database,
impl_def_id: ImplDefId<'db>,
) -> Maybe<ast::ItemImpl<'db>> {
let module_impls = impl_def_id.module_data(db)?.impls(db);
module_impls.get(&impl_def_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_extern_types_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<ExternTypeId<'db>> {
module_data.extern_types(db).keys().copied().collect_vec()
}
pub fn module_extern_types_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ExternTypeId<'db>]> {
Ok(module_extern_types_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_extern_type_by_id<'db>(
db: &'db dyn Database,
extern_type_id: ExternTypeId<'db>,
) -> Maybe<ast::ItemExternType<'db>> {
let module_extern_types = extern_type_id.module_data(db)?.extern_types(db);
module_extern_types.get(&extern_type_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_macro_declarations_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<MacroDeclarationId<'db>> {
module_data.macro_declarations(db).keys().copied().collect_vec()
}
pub fn module_macro_declarations_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [MacroDeclarationId<'db>]> {
Ok(module_macro_declarations_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_macro_declaration_by_id<'db>(
db: &'db dyn Database,
macro_declaration_id: MacroDeclarationId<'db>,
) -> Maybe<ast::ItemMacroDeclaration<'db>> {
let module_macro_declarations = macro_declaration_id.module_data(db)?.macro_declarations(db);
module_macro_declarations.get(¯o_declaration_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_macro_calls_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<MacroCallId<'db>> {
module_data.macro_calls(db).keys().copied().collect_vec()
}
pub fn module_macro_calls_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [MacroCallId<'db>]> {
Ok(module_macro_calls_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
fn module_macro_call_by_id<'db>(
db: &'db dyn Database,
macro_call_id: MacroCallId<'db>,
) -> Maybe<ast::ItemInlineMacro<'db>> {
let module_macro_calls = macro_call_id.module_data(db)?.macro_calls(db);
module_macro_calls.get(¯o_call_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_extern_functions_ids_helper<'db>(
db: &'db dyn Database,
module_data: ModuleData<'db>,
) -> Vec<ExternFunctionId<'db>> {
module_data.extern_functions(db).keys().copied().collect_vec()
}
pub fn module_extern_functions_ids<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
) -> Maybe<&'db [ExternFunctionId<'db>]> {
Ok(module_extern_functions_ids_helper(db, module_id.module_data(db)?))
}
#[salsa::tracked]
pub fn module_extern_function_by_id<'db>(
db: &'db dyn Database,
extern_function_id: ExternFunctionId<'db>,
) -> Maybe<ast::ItemExternFunction<'db>> {
let module_extern_functions = extern_function_id.module_data(db)?.extern_functions(db);
module_extern_functions.get(&extern_function_id).cloned().ok_or_else(skip_diagnostic)
}
#[salsa::tracked(returns(ref))]
fn module_ancestors_helper<'db>(
db: &'db dyn Database,
_tracked: Tracked,
module_id: ModuleId<'db>,
) -> OrderedHashSet<ModuleId<'db>> {
let mut current = module_id;
let mut ancestors = OrderedHashSet::default();
loop {
match current {
ModuleId::CrateRoot(_) => {
ancestors.insert(current);
return ancestors;
}
ModuleId::Submodule(submodule_id) => {
ancestors.insert(current);
current = submodule_id.parent_module(db);
}
ModuleId::MacroCall { id, .. } => {
current = id.parent_module(db);
}
}
}
}
#[salsa::tracked]
fn module_perceived_module_helper<'db>(
db: &'db dyn Database,
_tracked: Tracked,
mut module_id: ModuleId<'db>,
) -> ModuleId<'db> {
while let ModuleId::MacroCall { id, .. } = module_id {
module_id = id.parent_module(db);
}
module_id
}
fn module_item_name_stable_ptr<'db>(
db: &'db dyn Database,
module_id: ModuleId<'db>,
item_id: ModuleItemId<'db>,
) -> Maybe<SyntaxStablePtrId<'db>> {
let data = module_id.module_data(db)?;
Ok(match &item_id {
ModuleItemId::Constant(id) => data.constants(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::Submodule(id) => data.submodules(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::Use(id) => data.uses(db)[id].name_stable_ptr(db),
ModuleItemId::FreeFunction(id) => {
data.free_functions(db)[id].declaration(db).name(db).stable_ptr(db).untyped()
}
ModuleItemId::Struct(id) => data.structs(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::Enum(id) => data.enums(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::TypeAlias(id) => data.type_aliases(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::ImplAlias(id) => data.impl_aliases(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::Trait(id) => data.traits(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::Impl(id) => data.impls(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::ExternType(id) => data.extern_types(db)[id].name(db).stable_ptr(db).untyped(),
ModuleItemId::ExternFunction(id) => {
data.extern_functions(db)[id].declaration(db).name(db).stable_ptr(db).untyped()
}
ModuleItemId::MacroDeclaration(id) => {
data.macro_declarations(db)[id].name(db).stable_ptr(db).untyped()
}
})
}
pub trait DefsGroupEx: DefsGroup {
fn set_override_crate_macro_plugins<'db>(
&'db mut self,
crate_id: CrateId<'db>,
plugins: Arc<Vec<MacroPluginId<'db>>>,
) {
let crate_input = self.crate_input(crate_id);
let mut overrides = self.macro_plugin_overrides_input().clone();
let plugins = plugins.iter().map(|plugin| plugin.long(self).clone()).collect();
overrides.insert(crate_input.clone(), plugins);
defs_group_input(self.as_dyn_database())
.set_macro_plugin_overrides(self)
.to(Some(overrides));
}
fn set_override_crate_inline_macro_plugins<'db>(
&'db mut self,
crate_id: CrateId<'db>,
plugins: Arc<OrderedHashMap<String, InlineMacroExprPluginId<'db>>>,
) {
let crate_input = self.crate_input(crate_id);
let mut overrides = self.inline_macro_plugin_overrides_input().clone();
let plugins = Arc::new(
plugins
.iter()
.map(|(name, plugin)| (name.clone(), plugin.long(self).clone()))
.collect(),
);
overrides.insert(crate_input.clone(), plugins);
defs_group_input(self.as_dyn_database())
.set_inline_macro_plugin_overrides(self)
.to(Some(overrides));
}
}
impl<T: DefsGroup + ?Sized> DefsGroupEx for T {}