diff --git a/crates/hir/src/container.rs b/crates/hir/src/container.rs index 67d50589..c5d4c825 100644 --- a/crates/hir/src/container.rs +++ b/crates/hir/src/container.rs @@ -23,31 +23,104 @@ use crate::{ generate::{GenerateBlock, GenerateBlockId, GenerateBlockSourceMap}, }, stmt::{Stmt, StmtId, StmtSrc}, - subroutine::{Subroutine, SubroutineId, SubroutineSourceMap}, + subroutine::{LocalSubroutineId, Subroutine, SubroutineSourceMap}, typedef::{Typedef, TypedefId, TypedefSrc}, }, region_tree::RegionTree, + symbol::ScopeKind, }; define_enum_deriving_from! { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] - pub enum ContainerId { - HirFileId(HirFileId), - ModuleId(ModuleId), - GenerateBlockId(GenerateBlockId), - BlockId(BlockId), - SubroutineId(SubroutineId), + pub enum ScopeId { + File(HirFileId), + Module(ModuleId), + GenerateBlock(GenerateBlockId), + Block(BlockId), + Subroutine(SubroutineScope), + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub struct SubroutineScope { + pub cont_id: SubroutineParent, + pub value: LocalSubroutineId, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum SubroutineParent { + File(HirFileId), + Module(ModuleId), + GenerateBlock(GenerateBlockId), +} + +impl SubroutineScope { + pub fn new(cont_id: SubroutineParent, value: LocalSubroutineId) -> Self { + Self { cont_id, value } + } + + pub fn parent_scope(self) -> ScopeId { + self.cont_id.into() + } + + pub fn as_in_container(self) -> InContainer { + InContainer::new(self.parent_scope(), self.value) + } + + pub fn file_id(self, db: &dyn InternDb) -> FileId { + match self.cont_id { + SubroutineParent::File(file_id) => file_id.file_id(), + SubroutineParent::Module(module_id) => module_id.file_id(), + SubroutineParent::GenerateBlock(generate_block_id) => generate_block_id.file_id(db), + } + } +} + +impl From for ScopeId { + fn from(cont_id: SubroutineParent) -> Self { + match cont_id { + SubroutineParent::File(file_id) => file_id.into(), + SubroutineParent::Module(module_id) => module_id.into(), + SubroutineParent::GenerateBlock(generate_block_id) => generate_block_id.into(), + } + } +} + +impl TryFrom for SubroutineParent { + type Error = (); + + fn try_from(cont_id: ScopeId) -> Result { + match cont_id { + ScopeId::File(file_id) => Ok(Self::File(file_id)), + ScopeId::Module(module_id) => Ok(Self::Module(module_id)), + ScopeId::GenerateBlock(generate_block_id) => Ok(Self::GenerateBlock(generate_block_id)), + ScopeId::Block(_) | ScopeId::Subroutine(_) => Err(()), + } + } +} + +impl From> for SubroutineScope { + fn from(subroutine: InContainer) -> Self { + let parent = SubroutineParent::try_from(subroutine.cont_id) + .expect("subroutines are lowered only in file, module, or generate-block scopes"); + Self::new(parent, subroutine.value) + } +} + +impl From> for ScopeId { + fn from(subroutine: InContainer) -> Self { + ScopeId::Subroutine(subroutine.into()) } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] pub struct InContainer { pub value: T, - pub cont_id: ContainerId, + pub cont_id: ScopeId, } impl InContainer { - pub fn new(cont_id: ContainerId, value: T) -> InContainer { + pub fn new(cont_id: ScopeId, value: T) -> InContainer { InContainer { value, cont_id } } @@ -63,11 +136,11 @@ impl InContainer { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] pub struct InSubroutine { pub value: T, - pub subroutine: SubroutineId, + pub subroutine: InContainer, } impl InSubroutine { - pub fn new(subroutine: SubroutineId, value: T) -> Self { + pub fn new(subroutine: InContainer, value: T) -> Self { Self { value, subroutine } } @@ -78,7 +151,7 @@ impl InSubroutine { impl From> for InContainer { fn from(item: InSubroutine) -> InContainer { - InContainer::new(ContainerId::SubroutineId(item.subroutine), item.value) + InContainer::new(item.subroutine.into(), item.value) } } @@ -121,41 +194,47 @@ define_container_id! { InBlock[block_id: BlockId], } -impl ContainerId { +impl ScopeId { + pub fn kind(self) -> ScopeKind { + match self { + ScopeId::File(_) => ScopeKind::File, + ScopeId::Module(_) => ScopeKind::Module, + ScopeId::GenerateBlock(_) => ScopeKind::GenerateBlock, + ScopeId::Block(_) => ScopeKind::Block, + ScopeId::Subroutine(_) => ScopeKind::Subroutine, + } + } + pub fn file_id(self, db: &dyn InternDb) -> FileId { match self { - ContainerId::HirFileId(file_id) => file_id.file_id(), - ContainerId::ModuleId(module_id) => module_id.file_id(), - ContainerId::GenerateBlockId(generate_block_id) => generate_block_id.file_id(db), - ContainerId::BlockId(block_id) => block_id.file_id(db), - ContainerId::SubroutineId(subroutine_id) => { - subroutine_id.lookup(db).src.file_id.file_id() - } + ScopeId::File(file_id) => file_id.file_id(), + ScopeId::Module(module_id) => module_id.file_id(), + ScopeId::GenerateBlock(generate_block_id) => generate_block_id.file_id(db), + ScopeId::Block(block_id) => block_id.file_id(db), + ScopeId::Subroutine(subroutine) => subroutine.file_id(db), } } pub fn to_container(self, db: &dyn HirDb) -> Container { match self { - ContainerId::HirFileId(file_id) => file_id.to_container(db).into(), - ContainerId::ModuleId(module_id) => module_id.to_container(db).into(), - ContainerId::GenerateBlockId(generate_block_id) => { - generate_block_id.to_container(db).into() - } - ContainerId::BlockId(block_id) => block_id.to_container(db).into(), - ContainerId::SubroutineId(subroutine_id) => db.subroutine(subroutine_id).into(), + ScopeId::File(file_id) => file_id.to_container(db).into(), + ScopeId::Module(module_id) => module_id.to_container(db).into(), + ScopeId::GenerateBlock(generate_block_id) => generate_block_id.to_container(db).into(), + ScopeId::Block(block_id) => block_id.to_container(db).into(), + ScopeId::Subroutine(subroutine) => db.subroutine(subroutine.as_in_container()).into(), } } pub fn to_container_src_map(self, db: &dyn HirDb) -> ContainerSrcMap { match self { - ContainerId::HirFileId(file_id) => file_id.to_container_src_map(db).into(), - ContainerId::ModuleId(module_id) => module_id.to_container_src_map(db).into(), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => file_id.to_container_src_map(db).into(), + ScopeId::Module(module_id) => module_id.to_container_src_map(db).into(), + ScopeId::GenerateBlock(generate_block_id) => { generate_block_id.to_container_src_map(db).into() } - ContainerId::BlockId(block_id) => block_id.to_container_src_map(db).into(), - ContainerId::SubroutineId(subroutine_id) => { - db.subroutine_with_source_map(subroutine_id).1.into() + ScopeId::Block(block_id) => block_id.to_container_src_map(db).into(), + ScopeId::Subroutine(subroutine) => { + db.subroutine_with_source_map(subroutine.as_in_container()).1.into() } } } @@ -282,32 +361,30 @@ impl AsRef for ContainerSrcMap { } /// Parents of a scope. -pub struct ContainerParent<'db> { +pub struct ScopeParent<'db> { db: &'db dyn InternDb, - cont_id: Option, + cont_id: Option, } -impl ContainerParent<'_> { - pub fn start_from(db: &dyn InternDb, cont_id: ContainerId) -> ContainerParent<'_> { - ContainerParent { db, cont_id: Some(cont_id) } +impl ScopeParent<'_> { + pub fn start_from(db: &dyn InternDb, cont_id: ScopeId) -> ScopeParent<'_> { + ScopeParent { db, cont_id: Some(cont_id) } } } -impl Iterator for ContainerParent<'_> { - type Item = ContainerId; +impl Iterator for ScopeParent<'_> { + type Item = ScopeId; fn next(&mut self) -> Option { let next = self.cont_id; self.cont_id = match self.cont_id? { - ContainerId::HirFileId(_) => None, - ContainerId::ModuleId(module_id) => Some(module_id.file_id.into()), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(_) => None, + ScopeId::Module(module_id) => Some(module_id.file_id.into()), + ScopeId::GenerateBlock(generate_block_id) => { Some(generate_block_id.lookup(self.db).cont_id) } - ContainerId::BlockId(block_id) => Some(block_id.lookup(self.db).cont_id), - ContainerId::SubroutineId(subroutine_id) => { - Some(subroutine_id.lookup(self.db).cont_id.into()) - } + ScopeId::Block(block_id) => Some(block_id.lookup(self.db).cont_id), + ScopeId::Subroutine(subroutine) => Some(subroutine.parent_scope()), }; next } diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index 61cb9cd0..1f53314e 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs @@ -16,19 +16,17 @@ use crate::{ file::{self, FileSourceMap, HirFile}, macro_file::{self, ExpansionInfo, MacroCallId, MacroCallLoc, MacroFileId, MacroFileLoc}, module::{ - self, Module, ModuleId, ModuleSourceMap, + self, Module, ModuleId, ModuleSourceMap, PackageId, generate::{ self, GenerateBlock, GenerateBlockId, GenerateBlockLoc, GenerateBlockSourceMap, }, }, - subroutine::{ - self, Subroutine, SubroutineId, SubroutineLoc, SubroutinePortId, SubroutineSourceMap, - }, + subroutine::{self, LocalSubroutineId, Subroutine, SubroutinePortId, SubroutineSourceMap}, typedef::TypedefId, }, impl_intern_key, impl_intern_lookup, - scope::{BlockScope, GenerateBlockScope, ModuleScope, SubroutineScope, UnitScope}, semantics::pathres::PathResolution, + symbol::{DefId, DefLoc, NameScope}, type_infer::TyResult, }; @@ -45,9 +43,6 @@ pub trait InternDb: SourceRootDb { #[salsa::interned] fn intern_block(&self, block: BlockLoc) -> BlockId; - #[salsa::interned] - fn intern_subroutine(&self, subroutine: SubroutineLoc) -> SubroutineId; - #[salsa::interned] fn intern_generate_block(&self, generate_block: GenerateBlockLoc) -> GenerateBlockId; @@ -59,11 +54,13 @@ pub trait InternDb: SourceRootDb { #[salsa::interned] fn intern_module_def(&self, module_def: ModuleDef) -> ModuleDefId; + + #[salsa::interned] + fn intern_def(&self, def: DefLoc) -> DefId; } impl_intern!(BuiltinDataTyId, BuiltinDataTy, intern_ty, lookup_intern_ty); impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); -impl_intern!(SubroutineId, SubroutineLoc, intern_subroutine, lookup_intern_subroutine); impl_intern!( GenerateBlockId, GenerateBlockLoc, @@ -73,6 +70,7 @@ impl_intern!( impl_intern!(MacroCallId, MacroCallLoc, intern_macro_call, lookup_intern_macro_call); impl_intern!(MacroFileId, MacroFileLoc, intern_macro_file, lookup_intern_macro_file); impl_intern!(ModuleDefId, ModuleDef, intern_module_def, lookup_intern_module_def); +impl_intern!(DefId, DefLoc, intern_def, lookup_intern_def); #[salsa::query_group(HirDbStorage)] pub trait HirDb: InternDb { @@ -100,10 +98,10 @@ pub trait HirDb: InternDb { #[salsa::invoke(subroutine::subroutine_with_source_map_query)] fn subroutine_with_source_map( &self, - subroutine: SubroutineId, + subroutine: InContainer, ) -> (Arc, Arc); - fn subroutine(&self, subroutine_id: SubroutineId) -> Arc; + fn subroutine(&self, subroutine_id: InContainer) -> Arc; #[salsa::invoke(generate::generate_block_with_source_map_query)] fn generate_block_with_source_map( @@ -113,23 +111,29 @@ pub trait HirDb: InternDb { fn generate_block(&self, generate_block_id: GenerateBlockId) -> Arc; - #[salsa::invoke(UnitScope::unit_scope_query)] - fn unit_scope(&self) -> Arc; + #[salsa::invoke(NameScope::unit_scope_query)] + fn unit_scope(&self) -> Arc; + + #[salsa::invoke(NameScope::file_scope_query)] + fn file_scope(&self, file_id: HirFileId) -> Arc; + + #[salsa::invoke(NameScope::module_scope_query)] + fn module_scope(&self, module_id: ModuleId) -> Arc; - #[salsa::invoke(UnitScope::file_scope_query)] - fn file_scope(&self, file_id: HirFileId) -> Arc; + #[salsa::invoke(NameScope::generate_block_scope_query)] + fn generate_block_scope(&self, generate_block_id: GenerateBlockId) -> Arc; - #[salsa::invoke(ModuleScope::module_scope_query)] - fn module_scope(&self, module_id: ModuleId) -> Arc; + #[salsa::invoke(NameScope::block_scope_query)] + fn block_scope(&self, block_id: BlockId) -> Arc; - #[salsa::invoke(GenerateBlockScope::generate_block_scope_query)] - fn generate_block_scope(&self, generate_block_id: GenerateBlockId) -> Arc; + #[salsa::invoke(NameScope::subroutine_scope_query)] + fn subroutine_scope(&self, subroutine_id: InContainer) -> Arc; - #[salsa::invoke(BlockScope::block_scope_query)] - fn block_scope(&self, block_id: BlockId) -> Arc; + #[salsa::invoke(NameScope::package_export_signature_query)] + fn package_export_signature(&self, package_id: PackageId) -> Arc; - #[salsa::invoke(SubroutineScope::subroutine_scope_query)] - fn subroutine_scope(&self, subroutine_id: SubroutineId) -> Arc; + #[salsa::invoke(NameScope::package_export_scope_query)] + fn package_export_scope(&self, package_id: PackageId) -> Arc; #[salsa::invoke(crate::type_infer::type_of_decl_query)] fn type_of_decl(&self, decl: InContainer) -> Arc; @@ -166,7 +170,7 @@ fn block(db: &dyn HirDb, block_id: BlockId) -> Arc { db.block_with_source_map(block_id).0 } -fn subroutine(db: &dyn HirDb, subroutine_id: SubroutineId) -> Arc { +fn subroutine(db: &dyn HirDb, subroutine_id: InContainer) -> Arc { db.subroutine_with_source_map(subroutine_id).0 } diff --git a/crates/hir/src/def_id.rs b/crates/hir/src/def_id.rs index 4b6ae0a2..d4aaaf85 100644 --- a/crates/hir/src/def_id.rs +++ b/crates/hir/src/def_id.rs @@ -6,170 +6,189 @@ use syntax::{ }; use utils::{ get::{Get, GetRef}, - impl_from, line_index::TextRange, }; use crate::{ base_db::{intern::Lookup, salsa}, - container::{ContainerId, InContainer, InFile, InModule, InSubroutine}, + container::{InContainer, InFile, InModule, InSubroutine, ScopeId}, db::HirDb, hir_def::{ - block::{BlockId, BlockLoc}, - expr::declarator::{DeclId, DeclaratorParent}, - file::{config::ConfigDeclId, library::LibraryDeclId, udp::UdpDeclId}, - module::{ - ModuleId, - generate::{GenerateBlockId, GenerateBlockLoc}, - instantiation::InstanceId, - port::NonAnsiPortId, - }, - stmt::StmtId, - subroutine::{SubroutineId, SubroutinePortId}, - typedef::TypedefId, + block::BlockLoc, + declaration::Declaration, + expr::declarator::DeclaratorParent, + module::{ModuleKind, generate::GenerateBlockLoc}, + subroutine::{LocalSubroutineId, SubroutineSrc}, }, source_map::{IsNamedSrc, IsSrc, ToAstNode}, + symbol::{DefId, DefKind, DefLoc}, }; -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum ModuleDefOrigin { - ModuleId(ModuleId), - Config(InFile), - Library(InFile), - Udp(InFile), - BlockId(BlockId), - GenerateBlockId(GenerateBlockId), - SubroutineId(SubroutineId), - SubroutinePort(InSubroutine), - - NonAnsiPort(InModule), - Decl(InContainer), - Typedef(InContainer), - Instance(InModule), - Stmt(InContainer), -} - -impl_from! { ModuleDefOrigin => - ModuleId, - Config(InFile), - Library(InFile), - Udp(InFile), - BlockId, - GenerateBlockId, - SubroutineId, - SubroutinePort(InSubroutine), - NonAnsiPort(InModule), - Decl(InContainer), - Typedef(InContainer), - Instance(InModule), - Stmt(InContainer), +fn subroutine_src( + db: &dyn HirDb, + subroutine: InContainer, +) -> Option> { + match subroutine.cont_id { + ScopeId::File(file_id) => { + let (_, source_map) = db.hir_file_with_source_map(file_id); + Some(InFile::new(file_id, source_map.get(subroutine.value)?)) + } + ScopeId::Module(module_id) => { + let (_, source_map) = db.module_with_source_map(module_id); + Some(InFile::new(module_id.file_id, source_map.get(subroutine.value)?)) + } + ScopeId::GenerateBlock(generate_block_id) => { + let (_, source_map) = db.generate_block_with_source_map(generate_block_id); + let file_id = generate_block_id.lookup(db).src.file_id; + Some(InFile::new(file_id, source_map.get(subroutine.value)?)) + } + ScopeId::Block(_) | ScopeId::Subroutine(_) => None, + } } -impl ModuleDefOrigin { +impl DefId { #[inline] - pub fn container_id(&self, db: &dyn HirDb) -> ContainerId { - match *self { - ModuleDefOrigin::ModuleId(InFile { file_id, .. }) => file_id.into(), - ModuleDefOrigin::Config(InFile { file_id, .. }) => file_id.into(), - ModuleDefOrigin::Library(InFile { file_id, .. }) => file_id.into(), - ModuleDefOrigin::Udp(InFile { file_id, .. }) => file_id.into(), - ModuleDefOrigin::BlockId(block_id) => block_id.lookup(db).cont_id, - ModuleDefOrigin::GenerateBlockId(generate_block_id) => { - generate_block_id.lookup(db).cont_id - } - ModuleDefOrigin::SubroutineId(subroutine_id) => subroutine_id.lookup(db).cont_id.into(), - ModuleDefOrigin::SubroutinePort(InSubroutine { subroutine, .. }) => { - ContainerId::SubroutineId(subroutine) - } - ModuleDefOrigin::NonAnsiPort(InModule { module_id, .. }) => module_id.into(), - ModuleDefOrigin::Decl(InContainer { cont_id, .. }) => cont_id, - ModuleDefOrigin::Typedef(InContainer { cont_id, .. }) => cont_id, - ModuleDefOrigin::Instance(InModule { module_id, .. }) => module_id.into(), - ModuleDefOrigin::Stmt(InContainer { cont_id, .. }) => cont_id, + pub fn container_id(self, db: &dyn HirDb) -> ScopeId { + match self.loc(db) { + DefLoc::Module(InFile { file_id, .. }) => file_id.into(), + DefLoc::Config(InFile { file_id, .. }) => file_id.into(), + DefLoc::Library(InFile { file_id, .. }) => file_id.into(), + DefLoc::Udp(InFile { file_id, .. }) => file_id.into(), + DefLoc::Block(block_id) => block_id.lookup(db).cont_id, + DefLoc::GenerateBlock(generate_block_id) => generate_block_id.lookup(db).cont_id, + DefLoc::Subroutine(subroutine_id) => subroutine_id.cont_id, + DefLoc::SubroutinePort(InSubroutine { subroutine, .. }) => { + ScopeId::Subroutine(subroutine.into()) + } + DefLoc::NonAnsiPort(InModule { module_id, .. }) => module_id.into(), + DefLoc::Decl(InContainer { cont_id, .. }) => cont_id, + DefLoc::Typedef(InContainer { cont_id, .. }) => cont_id, + DefLoc::Instance(InModule { module_id, .. }) => module_id.into(), + DefLoc::Stmt(InContainer { cont_id, .. }) => cont_id, } } - pub fn name(&self, db: &dyn HirDb) -> Option { - match *self { - ModuleDefOrigin::ModuleId(InFile { value, file_id }) => { + pub fn kind(self, db: &dyn HirDb) -> DefKind { + match self.loc(db) { + DefLoc::Module(module_id) => { + let file = db.hir_file(module_id.file_id); + match file.get(module_id.value).kind { + ModuleKind::Module => DefKind::Module, + ModuleKind::Interface => DefKind::Interface, + ModuleKind::Program => DefKind::Program, + ModuleKind::Package => DefKind::Package, + } + } + DefLoc::Config(_) => DefKind::Config, + DefLoc::Library(_) => DefKind::Library, + DefLoc::Udp(_) => DefKind::Udp, + DefLoc::Block(_) => DefKind::Block, + DefLoc::GenerateBlock(_) => DefKind::GenerateBlock, + DefLoc::Subroutine(_) => DefKind::Subroutine, + DefLoc::SubroutinePort(_) => DefKind::SubroutinePort, + DefLoc::NonAnsiPort(_) => DefKind::NonAnsiPort, + DefLoc::Decl(InContainer { value, cont_id }) => { + let container = cont_id.to_container(db); + let decl = container.get(value); + match decl.parent { + DeclaratorParent::PortDeclId(_) => DefKind::Port, + DeclaratorParent::StmtId(_) => DefKind::Variable, + DeclaratorParent::DeclarationId(declaration_id) => { + match container.get(declaration_id) { + Declaration::DataDecl(_) => DefKind::Variable, + Declaration::NetDecl(_) => DefKind::Net, + Declaration::ParamDecl(_) => DefKind::Param, + Declaration::GenvarDecl(_) => DefKind::Genvar, + Declaration::SpecparamDecl(_) => DefKind::Specparam, + } + } + } + } + DefLoc::Typedef(_) => DefKind::Typedef, + DefLoc::Instance(_) => DefKind::Instance, + DefLoc::Stmt(_) => DefKind::Stmt, + } + } + + pub fn name(self, db: &dyn HirDb) -> Option { + match self.loc(db) { + DefLoc::Module(InFile { value, file_id }) => { file_id.to_container(db).get(value).name.clone() } - ModuleDefOrigin::Config(InFile { value, file_id }) => { + DefLoc::Config(InFile { value, file_id }) => { file_id.to_container(db).get(value).name.clone() } - ModuleDefOrigin::Library(InFile { value, file_id }) => { + DefLoc::Library(InFile { value, file_id }) => { file_id.to_container(db).get(value).name.clone() } - ModuleDefOrigin::Udp(InFile { value, file_id }) => { + DefLoc::Udp(InFile { value, file_id }) => { file_id.to_container(db).get(value).name.clone() } - ModuleDefOrigin::BlockId(block_id) => { + DefLoc::Block(block_id) => { let BlockLoc { cont_id, src: InFile { value, file_id: _ } } = block_id.lookup(db); let cont = cont_id.to_container(db); value.hir(&cont, &cont_id.to_container_src_map(db))?.name.clone() } - ModuleDefOrigin::GenerateBlockId(generate_block_id) => { + DefLoc::GenerateBlock(generate_block_id) => { db.generate_block(generate_block_id).name.clone() } - ModuleDefOrigin::SubroutineId(subroutine_id) => { - db.subroutine(subroutine_id).name.clone() - } - ModuleDefOrigin::SubroutinePort(InSubroutine { subroutine, value }) => { + DefLoc::Subroutine(subroutine_id) => db.subroutine(subroutine_id).name.clone(), + DefLoc::SubroutinePort(InSubroutine { subroutine, value }) => { db.subroutine(subroutine).ports.get(value.0 as usize)?.name.clone() } - ModuleDefOrigin::NonAnsiPort(InModule { value, module_id }) => { + DefLoc::NonAnsiPort(InModule { value, module_id }) => { module_id.to_container(db).get(value).label.clone() } - ModuleDefOrigin::Decl(InContainer { value, cont_id }) => { + DefLoc::Decl(InContainer { value, cont_id }) => { cont_id.to_container(db).get(value).name.clone() } - ModuleDefOrigin::Typedef(InContainer { value, cont_id }) => { + DefLoc::Typedef(InContainer { value, cont_id }) => { cont_id.to_container(db).get(value).name.clone() } - ModuleDefOrigin::Instance(InModule { value, module_id }) => { + DefLoc::Instance(InModule { value, module_id }) => { module_id.to_container(db).get(value).name.clone() } - ModuleDefOrigin::Stmt(InContainer { value, cont_id }) => { + DefLoc::Stmt(InContainer { value, cont_id }) => { cont_id.to_container(db).get(value).label.clone() } } } - pub fn name_range(&self, db: &dyn HirDb) -> Option> { - match *self { - ModuleDefOrigin::ModuleId(InFile { value, file_id }) => { + pub fn name_range(self, db: &dyn HirDb) -> Option> { + match self.loc(db) { + DefLoc::Module(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(file_id, range)) } - ModuleDefOrigin::Config(InFile { value, file_id }) => { + DefLoc::Config(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(file_id, range)) } - ModuleDefOrigin::Library(InFile { value, file_id }) => { + DefLoc::Library(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(file_id, range)) } - ModuleDefOrigin::Udp(InFile { value, file_id }) => { + DefLoc::Udp(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(file_id, range)) } - ModuleDefOrigin::BlockId(block_id) => { + DefLoc::Block(block_id) => { let BlockLoc { src: InFile { value, file_id }, .. } = block_id.lookup(db); let range = value.name_range()?; Some(InFile::new(file_id, range)) } - ModuleDefOrigin::GenerateBlockId(generate_block_id) => { + DefLoc::GenerateBlock(generate_block_id) => { let GenerateBlockLoc { src: InFile { value, file_id }, .. } = generate_block_id.lookup(db); let range = value.name_range()?; Some(InFile::new(file_id, range)) } - ModuleDefOrigin::SubroutineId(subroutine_id) => { - let src = subroutine_id.lookup(db).src; + DefLoc::Subroutine(subroutine_id) => { + let src = subroutine_src(db, subroutine_id)?; Some(InFile::new(src.file_id, src.value.name_or_full_range())) } - ModuleDefOrigin::SubroutinePort(InSubroutine { subroutine, value }) => { - let src = subroutine.lookup(db).src; + DefLoc::SubroutinePort(InSubroutine { subroutine, value }) => { + let src = subroutine_src(db, subroutine)?; let tree = db.parse(src.file_id); let func = src.value.to_node(&tree)?; let ports = func @@ -185,65 +204,65 @@ impl ModuleDefOrigin { let range = declarator.name()?.text_range_in(declarator.syntax())?; Some(InFile::new(src.file_id, range)) } - ModuleDefOrigin::NonAnsiPort(InModule { value, module_id }) => { + DefLoc::NonAnsiPort(InModule { value, module_id }) => { let range = module_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(module_id.file_id, range)) } - ModuleDefOrigin::Decl(InContainer { value, cont_id }) => { + DefLoc::Decl(InContainer { value, cont_id }) => { let range = cont_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(cont_id.file_id(db).into(), range)) } - ModuleDefOrigin::Typedef(InContainer { value, cont_id }) => { + DefLoc::Typedef(InContainer { value, cont_id }) => { let range = cont_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(cont_id.file_id(db).into(), range)) } - ModuleDefOrigin::Instance(InModule { value, module_id }) => { + DefLoc::Instance(InModule { value, module_id }) => { let range = module_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(module_id.file_id, range)) } - ModuleDefOrigin::Stmt(InContainer { value, cont_id }) => { + DefLoc::Stmt(InContainer { value, cont_id }) => { let range = cont_id.to_container_src_map(db).get(value)?.name_range()?; Some(InFile::new(cont_id.file_id(db).into(), range)) } } } - pub fn range(&self, db: &dyn HirDb) -> Option> { - Some(match *self { - ModuleDefOrigin::ModuleId(InFile { value, file_id }) => { + pub fn range(self, db: &dyn HirDb) -> Option> { + Some(match self.loc(db) { + DefLoc::Module(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.range(); InFile::new(file_id, range) } - ModuleDefOrigin::Config(InFile { value, file_id }) => { + DefLoc::Config(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.range(); InFile::new(file_id, range) } - ModuleDefOrigin::Library(InFile { value, file_id }) => { + DefLoc::Library(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.range(); InFile::new(file_id, range) } - ModuleDefOrigin::Udp(InFile { value, file_id }) => { + DefLoc::Udp(InFile { value, file_id }) => { let range = file_id.to_container_src_map(db).get(value)?.range(); InFile::new(file_id, range) } - ModuleDefOrigin::BlockId(block_id) => { + DefLoc::Block(block_id) => { let BlockLoc { src: InFile { value, file_id }, .. } = block_id.lookup(db); let range = value.range(); InFile::new(file_id, range) } - ModuleDefOrigin::GenerateBlockId(generate_block_id) => { + DefLoc::GenerateBlock(generate_block_id) => { let GenerateBlockLoc { src: InFile { value, file_id }, .. } = generate_block_id.lookup(db); let range = value.range(); InFile::new(file_id, range) } - ModuleDefOrigin::SubroutineId(subroutine_id) => { - let src = subroutine_id.lookup(db).src; + DefLoc::Subroutine(subroutine_id) => { + let src = subroutine_src(db, subroutine_id)?; let range = src.value.range(); InFile::new(src.file_id, range) } - ModuleDefOrigin::SubroutinePort(InSubroutine { subroutine, value }) => { - let src = subroutine.lookup(db).src; + DefLoc::SubroutinePort(InSubroutine { subroutine, value }) => { + let src = subroutine_src(db, subroutine)?; let tree = db.parse(src.file_id); let func = src.value.to_node(&tree)?; let ports = func.prototype().port_list()?; @@ -255,23 +274,23 @@ impl ModuleDefOrigin { let range = port.syntax().text_range()?; InFile::new(src.file_id, range) } - ModuleDefOrigin::NonAnsiPort(InModule { value, module_id }) => { + DefLoc::NonAnsiPort(InModule { value, module_id }) => { let range = module_id.to_container_src_map(db).get(value)?.range(); InFile::new(module_id.file_id, range) } - ModuleDefOrigin::Decl(InContainer { value, cont_id }) => { + DefLoc::Decl(InContainer { value, cont_id }) => { let range = cont_id.to_container_src_map(db).get(value)?.range(); InFile::new(cont_id.file_id(db).into(), range) } - ModuleDefOrigin::Typedef(InContainer { value, cont_id }) => { + DefLoc::Typedef(InContainer { value, cont_id }) => { let range = cont_id.to_container_src_map(db).get(value)?.range(); InFile::new(cont_id.file_id(db).into(), range) } - ModuleDefOrigin::Instance(InModule { value, module_id }) => { + DefLoc::Instance(InModule { value, module_id }) => { let range = module_id.to_container_src_map(db).get(value)?.range(); InFile::new(module_id.file_id, range) } - ModuleDefOrigin::Stmt(InContainer { value, cont_id }) => { + DefLoc::Stmt(InContainer { value, cont_id }) => { let range = cont_id.to_container_src_map(db).get(value)?.range(); InFile::new(cont_id.file_id(db).into(), range) } @@ -280,25 +299,23 @@ impl ModuleDefOrigin { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ModuleDef { - origins: SmallVec<[ModuleDefOrigin; 3]>, -} +pub struct ModuleDef(pub SmallVec<[DefId; 3]>); impl ModuleDef { - pub fn from_origins(origins: impl IntoIterator) -> Option { - let mut origins = origins.into_iter().collect::>(); - origins.sort_unstable(); - origins.dedup(); + pub fn from_def_ids(def_ids: impl IntoIterator) -> Option { + let mut def_ids = def_ids.into_iter().collect::>(); + def_ids.sort_unstable(); + def_ids.dedup(); - (!origins.is_empty()).then_some(ModuleDef { origins }) + (!def_ids.is_empty()).then_some(ModuleDef(def_ids)) } - pub fn origins(&self) -> &[ModuleDefOrigin] { - &self.origins + pub fn def_ids(&self) -> &[DefId] { + &self.0 } - fn into_origins(self) -> SmallVec<[ModuleDefOrigin; 3]> { - self.origins + fn into_def_ids(self) -> SmallVec<[DefId; 3]> { + self.0 } } @@ -307,33 +324,32 @@ impl ModuleDef { pub struct ModuleDefId(pub salsa::InternId); impl ModuleDefId { - pub fn origins(self, db: &dyn HirDb) -> SmallVec<[ModuleDefOrigin; 3]> { - db.lookup_intern_module_def(self).into_origins() + pub fn origins(self, db: &dyn HirDb) -> SmallVec<[DefId; 3]> { + db.lookup_intern_module_def(self).into_def_ids() } - pub fn declaration_origin(self, db: &dyn HirDb) -> Option { + pub fn declaration_origin(self, db: &dyn HirDb) -> Option { let origins = self.origins(db); - if origins.iter().any(|origin| matches!(origin, ModuleDefOrigin::NonAnsiPort(_))) { - return origins.iter().copied().find(|origin| is_port_decl_origin(db, origin)).or_else( - || { - origins - .iter() - .copied() - .find(|origin| matches!(origin, ModuleDefOrigin::Decl(_))) - }, - ); + if origins.iter().any(|origin| origin.kind(db) == DefKind::NonAnsiPort) { + return origins + .iter() + .copied() + .find(|origin| is_port_decl_origin(db, *origin)) + .or_else(|| { + origins.iter().copied().find(|origin| matches!(origin.loc(db), DefLoc::Decl(_))) + }); } origins.first().copied() } - pub fn def_origins(self, db: &dyn HirDb) -> SmallVec<[ModuleDefOrigin; 2]> { + pub fn def_origins(self, db: &dyn HirDb) -> SmallVec<[DefId; 2]> { let origins = self.origins(db); - if origins.iter().any(|origin| matches!(origin, ModuleDefOrigin::NonAnsiPort(_))) { + if origins.iter().any(|origin| origin.kind(db) == DefKind::NonAnsiPort) { return origins .iter() .copied() - .filter(|origin| matches!(origin, ModuleDefOrigin::Decl(_))) + .filter(|origin| matches!(origin.loc(db), DefLoc::Decl(_))) .collect(); } @@ -341,14 +357,12 @@ impl ModuleDefId { } pub fn is_port(self, db: &dyn HirDb) -> bool { - self.origins(db).iter().any(|origin| match origin { - ModuleDefOrigin::NonAnsiPort(_) => true, - ModuleDefOrigin::Decl(_) => is_port_decl_origin(db, origin), - _ => false, + self.origins(db).iter().any(|origin| { + origin.kind(db) == DefKind::NonAnsiPort || is_port_decl_origin(db, *origin) }) } - pub fn container_id(self, db: &dyn HirDb) -> Option { + pub fn container_id(self, db: &dyn HirDb) -> Option { let origins = self.origins(db); let container_id = origins.first().map(|origin| origin.container_id(db))?; debug_assert! { @@ -358,8 +372,8 @@ impl ModuleDefId { } } -fn is_port_decl_origin(db: &dyn HirDb, origin: &ModuleDefOrigin) -> bool { - let ModuleDefOrigin::Decl(decl_id) = origin else { +fn is_port_decl_origin(db: &dyn HirDb, origin: DefId) -> bool { + let DefLoc::Decl(decl_id) = origin.loc(db) else { return false; }; matches!( diff --git a/crates/hir/src/hir_def.rs b/crates/hir/src/hir_def.rs index 4e2d50bb..9b14d009 100644 --- a/crates/hir/src/hir_def.rs +++ b/crates/hir/src/hir_def.rs @@ -14,12 +14,19 @@ pub mod typedef; use la_arena::{Arena, Idx, RawIdx}; use smol_str::{SmolStr, ToSmolStr}; -use syntax::{SyntaxToken, ast}; +use syntax::{SyntaxToken, TokenKind, ast}; pub type Ident = SmolStr; pub const DEFAULT_NAME: SmolStr = SmolStr::new_static("unnamed"); +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PackageImport { + pub package: Ident, + /// `None` represents `pkg::*`. + pub item: Option, +} + #[inline] pub fn lower_ident(ident: Option) -> Option { Some(ident?.value_text().to_smolstr()) @@ -38,6 +45,22 @@ pub(crate) fn lower_named_label_opt(label: Option) -> Option Vec { + import_decl + .items() + .children() + .filter_map(|item| { + let package = lower_ident_opt(item.package())?; + let item = item.item()?; + let item = + (item.kind() != TokenKind::STAR).then(|| lower_ident_opt(Some(item))).flatten(); + Some(PackageImport { package, item }) + }) + .collect() +} + macro alloc_idx_and_src($file_id:expr; $hir:expr => $arena:expr, $ast:expr => $src_map:expr $(,)?) {{ let idx = $arena.alloc($hir.into()); // HIR lowering can consume include-expanded AST nodes, but source maps only diff --git a/crates/hir/src/hir_def/aggregate.rs b/crates/hir/src/hir_def/aggregate.rs index f12acfec..6165b6d5 100644 --- a/crates/hir/src/hir_def/aggregate.rs +++ b/crates/hir/src/hir_def/aggregate.rs @@ -10,7 +10,7 @@ use utils::text_edit::TextRange; use super::{Ident, expr::data_ty::DataTy, lower_ident_opt}; use crate::{ - container::{ContainerId, InContainer}, + container::{InContainer, ScopeId}, source_map::{FromSourceAst, IsNamedSrc, IsSrc, SourceAst, ToAstNode, root_token_in}, }; @@ -40,7 +40,7 @@ pub type StructId = Idx; pub(crate) fn lower_struct_def( struct_ty: StructUnionType, - container_id: ContainerId, + container_id: ScopeId, mut lower_data_ty: impl FnMut(DataType) -> DataTy, ) -> StructDef { let kind = match struct_ty { diff --git a/crates/hir/src/hir_def/block.rs b/crates/hir/src/hir_def/block.rs index e7022626..9d475d53 100644 --- a/crates/hir/src/hir_def/block.rs +++ b/crates/hir/src/hir_def/block.rs @@ -33,7 +33,7 @@ use super::{ }; use crate::{ base_db::intern::Lookup, - container::{ContainerId, InFile}, + container::{InFile, ScopeId}, db::{HirDb, InternDb}, file::HirFileId, region_tree::{RegionTree, RegionTreeBuilder}, @@ -212,7 +212,7 @@ pub struct BlockId(pub salsa::InternId); #[derive(Debug, Hash, PartialEq, Eq, Clone)] pub struct BlockLoc { - pub cont_id: ContainerId, + pub cont_id: ScopeId, pub src: InFile, } @@ -235,7 +235,7 @@ impl_lower_declaration!(LowerBlockCtx<'_>, block, block_source_map); impl LowerBlockCtx<'_> { fn lower_struct_type(&mut self, struct_ty: ast::StructUnionType) -> StructId { - let container_id = ContainerId::BlockId(self.block_id); + let container_id = ScopeId::Block(self.block_id); let struct_def = lower_struct_def(struct_ty, container_id, |ty| self.expr_ctx().lower_data_ty(ty)); @@ -259,7 +259,7 @@ impl LowerBlockCtx<'_> { let lowered_ty = lower_typedef_data_ty( self, data_ty, - ContainerId::BlockId(self.block_id), + ScopeId::Block(self.block_id), |ctx, struct_ty| ctx.lower_struct_type(struct_ty), |ctx, ty| ctx.expr_ctx().lower_data_ty(ty), ); diff --git a/crates/hir/src/hir_def/file.rs b/crates/hir/src/hir_def/file.rs index 61d77714..3edd559e 100644 --- a/crates/hir/src/hir_def/file.rs +++ b/crates/hir/src/hir_def/file.rs @@ -14,6 +14,7 @@ use udp::{UdpDecl, UdpDeclId, UdpDeclSrc}; use utils::{define_enum_deriving_from, get::Get}; use super::{ + PackageImport, aggregate::{StructDef, StructId, StructSrc, lower_struct_def}, alloc_idx_and_src, block::{BlockInfo, BlockSrc, LocalBlockId}, @@ -26,17 +27,18 @@ use super::{ impl_lower_expr, timing_control::{EventExpr, EventExprSrc, impl_lower_event_expr}, }, - module::{LocalModuleId, ModuleInfo, ModuleSrc}, + lower_package_imports, + module::{LocalModuleId, ModuleInfo, ModuleKind, ModuleSrc}, proc::{LowerProc, LowerProcCtx, Proc, ProcId, ProcSrc}, stmt::{Stmt, StmtId, StmtSrc, impl_lower_stmt}, subroutine::{ - LocalSubroutineId, LowerSubroutineBodyCtx, Subroutine, SubroutineLoc, SubroutineSrc, - lower_subroutine, lower_subroutine_body, + LocalSubroutineId, LowerSubroutineBodyCtx, Subroutine, SubroutineSrc, lower_subroutine, + lower_subroutine_body, }, typedef::{Typedef, TypedefId, TypedefSrc, lower_typedef_data_ty}, }; use crate::{ - container::{ContainerId, InFile}, + container::{InContainer, ScopeId}, db::{HirDb, InternDb}, file::HirFileId, hir_def::lower_ident_opt, @@ -61,6 +63,7 @@ define_container! { library_decls: [LibraryDecl], library_includes: [LibraryInclude], subroutines: [Subroutine], + package_imports: [PackageImport], declarations: [Declaration], exprs: [Expr], @@ -175,7 +178,7 @@ impl LowerProc for LowerFileCtx<'_> { impl LowerFileCtx<'_> { fn lower_struct_type(&mut self, struct_ty: ast::StructUnionType) -> StructId { - let container_id = ContainerId::HirFileId(self.file_id); + let container_id = ScopeId::File(self.file_id); let struct_def = lower_struct_def(struct_ty, container_id, |ty| self.expr_ctx().lower_data_ty(ty)); @@ -198,7 +201,7 @@ impl LowerFileCtx<'_> { let lowered_ty = lower_typedef_data_ty( self, data_ty, - ContainerId::HirFileId(self.file_id), + ScopeId::File(self.file_id), |ctx, struct_ty| ctx.lower_struct_type(struct_ty), |ctx, ty| ctx.expr_ctx().lower_data_ty(ty), ); @@ -220,12 +223,7 @@ impl LowerFileCtx<'_> { func => self.file_source_map.subroutine_srcs, }; - let src = SubroutineSrc::from_ast(self.file_id, func); - let subroutine_def_id = self.db.intern_subroutine(SubroutineLoc { - cont_id: self.file_id.into(), - src: InFile::new(self.file_id, src), - local_id: local_subroutine_id, - }); + let subroutine_id = InContainer::new(self.file_id.into(), local_subroutine_id); if func.end().is_some() { let subroutine = &mut self.file.subroutines[local_subroutine_id]; @@ -233,7 +231,7 @@ impl LowerFileCtx<'_> { let mut ctx = LowerSubroutineBodyCtx { db: self.db, file_id: self.file_id, - subroutine_id: subroutine_def_id, + subroutine_id, subroutine, subroutine_source_map: &mut subroutine_source_map, region_tree: RegionTreeBuilder::new(), @@ -295,10 +293,11 @@ impl LowerFileCtx<'_> { let idx = match member { ModuleDeclaration(decl) => { let name = lower_ident_opt(decl.header().name()); + let kind = ModuleKind::from_ast(decl); alloc_idx_and_src! { self.file_id; - ModuleInfo { name } => self.file.modules, + ModuleInfo { name, kind } => self.file.modules, decl => self.file_source_map.module_srcs, } .into() @@ -314,6 +313,12 @@ impl LowerFileCtx<'_> { Some(id) => id.into(), None => continue, }, + PackageImportDeclaration(import_decl) => { + for import in lower_package_imports(import_decl) { + self.file.package_imports.alloc(import); + } + continue; + } UdpDeclaration(udp_decl) => self.lower_udp_decl(udp_decl).into(), ConfigDeclaration(config_decl) => self.lower_config_decl(config_decl).into(), _ => continue, diff --git a/crates/hir/src/hir_def/module.rs b/crates/hir/src/hir_def/module.rs index c8ceeb41..40f1ba91 100644 --- a/crates/hir/src/hir_def/module.rs +++ b/crates/hir/src/hir_def/module.rs @@ -28,7 +28,7 @@ use utils::{ }; use super::{ - HirData, Ident, + HirData, Ident, PackageImport, aggregate::{StructDef, StructId, StructSrc, lower_struct_def}, alloc_idx_and_src, block::{BlockInfo, BlockSrc, LocalBlockId}, @@ -42,18 +42,18 @@ use super::{ impl_lower_expr, timing_control::{EventExpr, EventExprSrc, impl_lower_event_expr}, }, - lower_ident_opt, + lower_ident_opt, lower_package_imports, proc::{LowerProc, LowerProcCtx, Proc, ProcId, ProcSrc}, stmt::{Stmt, StmtId, StmtSrc, impl_lower_stmt}, subroutine::{ - LocalSubroutineId, LowerSubroutineBodyCtx, Subroutine, SubroutineLoc, SubroutineSrc, - lower_subroutine, lower_subroutine_body, + LocalSubroutineId, LowerSubroutineBodyCtx, Subroutine, SubroutineSrc, lower_subroutine, + lower_subroutine_body, }, ty::NetKind, typedef::{Typedef, TypedefId, TypedefSrc, lower_typedef_data_ty}, }; use crate::{ - container::{ContainerId, InFile}, + container::{InContainer, InFile, ScopeId}, db::{HirDb, InternDb}, file::HirFileId, region_tree::{RegionTree, RegionTreeBuilder}, @@ -91,6 +91,7 @@ define_container! { typedefs: [Typedef], structs: [StructDef], subroutines: [Subroutine], + package_imports: [PackageImport], instantiations: [Instantiation], inst_param_assigns: [ParamAssign], @@ -300,13 +301,38 @@ define_enum_deriving_from! { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)] +pub enum ModuleKind { + #[default] + Module, + Interface, + Program, + Package, +} + +impl ModuleKind { + pub fn from_ast(decl: ast::ModuleDeclaration) -> Self { + if decl.as_package_declaration().is_some() { + ModuleKind::Package + } else if decl.as_interface_declaration().is_some() { + ModuleKind::Interface + } else if decl.as_program_declaration().is_some() { + ModuleKind::Program + } else { + ModuleKind::Module + } + } +} + #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct ModuleInfo { pub name: Option, + pub kind: ModuleKind, } pub type LocalModuleId = Idx; pub type ModuleId = InFile; +pub type PackageId = ModuleId; pub(crate) struct LowerModuleCtx<'a> { pub(crate) db: &'a dyn InternDb, @@ -355,7 +381,7 @@ impl LowerProc for LowerModuleCtx<'_> { impl LowerModuleCtx<'_> { fn lower_struct_type(&mut self, struct_ty: ast::StructUnionType) -> StructId { - let container_id = ContainerId::ModuleId(self.module_id); + let container_id = ScopeId::Module(self.module_id); let struct_def = lower_struct_def(struct_ty, container_id, |ty| self.expr_ctx().lower_data_ty(ty)); @@ -379,7 +405,7 @@ impl LowerModuleCtx<'_> { let lowered_ty = lower_typedef_data_ty( self, data_ty, - ContainerId::ModuleId(self.module_id), + ScopeId::Module(self.module_id), |ctx, struct_ty| ctx.lower_struct_type(struct_ty), |ctx, ty| ctx.expr_ctx().lower_data_ty(ty), ); @@ -401,12 +427,7 @@ impl LowerModuleCtx<'_> { func => self.module_source_map.subroutine_srcs, }; - let src = SubroutineSrc::from_ast(self.file_id, func); - let subroutine_def_id = self.db.intern_subroutine(SubroutineLoc { - cont_id: self.module_id.into(), - src: InFile::new(self.file_id, src), - local_id: subroutine_id, - }); + let subroutine_def_id = InContainer::new(self.module_id.into(), subroutine_id); if func.end().is_some() { let subroutine = &mut self.module.subroutines[subroutine_id]; @@ -519,7 +540,12 @@ impl LowerModuleCtx<'_> { ExplicitAnsiPort(_) | ImplicitAnsiPort(_) => continue, // Imports - PackageImportDeclaration(_) => continue, + PackageImportDeclaration(import_decl) => { + for import in lower_package_imports(import_decl) { + self.module.package_imports.alloc(import); + } + continue; + } // Aggregates ClassDeclaration(_) => continue, @@ -643,7 +669,8 @@ pub(crate) fn module_with_source_map_query( let (file, file_source_map) = db.hir_file_with_source_map(file_id); let tree = db.parse(file_id); - let mut module = Module { name: file.get(local_module_id).name.clone(), ..Default::default() }; + let module_info = file.get(local_module_id); + let mut module = Module { name: module_info.name.clone(), ..Default::default() }; let mut module_source_map = ModuleSourceMap::default(); let Some(ast_module) = file_source_map.get(local_module_id).and_then(|src| src.to_node(&tree)) diff --git a/crates/hir/src/hir_def/module/generate.rs b/crates/hir/src/hir_def/module/generate.rs index 78ee6b5f..7f6af352 100644 --- a/crates/hir/src/hir_def/module/generate.rs +++ b/crates/hir/src/hir_def/module/generate.rs @@ -24,7 +24,7 @@ use super::{ }; use crate::{ base_db::intern::Lookup, - container::{ContainerId, InFile}, + container::{InContainer, InFile, ScopeId}, db::{HirDb, InternDb}, file::HirFileId, hir_def::{ @@ -44,8 +44,8 @@ use crate::{ proc::{LowerProc, LowerProcCtx, Proc, ProcId, ProcSrc}, stmt::{Stmt, StmtId, StmtSrc, impl_lower_stmt}, subroutine::{ - LocalSubroutineId, LowerSubroutineBodyCtx, Subroutine, SubroutineLoc, SubroutineSrc, - lower_subroutine, lower_subroutine_body, + LocalSubroutineId, LowerSubroutineBodyCtx, Subroutine, SubroutineSrc, lower_subroutine, + lower_subroutine_body, }, typedef::{Typedef, TypedefId, TypedefSrc, lower_typedef_data_ty}, }, @@ -397,7 +397,7 @@ pub struct GenerateBlockId(pub salsa::InternId); #[derive(Debug, Hash, PartialEq, Eq, Clone)] pub struct GenerateBlockLoc { - pub cont_id: ContainerId, + pub cont_id: ScopeId, pub src: InFile, } @@ -453,7 +453,7 @@ impl LowerProc for LowerGenerateBlockCtx<'_> { impl LowerGenerateBlockCtx<'_> { fn lower_struct_type(&mut self, struct_ty: ast::StructUnionType) -> StructId { - let container_id = ContainerId::GenerateBlockId(self.generate_block_id); + let container_id = ScopeId::GenerateBlock(self.generate_block_id); let struct_def = lower_struct_def(struct_ty, container_id, |ty| self.expr_ctx().lower_data_ty(ty)); @@ -477,7 +477,7 @@ impl LowerGenerateBlockCtx<'_> { let lowered_ty = lower_typedef_data_ty( self, data_ty, - ContainerId::GenerateBlockId(self.generate_block_id), + ScopeId::GenerateBlock(self.generate_block_id), |ctx, struct_ty| ctx.lower_struct_type(struct_ty), |ctx, ty| ctx.expr_ctx().lower_data_ty(ty), ); @@ -499,12 +499,7 @@ impl LowerGenerateBlockCtx<'_> { func => self.generate_block_source_map.subroutine_srcs, }; - let src = SubroutineSrc::from_ast(self.file_id, func); - let subroutine_def_id = self.db.intern_subroutine(SubroutineLoc { - cont_id: self.generate_block_id.into(), - src: InFile::new(self.file_id, src), - local_id: subroutine_id, - }); + let subroutine_def_id = InContainer::new(self.generate_block_id.into(), subroutine_id); if func.end().is_some() { let subroutine = &mut self.generate_block.subroutines[subroutine_id]; diff --git a/crates/hir/src/hir_def/proc.rs b/crates/hir/src/hir_def/proc.rs index 670feb7a..fe066216 100644 --- a/crates/hir/src/hir_def/proc.rs +++ b/crates/hir/src/hir_def/proc.rs @@ -6,7 +6,7 @@ use super::{ stmt::impl_lower_stmt, }; use crate::{ - container::ContainerId, + container::ScopeId, db::InternDb, file::HirFileId, hir_def::{ @@ -62,7 +62,7 @@ pub(crate) trait LowerProc: LowerStmt { pub(crate) struct LowerProcCtx<'a> { pub(crate) db: &'a dyn InternDb, pub(crate) file_id: HirFileId, - pub(crate) cont_id: ContainerId, + pub(crate) cont_id: ScopeId, pub(crate) procs: &'a mut Arena, pub(crate) proc_srcs: &'a mut SourceMap, diff --git a/crates/hir/src/hir_def/stmt.rs b/crates/hir/src/hir_def/stmt.rs index e65822a9..675161c6 100644 --- a/crates/hir/src/hir_def/stmt.rs +++ b/crates/hir/src/hir_def/stmt.rs @@ -20,7 +20,7 @@ use super::{ lower_ident_opt, }; use crate::{ - container::{ContainerId, InFile}, + container::{InFile, ScopeId}, db::InternDb, file::HirFileId, hir_def::{alloc_idx_and_src, lower_named_label_opt}, @@ -180,7 +180,7 @@ pub(in crate::hir_def) macro impl_lower_stmt { pub(crate) struct LowerStmtCtx<'a> { pub(crate) db: &'a dyn InternDb, pub(crate) file_id: HirFileId, - pub(crate) cont_id: ContainerId, + pub(crate) cont_id: ScopeId, pub(crate) stmts: &'a mut Arena, pub(crate) stmt_srcs: &'a mut SourceMap, diff --git a/crates/hir/src/hir_def/subroutine.rs b/crates/hir/src/hir_def/subroutine.rs index 9f30953f..859fb70e 100644 --- a/crates/hir/src/hir_def/subroutine.rs +++ b/crates/hir/src/hir_def/subroutine.rs @@ -25,13 +25,11 @@ use super::{ timing_control::{EventExpr, EventExprSrc}, }, lower_ident_opt, - module::{ModuleId, generate::GenerateBlockId}, stmt::{LowerStmt, Stmt, StmtId, StmtSrc, impl_lower_stmt}, typedef::{Typedef, TypedefId, TypedefSrc, lower_typedef_data_ty}, }; use crate::{ - base_db::intern::Lookup, - container::{ContainerId, InFile}, + container::{InContainer, ScopeId}, db::{HirDb, InternDb}, file::HirFileId, hir_def::{ @@ -141,64 +139,6 @@ pub type SubroutineSrc = NamedAstId; pub type LocalSubroutineId = Idx; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct SubroutineId(pub salsa::InternId); - -#[derive(Debug, Hash, PartialEq, Eq, Clone)] -pub struct SubroutineLoc { - pub cont_id: SubroutineContainerId, - pub src: InFile, - pub local_id: LocalSubroutineId, -} - -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -pub enum SubroutineContainerId { - HirFileId(HirFileId), - ModuleId(ModuleId), - GenerateBlockId(GenerateBlockId), -} - -impl From for SubroutineContainerId { - fn from(file_id: HirFileId) -> Self { - Self::HirFileId(file_id) - } -} - -impl From for SubroutineContainerId { - fn from(module_id: ModuleId) -> Self { - Self::ModuleId(module_id) - } -} - -impl From for SubroutineContainerId { - fn from(generate_block_id: GenerateBlockId) -> Self { - Self::GenerateBlockId(generate_block_id) - } -} - -impl From for ContainerId { - fn from(cont_id: SubroutineContainerId) -> Self { - match cont_id { - SubroutineContainerId::HirFileId(file_id) => file_id.into(), - SubroutineContainerId::ModuleId(module_id) => module_id.into(), - SubroutineContainerId::GenerateBlockId(generate_block_id) => generate_block_id.into(), - } - } -} - -impl TryFrom for SubroutineContainerId { - type Error = (); - - fn try_from(cont_id: ContainerId) -> Result { - match cont_id { - ContainerId::HirFileId(file_id) => Ok(file_id.into()), - ContainerId::ModuleId(module_id) => Ok(module_id.into()), - ContainerId::GenerateBlockId(generate_block_id) => Ok(generate_block_id.into()), - ContainerId::BlockId(_) | ContainerId::SubroutineId(_) => Err(()), - } - } -} - pub fn lower_subroutine(func: &ast::FunctionDeclaration, mut lower_ty: F) -> Option where F: FnMut(ast::DataType) -> DataTy, @@ -266,7 +206,7 @@ fn map_direction(kind: Option) -> SubroutinePortDir { pub struct LowerSubroutineBodyCtx<'a> { pub(crate) db: &'a dyn InternDb, pub(crate) file_id: HirFileId, - pub(crate) subroutine_id: SubroutineId, + pub(crate) subroutine_id: InContainer, pub(crate) subroutine: &'a mut Subroutine, pub(crate) subroutine_source_map: &'a mut SubroutineSourceMap, pub(crate) region_tree: RegionTreeBuilder, @@ -279,8 +219,8 @@ impl_lower_stmt!(LowerSubroutineBodyCtx<'_>, subroutine_id, subroutine, subrouti impl_lower_declaration!(LowerSubroutineBodyCtx<'_>, subroutine, subroutine_source_map); impl LowerSubroutineBodyCtx<'_> { - fn container_id(&self) -> ContainerId { - ContainerId::SubroutineId(self.subroutine_id) + fn container_id(&self) -> ScopeId { + self.subroutine_id.into() } fn lower_struct_type(&mut self, struct_ty: ast::StructUnionType) -> StructId { @@ -391,28 +331,29 @@ pub fn lower_subroutine_body(ctx: &mut LowerSubroutineBodyCtx<'_>, func: ast::Fu pub(crate) fn subroutine_with_source_map_query( db: &dyn HirDb, - subroutine_id: SubroutineId, + subroutine_id: InContainer, ) -> (Arc, Arc) { - let SubroutineLoc { cont_id, local_id, .. } = subroutine_id.lookup(db); - - match cont_id { - SubroutineContainerId::HirFileId(file_id) => { + match subroutine_id.cont_id { + ScopeId::File(file_id) => { let file = db.hir_file(file_id); - let subroutine = file.subroutines[local_id].clone(); + let subroutine = file.subroutines[subroutine_id.value].clone(); let source_map = subroutine.source_map.clone(); (Arc::new(subroutine), Arc::new(source_map)) } - SubroutineContainerId::ModuleId(module_id) => { + ScopeId::Module(module_id) => { let module = db.module(module_id); - let subroutine = module.subroutines[local_id].clone(); + let subroutine = module.subroutines[subroutine_id.value].clone(); let source_map = subroutine.source_map.clone(); (Arc::new(subroutine), Arc::new(source_map)) } - SubroutineContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::GenerateBlock(generate_block_id) => { let generate_block = db.generate_block(generate_block_id); - let subroutine = generate_block.subroutines[local_id].clone(); + let subroutine = generate_block.subroutines[subroutine_id.value].clone(); let source_map = subroutine.source_map.clone(); (Arc::new(subroutine), Arc::new(source_map)) } + ScopeId::Block(_) | ScopeId::Subroutine(_) => { + unreachable!("subroutines are lowered only in file, module, or generate-block scopes") + } } } diff --git a/crates/hir/src/hir_def/typedef.rs b/crates/hir/src/hir_def/typedef.rs index 4e3088f9..5800e5e2 100644 --- a/crates/hir/src/hir_def/typedef.rs +++ b/crates/hir/src/hir_def/typedef.rs @@ -9,7 +9,7 @@ use utils::text_edit::TextRange; use super::{Ident, aggregate::StructId, expr::data_ty::DataTy}; use crate::{ - container::{ContainerId, InContainer}, + container::{InContainer, ScopeId}, source_map::{FromSourceAst, IsNamedSrc, IsSrc, SourceAst, ToAstNode, root_token_in}, }; @@ -92,7 +92,7 @@ impl TypedefSrc { pub(crate) fn lower_typedef_data_ty( ctx: &mut Ctx, data_ty: ast::DataType, - container_id: ContainerId, + container_id: ScopeId, mut lower_struct_type: impl FnMut(&mut Ctx, ast::StructUnionType) -> StructId, mut lower_data_ty: impl FnMut(&mut Ctx, ast::DataType) -> DataTy, ) -> DataTy { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5e85aa03..0643085d 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -13,4 +13,5 @@ pub mod region_tree; pub mod scope; pub mod semantics; pub mod source_map; +pub mod symbol; pub mod type_infer; diff --git a/crates/hir/src/scope.rs b/crates/hir/src/scope.rs index 9e962e65..d7d5453a 100644 --- a/crates/hir/src/scope.rs +++ b/crates/hir/src/scope.rs @@ -1,334 +1,177 @@ -use rustc_hash::FxHashMap; -use smallvec::SmallVec; +use la_arena::{Idx, RawIdx}; use smol_str::SmolStr; +use syntax::ast; use triomphe::Arc; -use utils::{ - define_enum_deriving_from, - get::{Get, GetRef}, -}; +use utils::get::{Get, GetRef}; use crate::{ - container::InFile, + container::{InContainer, InFile, InModule, InSubroutine}, db::HirDb, file::HirFileId, hir_def::{ - Ident, - block::{BlockId, BlockInfo}, + PackageImport, + block::BlockInfo, + declaration::DeclarationId, expr::declarator::{DeclId, DeclaratorParent}, - file::{config::ConfigDeclId, library::LibraryDeclId, udp::UdpDeclId}, + lower_ident_opt, module::{ - Module, ModuleId, + Module, ModuleKind, PackageId, generate::GenerateBlockId, - instantiation::InstanceId, - port::{NonAnsiPortId, PortDeclId, Ports}, + port::{PortDeclId, Ports}, }, - stmt::{StmtId, StmtKind}, - subroutine::{SubroutineId, SubroutineLoc, SubroutinePortId}, + stmt::StmtKind, + subroutine::{LocalSubroutineId, SubroutinePortId}, typedef::TypedefId, }, + source_map::ToAstNode, + symbol::{DefId, DefLoc, Import, NameScope}, }; -define_enum_deriving_from! { - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub enum UnitEntry { - ModuleId, - FiledConfigDeclId, - FiledLibraryDeclId, - FiledUdpDeclId, - FiledDeclId, - FiledTypedefId, - } -} - -pub type FiledDeclId = InFile; -pub type FiledConfigDeclId = InFile; -pub type FiledLibraryDeclId = InFile; -pub type FiledUdpDeclId = InFile; -pub type FiledTypedefId = InFile; - -define_enum_deriving_from! { - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub enum ModuleEntry { - DeclId, - TypedefId, - GenerateBlockId, - NonAnsiPortEntry, - AnsiPortEntry, - InstanceId, - StmtId, - BlockId, - SubroutineId, - } -} - -define_enum_deriving_from! { - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] - pub enum GenerateBlockEntry { - DeclId, - TypedefId, - GenerateBlockId, - StmtId, - BlockId, - SubroutineId, - } -} +// SystemVerilog has separate namespaces. This scope stores current supported +// declarations as: +// - types: modules, interfaces, packages, programs, typedefs +// - values: nets, variables, params, ports, subroutines, instances, blocks +// - assertions: reserved for sequence/property/checker work +// Hierarchical lookup remains a separate resolver path. -#[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct NonAnsiPortEntry { - // explicit label for port - pub label: Option, - pub port_decl: Option, - pub data_decl: Option, +fn def_id(db: &dyn HirDb, loc: impl Into) -> DefId { + DefId::new(db, loc) } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct AnsiPortEntry(pub DeclId); - -define_enum_deriving_from! { - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] - pub enum BlockEntry { - StmtId, - DeclId, - TypedefId, - BlockId, - } -} +impl NameScope { + pub fn unit_scope_query(db: &dyn HirDb) -> Arc { + let mut scope = NameScope::default(); -define_enum_deriving_from! { - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] - pub enum SubroutineEntry { - StmtId, - DeclId, - TypedefId, - BlockId, - SubroutinePortId, - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Scope { - entries: FxHashMap, -} - -impl Default for Scope { - fn default() -> Self { - Scope { entries: FxHashMap::default() } - } -} - -impl Scope { - pub(crate) fn insert(&mut self, ident: &Ident, entry: Entry) -> Option { - self.entries.insert(ident.clone(), entry) - } - - pub(crate) fn insert_opt(&mut self, ident: &Option, entry: Entry) -> Option { - self.insert(ident.as_ref()?, entry) - } - - pub fn get(&self, ident: &Ident) -> Option { - self.entries.get(ident).copied() - } - - pub fn iter(&self) -> impl Iterator + '_ { - self.entries.iter().map(|(k, v)| (k, *v)) - } - - pub(crate) fn get_mut(&mut self, ident: &Ident) -> Option<&mut Entry> { - self.entries.get_mut(ident) - } -} - -pub type ModuleScope = Scope; -pub type GenerateBlockScope = Scope; -pub type BlockScope = Scope; -pub type SubroutineScope = Scope; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum ScopeResolution { - Unique(Entry), - Ambiguous(SmallVec<[Entry; 2]>), - Unresolved, -} - -impl ScopeResolution { - pub fn unique(self) -> Option { - match self { - ScopeResolution::Unique(entry) => Some(entry), - ScopeResolution::Ambiguous(_) | ScopeResolution::Unresolved => None, + for file_id in db.files().iter() { + let file_id = HirFileId::File(*file_id); + let file_scope = db.file_scope(file_id); + scope.extend_defs_from(&file_scope); } - } -} -#[derive(Debug, PartialEq, Eq, Clone, Default)] -pub struct UnitScope { - entries: FxHashMap>, -} - -impl UnitScope { - fn insert(&mut self, ident: &Ident, entry: UnitEntry) { - self.entries.entry(ident.clone()).or_default().push(entry); - } - - fn insert_opt(&mut self, ident: &Option, entry: UnitEntry) { - if let Some(ident) = ident { - self.insert(ident, entry); - } - } - - pub fn resolve(&self, ident: &Ident) -> ScopeResolution { - match self.entries.get(ident).map(SmallVec::as_slice) { - Some([entry]) => ScopeResolution::Unique(*entry), - Some(entries) => ScopeResolution::Ambiguous(SmallVec::from_slice(entries)), - None => ScopeResolution::Unresolved, - } + Arc::new(scope) } - pub fn get(&self, ident: &Ident) -> Option { - self.resolve(ident).unique() + pub fn package_export_scope_query(db: &dyn HirDb, package_id: PackageId) -> Arc { + db.package_export_signature(package_id) } - pub fn resolve_module(&self, ident: &Ident) -> ScopeResolution { - let entries = self - .entries - .get(ident) - .into_iter() - .flat_map(|entries| entries.iter()) - .filter_map(|entry| match entry { - UnitEntry::ModuleId(module_id) => Some(*module_id), - UnitEntry::FiledConfigDeclId(_) - | UnitEntry::FiledLibraryDeclId(_) - | UnitEntry::FiledUdpDeclId(_) - | UnitEntry::FiledDeclId(_) - | UnitEntry::FiledTypedefId(_) => None, - }) - .collect::>(); - - match entries.as_slice() { - [module_id] => ScopeResolution::Unique(*module_id), - [] => ScopeResolution::Unresolved, - _ => ScopeResolution::Ambiguous(entries), + pub fn package_export_signature_query(db: &dyn HirDb, package_id: PackageId) -> Arc { + let mut scope = NameScope::default(); + let file_id = HirFileId::File(package_id.file_id()); + let (file, file_source_map) = db.hir_file_with_source_map(file_id); + if file.get(package_id.value).kind != ModuleKind::Package { + return Arc::new(scope); } - } - pub fn iter(&self) -> impl Iterator + '_ { - self.entries - .iter() - .flat_map(|(ident, entries)| entries.iter().map(move |entry| (ident, *entry))) - } -} - -// TODO: diagnostics - -impl UnitScope { - pub fn unit_scope_query(db: &dyn HirDb) -> Arc { - let mut scope = UnitScope::default(); + let tree = db.parse(file_id); + let Some(package) = + file_source_map.get(package_id.value).and_then(|src| src.to_node(&tree)) + else { + return Arc::new(scope); + }; - for file_id in db.files().iter() { - let file_id = HirFileId::File(*file_id); - let file_scope = db.file_scope(file_id); - for (ident, entry) in file_scope.iter() { - scope.insert(ident, entry); - } - } + let mut builder = PackageExportSignatureBuilder { + db, + package_id, + scope: &mut scope, + next_declaration: 0, + next_decl: 0, + next_typedef: 0, + next_subroutine: 0, + }; + builder.collect(package); Arc::new(scope) } - pub(super) fn file_scope_query(db: &dyn HirDb, file_id: HirFileId) -> Arc { - let mut scope = UnitScope::default(); + pub(super) fn file_scope_query(db: &dyn HirDb, file_id: HirFileId) -> Arc { + let mut scope = NameScope::default(); let hir_file = db.hir_file(file_id); for (module_id, module_info) in hir_file.modules.iter() { - scope.insert_opt(&module_info.name, InFile::new(file_id, module_id).into()); + scope.insert_type_opt(&module_info.name, def_id(db, InFile::new(file_id, module_id))); + } + + for (_, import) in hir_file.package_imports.iter() { + scope.insert_package_import(import); } for (decl_id, decl) in hir_file.decls.iter() { - scope.insert_opt(&decl.name, InFile::new(file_id, decl_id).into()); + scope.insert_value_opt( + &decl.name, + def_id(db, InContainer::new(file_id.into(), decl_id)), + ); } for (config_decl_id, config_decl) in hir_file.config_decls.iter() { - scope.insert_opt(&config_decl.name, InFile::new(file_id, config_decl_id).into()); + scope.insert_value_opt( + &config_decl.name, + def_id(db, InFile::new(file_id, config_decl_id)), + ); } for (udp_decl_id, udp_decl) in hir_file.udp_decls.iter() { - scope.insert_opt(&udp_decl.name, InFile::new(file_id, udp_decl_id).into()); + scope.insert_value_opt(&udp_decl.name, def_id(db, InFile::new(file_id, udp_decl_id))); } for (library_decl_id, library_decl) in hir_file.library_decls.iter() { - scope.insert_opt(&library_decl.name, InFile::new(file_id, library_decl_id).into()); + scope.insert_value_opt( + &library_decl.name, + def_id(db, InFile::new(file_id, library_decl_id)), + ); } for (typedef_id, typedef) in hir_file.typedefs.iter() { - scope.insert_opt(&typedef.name, InFile::new(file_id, typedef_id).into()); + scope.insert_type_opt( + &typedef.name, + def_id(db, InContainer::new(file_id.into(), typedef_id)), + ); } Arc::new(scope) } -} -impl ModuleScope { - pub fn module_scope_query(db: &dyn HirDb, module_id: ModuleId) -> Arc { - let mut scope = Scope::default(); + pub fn module_scope_query( + db: &dyn HirDb, + module_id: crate::hir_def::module::ModuleId, + ) -> Arc { + let mut scope = NameScope::default(); let (module, module_src_map) = db.module_with_source_map(module_id); - let file_id = HirFileId::File(module_id.file_id()); - // handle labels of non-ansi ports if let Ports::NonAnsi { ports, .. } = &module.ports { for (port_id, port) in ports.iter() { - let entry = NonAnsiPortEntry { label: Some(port_id), ..Default::default() }.into(); - scope.insert_opt(&port.label, entry); + scope.insert_value_opt(&port.label, def_id(db, InModule::new(module_id, port_id))); } } - for (local_subroutine_id, subroutine) in module.subroutines.iter() { - let Some(src) = module_src_map.get(local_subroutine_id) else { - continue; - }; - let subroutine_id = db.intern_subroutine(SubroutineLoc { - cont_id: module_id.into(), - src: InFile::new(file_id, src), - local_id: local_subroutine_id, - }); - scope.insert_opt(&subroutine.name, subroutine_id.into()); - } - - // handle other members - for (decl_id, decl) in module.decls.iter() { - let Some(name) = &decl.name else { - continue; - }; - - let is_port_decl = matches!(decl.parent, DeclaratorParent::PortDeclId(_)); - - if let Some(ModuleEntry::NonAnsiPortEntry(entry)) = scope.get_mut(name) { - if is_port_decl { - entry.port_decl = Some(decl_id); - } else { - entry.data_decl = Some(decl_id); - } - continue; - } + for (_, import) in module.package_imports.iter() { + scope.insert_package_import(import); + } - let entry = if is_port_decl { - match module.ports { - Ports::NonAnsi { .. } => { - NonAnsiPortEntry { port_decl: Some(decl_id), ..Default::default() }.into() - } - Ports::Ansi(_) => AnsiPortEntry(decl_id).into(), - } - } else { - decl_id.into() - }; + for (local_subroutine_id, subroutine) in module.subroutines.iter() { + let subroutine_id = InContainer::new(module_id.into(), local_subroutine_id); + scope.insert_value_opt(&subroutine.name, def_id(db, subroutine_id)); + } - scope.insert(name, entry); + for (decl_id, decl) in module.decls.iter() { + scope.insert_value_opt( + &decl.name, + def_id(db, InContainer::new(module_id.into(), decl_id)), + ); } for (typedef_id, typedef) in module.typedefs.iter() { - scope.insert_opt(&typedef.name, typedef_id.into()); + scope.insert_type_opt( + &typedef.name, + def_id(db, InContainer::new(module_id.into(), typedef_id)), + ); } for (instance_id, instance) in module.instances.iter() { - scope.insert_opt(&instance.name, instance_id.into()); + scope.insert_value_opt( + &instance.name, + def_id(db, InModule::new(module_id, instance_id)), + ); } for item in &module_src_map.items { @@ -340,69 +183,52 @@ impl ModuleScope { ) = *item { let generate_block = db.generate_block(generate_block_id); - scope.insert_opt(&generate_block.name, generate_block_id.into()); + scope.insert_value_opt(&generate_block.name, def_id(db, generate_block_id)); } } } } for (stmt_id, stmt) in module.stmts.iter() { - scope.insert_opt(&stmt.label, stmt_id.into()); + scope.insert_value_opt( + &stmt.label, + def_id(db, InContainer::new(module_id.into(), stmt_id)), + ); if let StmtKind::Block(BlockInfo { name, block_id }) = &stmt.kind { - scope.insert_opt(name, (*block_id).into()); + scope.insert_value_opt(name, def_id(db, *block_id)); } } Arc::new(scope) } - pub fn non_ansi_port_decl_id_by_name( - &self, - module: &Module, - name: &SmolStr, - ) -> Option { - let ModuleEntry::NonAnsiPortEntry(NonAnsiPortEntry { port_decl, .. }) = self.get(name)? - else { - return None; - }; - let decl = module.get(port_decl?); - let DeclaratorParent::PortDeclId(port_decl_id) = decl.parent else { - return None; - }; - Some(port_decl_id) - } -} - -impl GenerateBlockScope { pub fn generate_block_scope_query( db: &dyn HirDb, generate_block_id: GenerateBlockId, - ) -> Arc { - let mut scope = Scope::default(); - let (generate_block, source_map) = db.generate_block_with_source_map(generate_block_id); - let file_id = HirFileId::File(generate_block_id.file_id(db)); + ) -> Arc { + let mut scope = NameScope::default(); + let (generate_block, _) = db.generate_block_with_source_map(generate_block_id); - scope.insert_opt(&generate_block.name, generate_block_id.into()); + scope.insert_value_opt(&generate_block.name, def_id(db, generate_block_id)); for (local_subroutine_id, subroutine) in generate_block.subroutines.iter() { - let Some(src) = source_map.get(local_subroutine_id) else { - continue; - }; - let subroutine_id = db.intern_subroutine(SubroutineLoc { - cont_id: generate_block_id.into(), - src: InFile::new(file_id, src), - local_id: local_subroutine_id, - }); - scope.insert_opt(&subroutine.name, subroutine_id.into()); + let subroutine_id = InContainer::new(generate_block_id.into(), local_subroutine_id); + scope.insert_value_opt(&subroutine.name, def_id(db, subroutine_id)); } for (decl_id, decl) in generate_block.decls.iter() { - scope.insert_opt(&decl.name, decl_id.into()); + scope.insert_value_opt( + &decl.name, + def_id(db, InContainer::new(generate_block_id.into(), decl_id)), + ); } for (typedef_id, typedef) in generate_block.typedefs.iter() { - scope.insert_opt(&typedef.name, typedef_id.into()); + scope.insert_type_opt( + &typedef.name, + def_id(db, InContainer::new(generate_block_id.into(), typedef_id)), + ); } for item in &generate_block.items { @@ -410,73 +236,709 @@ impl GenerateBlockScope { *item { let child = db.generate_block(child_id); - scope.insert_opt(&child.name, child_id.into()); + scope.insert_value_opt(&child.name, def_id(db, child_id)); } } for (stmt_id, stmt) in generate_block.stmts.iter() { - scope.insert_opt(&stmt.label, stmt_id.into()); + scope.insert_value_opt( + &stmt.label, + def_id(db, InContainer::new(generate_block_id.into(), stmt_id)), + ); if let StmtKind::Block(BlockInfo { name, block_id }) = &stmt.kind { - scope.insert_opt(name, (*block_id).into()); + scope.insert_value_opt(name, def_id(db, *block_id)); } } Arc::new(scope) } -} -impl BlockScope { - pub fn block_scope_query(db: &dyn HirDb, block_id: BlockId) -> Arc { - let mut scope = Scope::default(); + pub fn block_scope_query( + db: &dyn HirDb, + block_id: crate::hir_def::block::BlockId, + ) -> Arc { + let mut scope = NameScope::default(); let block = db.block(block_id); for (decl_id, decl) in block.decls.iter() { - scope.insert_opt(&decl.name, decl_id.into()); + scope.insert_value_opt( + &decl.name, + def_id(db, InContainer::new(block_id.into(), decl_id)), + ); } for (typedef_id, typedef) in block.typedefs.iter() { - scope.insert_opt(&typedef.name, typedef_id.into()); + scope.insert_type_opt( + &typedef.name, + def_id(db, InContainer::new(block_id.into(), typedef_id)), + ); } for (stmt_id, stmt) in block.stmts.iter() { - scope.insert_opt(&stmt.label, stmt_id.into()); + scope.insert_value_opt( + &stmt.label, + def_id(db, InContainer::new(block_id.into(), stmt_id)), + ); if let StmtKind::Block(BlockInfo { name, block_id }) = &stmt.kind { - scope.insert_opt(name, (*block_id).into()); + scope.insert_value_opt(name, def_id(db, *block_id)); } } Arc::new(scope) } -} -impl SubroutineScope { - pub fn subroutine_scope_query(db: &dyn HirDb, subroutine_id: SubroutineId) -> Arc { - let mut scope = Scope::default(); + pub fn subroutine_scope_query( + db: &dyn HirDb, + subroutine_id: InContainer, + ) -> Arc { + let mut scope = NameScope::default(); let subroutine = db.subroutine(subroutine_id); for (port_idx, port) in subroutine.ports.iter().enumerate() { let port_id = SubroutinePortId(port_idx as u32); - scope.insert_opt(&port.name, port_id.into()); + scope.insert_value_opt( + &port.name, + def_id(db, InSubroutine::new(subroutine_id, port_id)), + ); } for (decl_id, decl) in subroutine.decls.iter() { - scope.insert_opt(&decl.name, decl_id.into()); + scope.insert_value_opt( + &decl.name, + def_id(db, InContainer::new(subroutine_id.into(), decl_id)), + ); } for (typedef_id, typedef) in subroutine.typedefs.iter() { - scope.insert_opt(&typedef.name, typedef_id.into()); + scope.insert_type_opt( + &typedef.name, + def_id(db, InContainer::new(subroutine_id.into(), typedef_id)), + ); } for (stmt_id, stmt) in subroutine.stmts.iter() { - scope.insert_opt(&stmt.label, stmt_id.into()); + scope.insert_value_opt( + &stmt.label, + def_id(db, InContainer::new(subroutine_id.into(), stmt_id)), + ); if let StmtKind::Block(BlockInfo { name, block_id }) = &stmt.kind { - scope.insert_opt(name, (*block_id).into()); + scope.insert_value_opt(name, def_id(db, *block_id)); } } Arc::new(scope) } + + pub fn non_ansi_port_decl_id_by_name( + &self, + db: &dyn HirDb, + module: &Module, + name: &SmolStr, + ) -> Option { + let defs = self.values.get(name)?; + defs.iter().filter_map(|def_id| def_id.as_decl(db)).find_map(|decl_id| { + let decl = module.get(decl_id.value); + match decl.parent { + DeclaratorParent::PortDeclId(port_decl_id) => Some(port_decl_id), + _ => None, + } + }) + } + + fn insert_package_import(&mut self, import: &PackageImport) { + self.imports.push(Import { package: import.package.clone(), name: import.item.clone() }); + } + + fn extend_defs_from(&mut self, other: &NameScope) { + for (ident, defs) in &other.types { + for def_id in defs { + self.insert_type(ident, *def_id); + } + } + for (ident, defs) in &other.values { + for def_id in defs { + self.insert_value(ident, *def_id); + } + } + for (ident, defs) in &other.assertions { + for def_id in defs { + self.insert_assertion(ident, *def_id); + } + } + } +} + +struct PackageExportSignatureBuilder<'a> { + db: &'a dyn HirDb, + package_id: PackageId, + scope: &'a mut NameScope, + next_declaration: u32, + next_decl: u32, + next_typedef: u32, + next_subroutine: u32, +} + +impl PackageExportSignatureBuilder<'_> { + fn collect(&mut self, package: ast::ModuleDeclaration<'_>) { + for member in package.members().children() { + use ast::Member::*; + match member { + DataDeclaration(decl) => self.record_declarators(decl.declarators()), + NetDeclaration(decl) => self.record_declarators(decl.declarators()), + ParameterDeclarationStatement(decl) => self.record_param_decl(decl.parameter()), + TypedefDeclaration(decl) => self.record_typedef(decl), + GenvarDeclaration(decl) => self.record_identifier_names(decl.identifiers()), + SpecparamDeclaration(decl) => self.record_specparam_declarators(decl.declarators()), + FunctionDeclaration(decl) => self.record_subroutine(decl), + _ => {} + } + } + } + + fn record_declarators<'a>(&mut self, declarators: ast::SeparatedList<'a, ast::Declarator<'a>>) { + let _declaration_id = self.next_declaration_id(); + for declarator in declarators.children() { + self.record_decl_name(lower_ident_opt(declarator.name())); + } + } + + fn record_specparam_declarators<'a>( + &mut self, + declarators: ast::SeparatedList<'a, ast::SpecparamDeclarator<'a>>, + ) { + let _declaration_id = self.next_declaration_id(); + for declarator in declarators.children() { + self.record_decl_name(lower_ident_opt(declarator.name())); + } + } + + fn record_identifier_names<'a>( + &mut self, + identifiers: ast::SeparatedList<'a, ast::IdentifierName<'a>>, + ) { + let _declaration_id = self.next_declaration_id(); + for ident in identifiers.children() { + self.record_decl_name(lower_ident_opt(ident.identifier())); + } + } + + fn record_param_decl(&mut self, param_decl: ast::ParameterDeclarationBase<'_>) { + match param_decl { + ast::ParameterDeclarationBase::ParameterDeclaration(decl) => { + self.record_declarators(decl.declarators()); + } + ast::ParameterDeclarationBase::TypeParameterDeclaration(_) => { + let _declaration_id = self.next_declaration_id(); + } + } + } + + fn record_decl_name(&mut self, name: Option) { + let decl_id = self.next_decl_id(); + self.scope.insert_value_opt( + &name, + def_id(self.db, InContainer::new(self.package_id.into(), decl_id)), + ); + } + + fn record_typedef(&mut self, typedef: ast::TypedefDeclaration<'_>) { + let typedef_id = self.next_typedef_id(); + let name = lower_ident_opt(typedef.name()); + self.scope.insert_type_opt( + &name, + def_id(self.db, InContainer::new(self.package_id.into(), typedef_id)), + ); + } + + fn record_subroutine(&mut self, subroutine: ast::FunctionDeclaration<'_>) { + let local_id = self.next_subroutine_id(); + let name = lower_name(subroutine.prototype().name()); + self.scope.insert_value_opt( + &name, + def_id(self.db, InContainer::new(self.package_id.into(), local_id)), + ); + } + + fn next_declaration_id(&mut self) -> DeclarationId { + let id = Idx::from_raw(RawIdx::from(self.next_declaration)); + self.next_declaration += 1; + id + } + + fn next_decl_id(&mut self) -> DeclId { + let id = Idx::from_raw(RawIdx::from(self.next_decl)); + self.next_decl += 1; + id + } + + fn next_typedef_id(&mut self) -> TypedefId { + let id = Idx::from_raw(RawIdx::from(self.next_typedef)); + self.next_typedef += 1; + id + } + + fn next_subroutine_id(&mut self) -> LocalSubroutineId { + let id = Idx::from_raw(RawIdx::from(self.next_subroutine)); + self.next_subroutine += 1; + id + } +} + +fn lower_name(name: ast::Name<'_>) -> Option { + if let Some(id) = name.as_identifier_name().and_then(|name| name.identifier()) { + return lower_ident_opt(Some(id)); + } + if let Some(select) = name.as_identifier_select_name() { + return select.identifier().and_then(|tok| lower_ident_opt(Some(tok))); + } + if let Some(scoped) = name.as_scoped_name() { + return lower_name(scoped.right()); + } + None +} + +#[cfg(test)] +mod tests { + use std::fmt; + + use rustc_hash::FxHashSet; + use smol_str::SmolStr; + use triomphe::Arc; + use utils::paths::{AbsPathBuf, Utf8PathBuf}; + use vfs::{FileId, FileSet, VfsPath, anchored_path::AnchoredPath}; + + use crate::{ + base_db::{ + diagnostics_config::DiagnosticsConfig, + project::{CompilationProfile, CompilationProfileId, PreprocessConfig, ProjectConfig}, + salsa::{self, Durability}, + source_db::{ + FileLoader, SourceDb, SourceDbStorage, SourceFileKind, SourceRootDb, + SourceRootDbStorage, + }, + source_root::{SourceRoot, SourceRootId}, + }, + container::ScopeId, + db::{HirDb, HirDbStorage, InternDbStorage}, + hir_def::Ident, + semantics::pathres::resolve_name, + symbol::{DefKind, DefLoc, NameContext}, + }; + + const TOP: FileId = FileId(0); + const ROOT: SourceRootId = SourceRootId(0); + const PROFILE: CompilationProfileId = CompilationProfileId(0); + + #[salsa::database(SourceDbStorage, SourceRootDbStorage, InternDbStorage, HirDbStorage)] + #[derive(Default)] + struct TestDb { + storage: salsa::Storage, + } + + impl salsa::Database for TestDb {} + + impl fmt::Debug for TestDb { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TestDb").finish() + } + } + + impl FileLoader for TestDb { + fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { + let source_root_id = SourceRootDb::source_root_id(self, path.anchor_id); + SourceRootDb::source_root(self, source_root_id).resolve_path(path) + } + } + + fn db_with_root_text(root_text: &str) -> TestDb { + let top_path = abs_path("rtl/top.sv"); + let mut file_set = FileSet::default(); + file_set.insert(TOP, VfsPath::from(top_path.clone())); + let root = SourceRoot::new_local_with_source_files(file_set, vec![TOP]); + let mut files = FxHashSet::default(); + files.insert(TOP); + + let preprocess = PreprocessConfig::default(); + let project_config = ProjectConfig::new( + vec![Some(PROFILE)], + vec![CompilationProfile { + source_roots: vec![ROOT], + top_modules: Vec::new(), + preprocess: preprocess.clone(), + }], + ); + + let mut db = TestDb::default(); + db.set_files_with_durability(Box::new(files), Durability::HIGH); + db.set_project_config_with_durability(Arc::new(project_config), Durability::HIGH); + db.set_diagnostics_config_with_durability( + Arc::new(DiagnosticsConfig::default()), + Durability::HIGH, + ); + db.set_source_root_with_durability(ROOT, Arc::new(root), Durability::LOW); + db.set_source_root_id_with_durability(TOP, ROOT, Durability::LOW); + db.set_file_path_with_durability(TOP, Some(top_path), Durability::LOW); + db.set_file_kind_with_durability(TOP, SourceFileKind::SystemVerilog, Durability::LOW); + db.set_file_text_with_durability(TOP, Arc::from(root_text), Durability::LOW); + db.set_file_preprocess_config_with_durability(TOP, Arc::new(preprocess), Durability::LOW); + db + } + + fn abs_path(path: &str) -> AbsPathBuf { + let prefix = if cfg!(windows) { "C:/repo" } else { "/repo" }; + AbsPathBuf::assert(Utf8PathBuf::from(format!("{prefix}/{path}"))) + } + + fn ident(name: &str) -> Ident { + SmolStr::new(name) + } + + #[test] + fn name_scope_context_lookup_covers_current_scope_shapes() { + let db = db_with_root_text( + r#" +typedef logic shared; +wire shared; +wire file_sig; + +module m(a); + output a; + reg [7:0] a; + + function automatic [3:0] f(input p); + begin: b + integer x; + end + endfunction + + generate + if (1) begin: g + wire y; + end + endgenerate +endmodule +"#, + ); + + let unit_scope = db.unit_scope(); + assert!( + unit_scope + .lookup(NameContext::Value, &ident("file_sig")) + .expect("file decl should be visible") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::Net) + ); + let shared_defs = unit_scope + .lookup(NameContext::Listing, &ident("shared")) + .expect("listing lookup should preserve same-name type and value definitions"); + assert!(shared_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Typedef)); + assert!(shared_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Net)); + let shared_type_defs = unit_scope + .lookup(NameContext::Type, &ident("shared")) + .expect("type lookup should see the typedef side of a collision"); + assert!(shared_type_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Typedef)); + assert!(!shared_type_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Net)); + let shared_value_defs = unit_scope + .lookup(NameContext::Value, &ident("shared")) + .expect("value lookup should see the net side of a collision"); + assert!(shared_value_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Net)); + assert!(!shared_value_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Typedef)); + + let module_id = unit_scope + .module_ids(&db, &ident("m")) + .unique() + .expect("module should resolve uniquely"); + + let module_scope = db.module_scope(module_id); + let port_defs = module_scope + .lookup(NameContext::Value, &ident("a")) + .expect("non-ANSI port name should resolve"); + assert!(port_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::NonAnsiPort)); + assert!(port_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Port)); + assert!(port_defs.iter().any(|def_id| def_id.kind(&db) == DefKind::Variable)); + + let subroutine_id = module_scope + .lookup(NameContext::Value, &ident("f")) + .and_then(|defs| defs.iter().find_map(|def_id| def_id.as_subroutine(&db))) + .expect("subroutine should be visible from module scope"); + let subroutine_scope = db.subroutine_scope(subroutine_id); + assert!( + subroutine_scope + .lookup(NameContext::Value, &ident("p")) + .expect("subroutine port should be visible") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::SubroutinePort) + ); + + let block_id = subroutine_scope + .lookup(NameContext::Value, &ident("b")) + .and_then(|defs| defs.iter().find_map(|def_id| def_id.as_block(&db))) + .expect("named block should be visible from subroutine scope"); + assert!( + db.block_scope(block_id) + .lookup(NameContext::Value, &ident("x")) + .expect("block local should be visible") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::Variable) + ); + + let generate_block_id = module_scope + .lookup(NameContext::Value, &ident("g")) + .and_then(|defs| defs.iter().find_map(|def_id| def_id.as_generate_block(&db))) + .expect("generate block should be visible from module scope"); + assert!( + db.generate_block_scope(generate_block_id) + .lookup(NameContext::Value, &ident("y")) + .expect("generate local should be visible") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::Net) + ); + + // Adding an interface lowering should create a DefKind::Interface + // producer and insert the resulting DefId into NameScope; IDE + // feature matches already have default no-op arms. + } + + #[test] + fn package_imports_resolve_through_export_scope() { + let db = db_with_root_text( + r#" +package pkg; + typedef logic imported_t; + int imported_v; + int shadowed_v; + function int imported_f(); + return 1; + endfunction +endpackage + +module wildcard_importer; + import pkg::*; + wire shadowed_v; +endmodule + +module named_importer; + import pkg::imported_v; +endmodule +"#, + ); + + let unit_scope = db.unit_scope(); + let package_id = unit_scope + .package_ids(&db, &ident("pkg")) + .unique() + .expect("package should resolve uniquely"); + let package_exports = db.package_export_scope(package_id); + assert!( + package_exports + .lookup(NameContext::Type, &ident("imported_t")) + .expect("package export scope should expose package typedef") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::Typedef) + ); + assert!( + package_exports + .lookup(NameContext::Value, &ident("imported_v")) + .expect("package export scope should expose package value") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::Variable) + ); + assert!( + package_exports + .lookup(NameContext::Value, &ident("imported_f")) + .expect("package export scope should expose package subroutines") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::Subroutine) + ); + + let wildcard_importer = db + .unit_scope() + .module_ids(&db, &ident("wildcard_importer")) + .unique() + .expect("wildcard importer should resolve uniquely"); + let wildcard_scope = db.module_scope(wildcard_importer); + assert!( + wildcard_scope + .imports + .iter() + .any(|import| import.package == ident("pkg") && import.name.is_none()) + ); + + let imported_t = resolve_name( + &db, + ScopeId::Module(wildcard_importer), + &ident("imported_t"), + NameContext::Type, + ) + .expect("wildcard import should expose package typedef"); + assert!(imported_t.def_ids().iter().any(|def_id| def_id.kind(&db) == DefKind::Typedef)); + assert!( + resolve_name( + &db, + ScopeId::Module(wildcard_importer), + &ident("imported_t"), + NameContext::Value, + ) + .is_none(), + "value lookup should not fall back to the type bucket" + ); + + let shadowed_v = resolve_name( + &db, + ScopeId::Module(wildcard_importer), + &ident("shadowed_v"), + NameContext::Value, + ) + .expect("local declaration should win before wildcard imports"); + assert!(shadowed_v.def_ids().iter().any(|def_id| def_id.kind(&db) == DefKind::Net)); + assert!(!shadowed_v.def_ids().iter().any(|def_id| def_id.kind(&db) == DefKind::Variable)); + + let named_importer = db + .unit_scope() + .module_ids(&db, &ident("named_importer")) + .unique() + .expect("named importer should resolve uniquely"); + let named_scope = db.module_scope(named_importer); + assert!(named_scope.imports.iter().any(|import| { + import.package == ident("pkg") + && import.name.as_ref().is_some_and(|name| name == "imported_v") + })); + + let imported_v = resolve_name( + &db, + ScopeId::Module(named_importer), + &ident("imported_v"), + NameContext::Value, + ) + .expect("named import should expose the selected package value"); + assert!(imported_v.def_ids().iter().any(|def_id| def_id.kind(&db) == DefKind::Variable)); + assert!( + resolve_name( + &db, + ScopeId::Module(named_importer), + &ident("imported_t"), + NameContext::Type, + ) + .is_none(), + "named import should not expose unrelated package symbols" + ); + } + + #[test] + fn package_subroutine_def_id_is_canonical_across_imports() { + let db = db_with_root_text( + r#" +package pkg; + function automatic int f(); + return 1; + endfunction +endpackage + +module named_importer; + import pkg::f; +endmodule + +module wildcard_importer; + import pkg::*; +endmodule +"#, + ); + + let package_id = db + .unit_scope() + .package_ids(&db, &ident("pkg")) + .unique() + .expect("package should resolve uniquely"); + let package_f = + resolve_name(&db, ScopeId::Module(package_id), &ident("f"), NameContext::Value) + .and_then(|res| res.primary_def_id()) + .expect("package scope should resolve package subroutine"); + + let DefLoc::Subroutine(package_subroutine) = package_f.loc(&db) else { + panic!("package f should resolve to a subroutine"); + }; + assert_eq!(package_subroutine.cont_id, ScopeId::Module(package_id)); + + let named_importer = db + .unit_scope() + .module_ids(&db, &ident("named_importer")) + .unique() + .expect("named importer should resolve uniquely"); + let named_import_f = + resolve_name(&db, ScopeId::Module(named_importer), &ident("f"), NameContext::Value) + .and_then(|res| res.primary_def_id()) + .expect("named import should resolve package subroutine"); + + let wildcard_importer = db + .unit_scope() + .module_ids(&db, &ident("wildcard_importer")) + .unique() + .expect("wildcard importer should resolve uniquely"); + let wildcard_import_f = + resolve_name(&db, ScopeId::Module(wildcard_importer), &ident("f"), NameContext::Value) + .and_then(|res| res.primary_def_id()) + .expect("wildcard import should resolve package subroutine"); + + assert_eq!(package_f, named_import_f); + assert_eq!(package_f.loc(&db), named_import_f.loc(&db)); + assert_eq!(package_f, wildcard_import_f); + assert_eq!(package_f.loc(&db), wildcard_import_f.loc(&db)); + } + + #[test] + fn package_export_signature_is_stable_across_function_body_edits() { + let mut db = db_with_root_text( + r#" +package pkg; + typedef logic exported_t; + int exported_v; + function int exported_f(); + int body_local; + return body_local; + endfunction +endpackage +"#, + ); + + let package_id = db + .unit_scope() + .package_ids(&db, &ident("pkg")) + .unique() + .expect("package should resolve uniquely"); + + let exports = db.package_export_scope(package_id); + assert!( + exports + .lookup(NameContext::Value, &ident("exported_f")) + .expect("signature should include package subroutine declarations") + .iter() + .any(|def_id| def_id.kind(&db) == DefKind::Subroutine) + ); + + let before_body_edit = db.package_export_signature(package_id); + db.set_file_text_with_durability( + TOP, + Arc::from( + r#" +package pkg; + typedef logic exported_t; + int exported_v; + function int exported_f(); + int changed_body_local; + return changed_body_local; + endfunction +endpackage +"#, + ), + Durability::LOW, + ); + let after_body_edit = db.package_export_signature(package_id); + assert_eq!( + before_body_edit, after_body_edit, + "function body edits should not change the package export signature" + ); + } } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 246eb1a5..c201db79 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -12,7 +12,7 @@ use utils::text_edit::TextSize; use vfs::FileId; use crate::{ - container::{ContainerId, InContainer, InFile}, + container::{InContainer, InFile, ScopeId}, db::HirDb, file::HirFileId, hir_def::{ @@ -20,8 +20,9 @@ use crate::{ block::{BlockId, BlockSrc}, expr::ExprId, module::{ModuleId, ModuleSrc}, - subroutine::{SubroutineId, SubroutineSrc}, + subroutine::{LocalSubroutineId, SubroutineSrc}, }, + symbol::NameContext, }; mod hir_to_def; @@ -114,7 +115,7 @@ impl<'db> SemanticsImpl<'db> { ParsedFile { file_id, tree: self.db.parse(file_id) } } - pub fn container_for_node(&self, file_id: HirFileId, node: SyntaxNode) -> Option { + pub fn container_for_node(&self, file_id: HirFileId, node: SyntaxNode) -> Option { self.with_ctx(|ctx| Some(ctx.find_container(InFile::new(file_id, node)))) } @@ -147,7 +148,7 @@ impl SemanticsImpl<'_> { &self, file_id: HirFileId, subroutine: ast::FunctionDeclaration, - ) -> Option { + ) -> Option> { let subroutine_src = SubroutineSrc::from_ast(file_id, subroutine); self.with_ctx(|ctx| ctx.subroutine_to_def(InFile::new(file_id, subroutine_src))) } @@ -157,6 +158,6 @@ impl SemanticsImpl<'_> { } pub fn name_to_def(&self, in_cont: InContainer) -> Option { - self.with_ctx(|ctx| ctx.name_to_def(in_cont)) + self.with_ctx(|ctx| ctx.name_to_def(in_cont, NameContext::Value)) } } diff --git a/crates/hir/src/semantics/hir_to_def.rs b/crates/hir/src/semantics/hir_to_def.rs index 93f1ab60..76f88e27 100644 --- a/crates/hir/src/semantics/hir_to_def.rs +++ b/crates/hir/src/semantics/hir_to_def.rs @@ -3,15 +3,15 @@ use utils::get::GetRef; use super::{Source2DefCtx, pathres::PathResolution}; use crate::{ - container::{ - ContainerId, ContainerParent, InBlock, InContainer, InGenerateBlock, InModule, InSubroutine, - }, + container::{InContainer, ScopeId}, hir_def::{ Ident, block::BlockId, expr::{Expr, ExprId}, module::{ModuleId, generate::GenerateBlockId, instantiation::InstanceId}, }, + semantics::pathres::{name_scope, resolve_name}, + symbol::{DefLoc, NameContext}, }; #[derive(Default, Debug)] @@ -32,41 +32,42 @@ impl Source2DefCtx<'_, '_> { let field = field.as_ref()?; let receiver_res = self.expr_to_def(InContainer::new(cont_id, *receiver))?; let res = self.resolve_member_from_resolution(receiver_res, field)?; - self.hir_cache.expr_map.insert(InContainer::new(cont_id, expr_id), res); + self.hir_cache.expr_map.insert(InContainer::new(cont_id, expr_id), res.clone()); Some(res) } Expr::ElementSelect { receiver, .. } => { let res = self.expr_to_def(InContainer::new(cont_id, *receiver))?; - self.hir_cache.expr_map.insert(InContainer::new(cont_id, expr_id), res); + self.hir_cache.expr_map.insert(InContainer::new(cont_id, expr_id), res.clone()); Some(res) } Expr::Ident(ident) => { - let res = self.name_to_def(InContainer::new(cont_id, ident.clone()))?; - self.hir_cache.expr_map.insert(InContainer::new(cont_id, expr_id), res); + let res = + self.name_to_def(InContainer::new(cont_id, ident.clone()), NameContext::Value)?; + self.hir_cache.expr_map.insert(InContainer::new(cont_id, expr_id), res.clone()); Some(res) } _ => None, }; match cont_id { - ContainerId::HirFileId(file_id) => { + ScopeId::File(file_id) => { let file = db.hir_file(file_id); resolve(file.get(expr_id)) } - ContainerId::ModuleId(in_file) => { + ScopeId::Module(in_file) => { let module = db.module(in_file); resolve(module.get(expr_id)) } - ContainerId::BlockId(block_id) => { + ScopeId::Block(block_id) => { let block = db.block(block_id); resolve(block.get(expr_id)) } - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::GenerateBlock(generate_block_id) => { let generate_block = db.generate_block(generate_block_id); resolve(generate_block.get(expr_id)) } - ContainerId::SubroutineId(subroutine_id) => { - let subroutine = db.subroutine(subroutine_id); + ScopeId::Subroutine(subroutine_id) => { + let subroutine = db.subroutine(subroutine_id.as_in_container()); resolve(subroutine.get(expr_id)) } } @@ -75,36 +76,10 @@ impl Source2DefCtx<'_, '_> { pub(super) fn name_to_def( &mut self, InContainer { cont_id, value: ident }: InContainer, + name_ctx: NameContext, ) -> Option { - let db = self.db; - let res = ContainerParent::start_from(db, cont_id).find_map(|id| match id { - ContainerId::HirFileId(_) => { - let scope = db.unit_scope(); - let entry = scope.get(&ident)?; - Some(entry.into()) - } - ContainerId::ModuleId(module_id) => { - let scope = db.module_scope(module_id); - let entry = scope.get(&ident)?; - Some(InModule::new(module_id, entry).into()) - } - ContainerId::BlockId(block_id) => { - let scope = db.block_scope(block_id); - let entry = scope.get(&ident)?; - Some(InBlock::new(block_id, entry).into()) - } - ContainerId::GenerateBlockId(generate_block_id) => { - let scope = db.generate_block_scope(generate_block_id); - let entry = scope.get(&ident)?; - Some(InGenerateBlock::new(generate_block_id, entry).into()) - } - ContainerId::SubroutineId(subroutine_id) => { - let scope = db.subroutine_scope(subroutine_id); - let entry = scope.get(&ident)?; - Some(InSubroutine::new(subroutine_id, entry).into()) - } - })?; - self.hir_cache.name_map.insert(InContainer::new(cont_id, ident), res); + let res = resolve_name(self.db, cont_id, &ident, name_ctx)?; + self.hir_cache.name_map.insert(InContainer::new(cont_id, ident), res.clone()); Some(res) } @@ -113,19 +88,36 @@ impl Source2DefCtx<'_, '_> { res: PathResolution, field: &Ident, ) -> Option { - match res { - PathResolution::Module(module_id) => self.resolve_member_in_module(module_id, field), - PathResolution::Instance(instance) => { - let target_module = - self.instance_target_module_id(instance.module_id, instance.value)?; - self.resolve_member_in_module(target_module, field) + for def_id in res.def_ids() { + match def_id.loc(self.db) { + DefLoc::Module(module_id) => { + if let Some(res) = self.resolve_member_in_module(module_id, field) { + return Some(res); + } + } + DefLoc::Instance(instance) => { + let target_module = + self.instance_target_module_id(instance.module_id, instance.value)?; + if let Some(res) = self.resolve_member_in_module(target_module, field) { + return Some(res); + } + } + DefLoc::Block(block_id) => { + if let Some(res) = self.resolve_member_in_block(block_id, field) { + return Some(res); + } + } + DefLoc::GenerateBlock(generate_block_id) => { + if let Some(res) = + self.resolve_member_in_generate_block(generate_block_id, field) + { + return Some(res); + } + } + _ => {} } - PathResolution::Block(block_id) => self.resolve_member_in_block(block_id, field), - PathResolution::GenerateBlock(generate_block_id) => { - self.resolve_member_in_generate_block(generate_block_id, field) - } - _ => None, } + None } fn resolve_member_in_module( @@ -133,9 +125,9 @@ impl Source2DefCtx<'_, '_> { module_id: ModuleId, field: &Ident, ) -> Option { - let scope = self.db.module_scope(module_id); - let entry = scope.get(field)?; - Some(InModule::new(module_id, entry).into()) + name_scope(self.db, module_id.into()) + .lookup(NameContext::Value, field) + .and_then(PathResolution::from_def_ids) } fn resolve_member_in_block( @@ -143,9 +135,9 @@ impl Source2DefCtx<'_, '_> { block_id: BlockId, field: &Ident, ) -> Option { - let scope = self.db.block_scope(block_id); - let entry = scope.get(field)?; - Some(InBlock::new(block_id, entry).into()) + name_scope(self.db, block_id.into()) + .lookup(NameContext::Value, field) + .and_then(PathResolution::from_def_ids) } fn resolve_member_in_generate_block( @@ -153,9 +145,9 @@ impl Source2DefCtx<'_, '_> { generate_block_id: GenerateBlockId, field: &Ident, ) -> Option { - let scope = self.db.generate_block_scope(generate_block_id); - let entry = scope.get(field)?; - Some(InGenerateBlock::new(generate_block_id, entry).into()) + name_scope(self.db, generate_block_id.into()) + .lookup(NameContext::Value, field) + .and_then(PathResolution::from_def_ids) } fn instance_target_module_id( @@ -167,6 +159,6 @@ impl Source2DefCtx<'_, '_> { let instance = module.get(instance_id); let instantiation = module.get(instance.parent); let module_name = instantiation.module_name.as_ref()?; - self.db.unit_scope().resolve_module(module_name).unique() + self.db.unit_scope().module_ids(self.db, module_name).unique() } } diff --git a/crates/hir/src/semantics/pathres.rs b/crates/hir/src/semantics/pathres.rs index 6c6372ae..a733f91a 100644 --- a/crates/hir/src/semantics/pathres.rs +++ b/crates/hir/src/semantics/pathres.rs @@ -1,29 +1,15 @@ -use smallvec::{SmallVec, smallvec}; +use smallvec::SmallVec; use syntax::{SyntaxNode, SyntaxTokenWithParent}; +use triomphe::Arc; use super::SemanticsImpl; use crate::{ - container::{ - ContainerId, ContainerParent, InBlock, InContainer, InFile, InGenerateBlock, InModule, - InSubroutine, - }, + container::{InContainer, InFile, ScopeId, ScopeParent}, db::HirDb, - def_id::{ModuleDef, ModuleDefId, ModuleDefOrigin}, + def_id::{ModuleDef, ModuleDefId}, file::HirFileId, - hir_def::{ - Ident, - block::BlockId, - expr::declarator::DeclId, - file::{config::ConfigDeclId, library::LibraryDeclId, udp::UdpDeclId}, - lower_ident_opt, - module::{ - ModuleId, generate::GenerateBlockId, instantiation::InstanceId, port::NonAnsiPortId, - }, - stmt::StmtId, - subroutine::{SubroutineId, SubroutinePortId}, - typedef::TypedefId, - }, - scope::{self, BlockEntry, GenerateBlockEntry, ModuleEntry, SubroutineEntry, UnitEntry}, + hir_def::{Ident, lower_ident_opt}, + symbol::{DefId, NameContext, NameScope}, }; impl SemanticsImpl<'_> { @@ -31,203 +17,157 @@ impl SemanticsImpl<'_> { &self, file_id: HirFileId, SyntaxTokenWithParent { parent, tok }: SyntaxTokenWithParent, + name_ctx: NameContext, ) -> Option { let ident = lower_ident_opt(Some(tok))?; - self.with_ctx(|ctx| { - let container = ctx.find_container(InFile::new(file_id, parent)); - ctx.name_to_def(InContainer::new(container, ident)) + self.with_ctx(|source_ctx| { + let container = source_ctx.find_container(InFile::new(file_id, parent)); + source_ctx.name_to_def(InContainer::new(container, ident), name_ctx) }) } - pub(in crate::semantics) fn find_container(&self, node: InFile) -> ContainerId { + pub(in crate::semantics) fn find_container(&self, node: InFile) -> ScopeId { self.with_ctx(|ctx| ctx.find_container(node)) } - pub fn resolve_name(&self, cont_id: ContainerId, ident: &Ident) -> Option { - resolve_name(self.db, cont_id, ident) + pub fn resolve_name( + &self, + cont_id: ScopeId, + ident: &Ident, + ctx: NameContext, + ) -> Option { + resolve_name(self.db, cont_id, ident, ctx) } } -pub fn resolve_name(db: &dyn HirDb, cont_id: ContainerId, ident: &Ident) -> Option { - ContainerParent::start_from(db, cont_id).find_map(|id| match id { - ContainerId::HirFileId(_) => db.unit_scope().get(ident).map(PathResolution::from), - ContainerId::ModuleId(module_id) => db - .module_scope(module_id) - .get(ident) - .map(|entry| PathResolution::from(InModule::new(module_id, entry))), - ContainerId::GenerateBlockId(generate_block_id) => db - .generate_block_scope(generate_block_id) - .get(ident) - .map(|entry| PathResolution::from(InGenerateBlock::new(generate_block_id, entry))), - ContainerId::BlockId(block_id) => db - .block_scope(block_id) - .get(ident) - .map(|entry| PathResolution::from(InBlock::new(block_id, entry))), - ContainerId::SubroutineId(subroutine_id) => db - .subroutine_scope(subroutine_id) - .get(ident) - .map(|entry| PathResolution::from(InSubroutine::new(subroutine_id, entry))), - }) +pub fn resolve_name( + db: &dyn HirDb, + cont_id: ScopeId, + ident: &Ident, + ctx: NameContext, +) -> Option { + let scopes = ScopeParent::start_from(db, cont_id).collect::>(); + + for id in &scopes { + let scope = name_scope(db, *id); + if let Some(res) = scope.lookup(ctx, ident).and_then(PathResolution::from_def_ids) { + return Some(res); + } + } + + // IEEE 1800-2017 keeps package imports distinct from ordinary lexical + // declarations: visible declarations in the lexical chain win, then + // package imports are considered, and `$unit` remains an explicit outer + // scope. `NameContext` chooses the namespace bucket at every phase. + if let Some(res) = resolve_imported_name(db, &scopes, ident, ctx) { + return Some(res); + } + + db.unit_scope().lookup(ctx, ident).and_then(PathResolution::from_def_ids) } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum PathResolution { - Module(ModuleId), - Config(InFile), - Library(InFile), - Udp(InFile), - Decl(InContainer), - Typedef(InContainer), - ParamDecl(InModule), - Subroutine(SubroutineId), - SubroutinePort(InSubroutine), - NonAnsiPort { - // There won't be a situation where all fields are None. - label: Option, - port_decl: Option, - data_decl: Option, - module: ModuleId, - }, - AnsiPort(InModule), - Instance(InModule), - Stmt(InContainer), - Block(BlockId), - GenerateBlock(GenerateBlockId), +pub(crate) fn name_scope(db: &dyn HirDb, scope_id: ScopeId) -> Arc { + match scope_id { + ScopeId::File(file_id) => db.file_scope(file_id), + ScopeId::Module(module_id) => db.module_scope(module_id), + ScopeId::GenerateBlock(generate_block_id) => db.generate_block_scope(generate_block_id), + ScopeId::Block(block_id) => db.block_scope(block_id), + ScopeId::Subroutine(subroutine_id) => db.subroutine_scope(subroutine_id.as_in_container()), + } } -impl PathResolution { - pub fn to_def_id(self, db: &dyn HirDb) -> Option { - let module_def = ModuleDef::from_origins(self.origins())?; - Some(db.intern_module_def(module_def)) +fn resolve_imported_name( + db: &dyn HirDb, + scopes: &[ScopeId], + ident: &Ident, + ctx: NameContext, +) -> Option { + let mut defs = SmallVec::<[DefId; 3]>::new(); + + for scope_id in scopes { + let scope = name_scope(db, *scope_id); + collect_imports(db, &scope, ident, ctx, true, &mut defs); + if !defs.is_empty() { + return PathResolution::from_def_ids(defs); + } } - fn origins(self) -> SmallVec<[ModuleDefOrigin; 3]> { - let mut res = smallvec![]; - let mut add_source = |source| res.push(source); + for scope_id in scopes { + let scope = name_scope(db, *scope_id); + collect_imports(db, &scope, ident, ctx, false, &mut defs); + if !defs.is_empty() { + return PathResolution::from_def_ids(defs); + } + } - match self { - PathResolution::NonAnsiPort { label, port_decl, data_decl, module } => { - let container: ContainerId = module.into(); - if let Some(label) = label { - add_source(InModule::new(module, label).into()); - } - if let Some(port_decl) = port_decl { - add_source(InContainer::new(container, port_decl).into()); - } - if let Some(decl) = data_decl { - add_source(InContainer::new(container, decl).into()); - } - } - _ => { - if let Some(origin) = self.pick() { - add_source(origin); - } - } - }; + None +} - res - } +fn collect_imports( + db: &dyn HirDb, + scope: &NameScope, + ident: &Ident, + ctx: NameContext, + named_only: bool, + defs: &mut SmallVec<[DefId; 3]>, +) { + for import in &scope.imports { + match (&import.name, named_only) { + (Some(name), true) if name == ident => {} + (None, false) => {} + _ => continue, + } - #[inline] - fn pick(self) -> Option { - match self { - PathResolution::Module(module_id) => Some(module_id.into()), - PathResolution::Config(config_id) => Some(config_id.into()), - PathResolution::Library(library_id) => Some(library_id.into()), - PathResolution::Udp(udp_id) => Some(udp_id.into()), - PathResolution::Decl(decl_id) => Some(decl_id.into()), - PathResolution::Typedef(typedef_id) => Some(typedef_id.into()), - PathResolution::Instance(instance_id) => Some(instance_id.into()), - PathResolution::Stmt(stmt_id) => Some(stmt_id.into()), - PathResolution::Block(blk_id) => Some(blk_id.into()), - PathResolution::GenerateBlock(generate_block_id) => Some(generate_block_id.into()), - PathResolution::Subroutine(subroutine_id) => Some(subroutine_id.into()), - PathResolution::SubroutinePort(port_id) => Some(port_id.into()), - PathResolution::ParamDecl(decl_id) | PathResolution::AnsiPort(decl_id) => { - Some(InContainer::new(decl_id.module_id.into(), decl_id.value).into()) - } - PathResolution::NonAnsiPort { label, port_decl, data_decl, module } => { - let container: ContainerId = module.into(); - if let Some(label) = label { - Some(InModule::new(module, label).into()) - } else if let Some(port_decl) = port_decl { - Some(InContainer::new(container, port_decl).into()) - } else { - data_decl.map(|decl| InContainer::new(container, decl).into()) + let Some(package_id) = db.unit_scope().package_ids(db, &import.package).unique() else { + continue; + }; + let package_scope = db.package_export_scope(package_id); + if let Some(imported) = package_scope.lookup(ctx, ident) { + for def_id in imported { + if !defs.contains(&def_id) { + defs.push(def_id); } } } } } -impl From for PathResolution { - fn from(entry: UnitEntry) -> Self { - use UnitEntry::*; - match entry { - ModuleId(idx) => Self::Module(idx), - FiledConfigDeclId(idx) => Self::Config(idx), - FiledLibraryDeclId(idx) => Self::Library(idx), - FiledUdpDeclId(idx) => Self::Udp(idx), - FiledDeclId(idx) => Self::Decl(idx.into()), - FiledTypedefId(idx) => Self::Typedef(idx.into()), - } - } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathResolution { + def_ids: SmallVec<[DefId; 3]>, } -impl From> for PathResolution { - fn from(entry: InModule) -> Self { - use ModuleEntry::*; - match entry.value { - DeclId(decl_id) => Self::Decl(entry.with_value(decl_id).into()), - TypedefId(typedef_id) => Self::Typedef(entry.with_value(typedef_id).into()), - InstanceId(idx) => Self::Instance(entry.with_value(idx)), - GenerateBlockId(generate_block_id) => Self::GenerateBlock(generate_block_id), - StmtId(idx) => Self::Stmt(entry.with_value(idx).into()), - SubroutineId(subroutine_id) => Self::Subroutine(subroutine_id), - NonAnsiPortEntry(scope::NonAnsiPortEntry { label, port_decl, data_decl }) => { - Self::NonAnsiPort { label, port_decl, data_decl, module: entry.module_id } +impl PathResolution { + pub fn from_def_id(def_id: DefId) -> Self { + Self { def_ids: SmallVec::from_slice(&[def_id]) } + } + + pub fn from_def_ids(def_ids: impl IntoIterator) -> Option { + let mut resolved = SmallVec::<[DefId; 3]>::new(); + for def_id in def_ids { + if !resolved.contains(&def_id) { + resolved.push(def_id); } - AnsiPortEntry(scope::AnsiPortEntry(idx)) => Self::AnsiPort(entry.with_value(idx)), - BlockId(block_id) => Self::Block(block_id), } + (!resolved.is_empty()).then_some(Self { def_ids: resolved }) } -} -impl From> for PathResolution { - fn from(entry: InGenerateBlock) -> Self { - use GenerateBlockEntry::*; - match entry.value { - DeclId(idx) => Self::Decl(entry.with_value(idx).into()), - TypedefId(idx) => Self::Typedef(entry.with_value(idx).into()), - GenerateBlockId(generate_block_id) => Self::GenerateBlock(generate_block_id), - StmtId(idx) => Self::Stmt(entry.with_value(idx).into()), - BlockId(block_id) => Self::Block(block_id), - SubroutineId(subroutine_id) => Self::Subroutine(subroutine_id), - } + pub fn def_ids(&self) -> &[DefId] { + &self.def_ids } -} -impl From> for PathResolution { - fn from(entry: InBlock) -> Self { - use BlockEntry::*; - match entry.value { - DeclId(idx) => Self::Decl(entry.with_value(idx).into()), - TypedefId(idx) => Self::Typedef(entry.with_value(idx).into()), - StmtId(idx) => Self::Stmt(entry.with_value(idx).into()), - BlockId(block_id) => Self::Block(block_id), - } + pub fn primary_def_id(&self) -> Option { + self.def_ids.first().copied() + } + + pub fn to_def_id(&self, db: &dyn HirDb) -> Option { + let module_def = ModuleDef::from_def_ids(self.def_ids.iter().copied())?; + Some(db.intern_module_def(module_def)) } } -impl From> for PathResolution { - fn from(entry: InSubroutine) -> Self { - use SubroutineEntry::*; - match entry.value { - DeclId(idx) => Self::Decl(entry.with_value(idx).into()), - TypedefId(idx) => Self::Typedef(entry.with_value(idx).into()), - StmtId(idx) => Self::Stmt(entry.with_value(idx).into()), - BlockId(block_id) => Self::Block(block_id), - SubroutinePortId(idx) => Self::SubroutinePort(entry.with_value(idx)), - } +impl From for PathResolution { + fn from(def_id: DefId) -> Self { + Self::from_def_id(def_id) } } diff --git a/crates/hir/src/semantics/resolver.rs b/crates/hir/src/semantics/resolver.rs index 478b7122..b329f7ac 100644 --- a/crates/hir/src/semantics/resolver.rs +++ b/crates/hir/src/semantics/resolver.rs @@ -3,7 +3,7 @@ use utils::get::Get; use super::SemanticsImpl; use crate::{ - container::{ContainerId, InContainer, InFile, InModule}, + container::{InContainer, InFile, InModule, ScopeId}, file::HirFileId, hir_def::{ expr::{ExprId, ExprSrc}, @@ -22,7 +22,7 @@ impl SemanticsImpl<'_> { instance: ast::HierarchicalInstance, ) -> Option> { let db = self.db; - let ContainerId::ModuleId(module_id) = + let ScopeId::Module(module_id) = self.find_container(InFile::new(file_id, instance.syntax())) else { return None; @@ -40,7 +40,7 @@ impl SemanticsImpl<'_> { instantiation: ast::HierarchyInstantiation, ) -> Option> { let db = self.db; - let ContainerId::ModuleId(module_id) = + let ScopeId::Module(module_id) = self.find_container(InFile::new(file_id, instantiation.syntax())) else { return None; @@ -60,8 +60,7 @@ impl SemanticsImpl<'_> { conn: ast::PortConnection, ) -> Option> { let db = self.db; - let ContainerId::ModuleId(module_id) = - self.find_container(InFile::new(file_id, conn.syntax())) + let ScopeId::Module(module_id) = self.find_container(InFile::new(file_id, conn.syntax())) else { return None; }; diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 779d9012..d8993212 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -9,7 +9,7 @@ use utils::get::{Get, GetRef}; use super::hir_to_def::Hir2DefCache; use crate::{ - container::{ContainerId, InFile}, + container::{InContainer, InFile, ScopeId}, db::HirDb, file::HirFileId, hir_def::{ @@ -18,16 +18,14 @@ use crate::{ ModuleId, ModuleSrc, generate::{GenerateBlockLoc, GenerateBlockSrc}, }, - subroutine::{ - LocalSubroutineId, SubroutineContainerId, SubroutineId, SubroutineLoc, SubroutineSrc, - }, + subroutine::{LocalSubroutineId, SubroutineSrc}, }, source_map::ToAstNode, }; #[derive(Default, Debug)] pub(super) struct Source2DefCache { - container_map: FxHashMap, ContainerId>, + container_map: FxHashMap, ScopeId>, } pub(super) struct Source2DefCtx<'db, 'cache> { @@ -57,7 +55,7 @@ impl Source2DefCtx<'_, '_> { pub(super) fn subroutine_to_def( &mut self, InFile { file_id, value: subroutine_src }: InFile, - ) -> Option { + ) -> Option> { let tree = self.db.parse(file_id); let node = subroutine_src.to_node(&tree)?; self.subroutine_to_def_inner(file_id, node, subroutine_src) @@ -74,30 +72,30 @@ impl Source2DefCtx<'_, '_> { let container = self.find_container(InFile::new(file_id, node)); let block_id = match container { - ContainerId::HirFileId(file_id) => { + ScopeId::File(file_id) => { let (file, file_src_map) = self.db.hir_file_with_source_map(file_id); let local_block_id = find_local_block_id(&file_src_map.stmt_srcs, block_src)?; file.get(local_block_id).block_id } - ContainerId::ModuleId(module_id) => { + ScopeId::Module(module_id) => { let (module, module_src_map) = self.db.module_with_source_map(module_id); let local_block_id = find_local_block_id(&module_src_map.stmt_srcs, block_src)?; module.get(local_block_id).block_id } - ContainerId::BlockId(block_id) => { + ScopeId::Block(block_id) => { let (block, block_src_map) = self.db.block_with_source_map(block_id); let local_block_id = *block_src_map.block_srcs.get(&block_src)?; block.get(local_block_id).block_id } - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::GenerateBlock(generate_block_id) => { let (generate_block, generate_block_src_map) = self.db.generate_block_with_source_map(generate_block_id); let local_block_id = generate_block_src_map.get(block_src)?; generate_block.get(local_block_id).block_id } - ContainerId::SubroutineId(subroutine_id) => { + ScopeId::Subroutine(subroutine_id) => { let (subroutine, subroutine_src_map) = - self.db.subroutine_with_source_map(subroutine_id); + self.db.subroutine_with_source_map(subroutine_id.as_in_container()); let local_block_id = *subroutine_src_map.block_srcs.get(&block_src)?; subroutine.stmts.get(local_block_id).block_id } @@ -106,7 +104,7 @@ impl Source2DefCtx<'_, '_> { Some(block_id) } - fn container_to_def(&mut self, file_id: HirFileId, node: SyntaxNode) -> Option { + fn container_to_def(&mut self, file_id: HirFileId, node: SyntaxNode) -> Option { let cont_id = match_ast! { node, ast::ModuleDeclaration[module] => { let src = ModuleSrc::from_ast(file_id, module); @@ -151,7 +149,7 @@ impl Source2DefCtx<'_, '_> { file_id: HirFileId, node: ast::FunctionDeclaration, src: SubroutineSrc, - ) -> Option { + ) -> Option> { let parent = ast::Member::cast(node.syntax()) .and_then(|member| self.single_member_generate_block_to_def(file_id, member)) .or_else(|| { @@ -160,33 +158,29 @@ impl Source2DefCtx<'_, '_> { .find_map(|node| self.container_to_def(file_id, node)) }) .unwrap_or(file_id.into()); - let cont_id = SubroutineContainerId::try_from(parent).ok()?; - let local_id = self.local_subroutine_id(cont_id, src)?; - Some(self.db.intern_subroutine(SubroutineLoc { - cont_id, - src: InFile::new(file_id, src), - local_id, - })) + let local_id = self.local_subroutine_id(parent, src)?; + Some(InContainer::new(parent, local_id)) } fn local_subroutine_id( &self, - cont_id: SubroutineContainerId, + cont_id: ScopeId, src: SubroutineSrc, ) -> Option { match cont_id { - SubroutineContainerId::HirFileId(file_id) => { + ScopeId::File(file_id) => { let (_, source_map) = self.db.hir_file_with_source_map(file_id); source_map.get(src) } - SubroutineContainerId::ModuleId(module_id) => { + ScopeId::Module(module_id) => { let (_, source_map) = self.db.module_with_source_map(module_id); source_map.get(src) } - SubroutineContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::GenerateBlock(generate_block_id) => { let (_, source_map) = self.db.generate_block_with_source_map(generate_block_id); source_map.get(src) } + ScopeId::Block(_) | ScopeId::Subroutine(_) => None, } } @@ -194,7 +188,7 @@ impl Source2DefCtx<'_, '_> { &mut self, file_id: HirFileId, member: ast::Member, - ) -> Option { + ) -> Option { if matches!(member, ast::Member::GenerateBlock(_) | ast::Member::LoopGenerate(_)) { return None; } @@ -242,7 +236,7 @@ impl Source2DefCtx<'_, '_> { pub(super) fn find_container( &mut self, InFile { value: node, file_id }: InFile, - ) -> ContainerId { + ) -> ScopeId { let in_file = InFile::new(file_id, SyntaxNodePtr::from_node(node)); if let Some(container_id) = self.source_cache.container_map.get(&in_file) { diff --git a/crates/hir/src/symbol.rs b/crates/hir/src/symbol.rs new file mode 100644 index 00000000..c553a666 --- /dev/null +++ b/crates/hir/src/symbol.rs @@ -0,0 +1,455 @@ +use rustc_hash::FxHashMap; +use smallvec::SmallVec; +use utils::impl_from; + +use crate::{ + base_db::salsa, + container::{InContainer, InFile, InModule, InSubroutine}, + db::{HirDb, InternDb}, + hir_def::{ + Ident, + block::BlockId, + expr::declarator::DeclId, + file::{config::ConfigDeclId, library::LibraryDeclId, udp::UdpDeclId}, + module::{ + ModuleId, generate::GenerateBlockId, instantiation::InstanceId, port::NonAnsiPortId, + }, + stmt::StmtId, + subroutine::{LocalSubroutineId, SubroutinePortId}, + typedef::TypedefId, + }, +}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct DefId(pub salsa::InternId); + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum DefLoc { + Module(ModuleId), + Config(InFile), + Library(InFile), + Udp(InFile), + Block(BlockId), + GenerateBlock(GenerateBlockId), + Subroutine(InContainer), + SubroutinePort(InSubroutine), + NonAnsiPort(InModule), + Decl(InContainer), + Typedef(InContainer), + Instance(InModule), + Stmt(InContainer), +} + +impl_from! { DefLoc => + Module(ModuleId), + Config(InFile), + Library(InFile), + Udp(InFile), + Block(BlockId), + GenerateBlock(GenerateBlockId), + Subroutine(InContainer), + SubroutinePort(InSubroutine), + NonAnsiPort(InModule), + Decl(InContainer), + Typedef(InContainer), + Instance(InModule), + Stmt(InContainer), +} + +impl DefId { + pub fn new(db: &dyn InternDb, loc: impl Into) -> Self { + db.intern_def(loc.into()) + } + + pub fn loc(self, db: &dyn InternDb) -> DefLoc { + db.lookup_intern_def(self) + } + + pub fn as_module(self, db: &dyn InternDb) -> Option { + match self.loc(db) { + DefLoc::Module(id) => Some(id), + _ => None, + } + } + + pub fn as_config(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Config(id) => Some(id), + _ => None, + } + } + + pub fn as_library(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Library(id) => Some(id), + _ => None, + } + } + + pub fn as_udp(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Udp(id) => Some(id), + _ => None, + } + } + + pub fn as_block(self, db: &dyn InternDb) -> Option { + match self.loc(db) { + DefLoc::Block(id) => Some(id), + _ => None, + } + } + + pub fn as_generate_block(self, db: &dyn InternDb) -> Option { + match self.loc(db) { + DefLoc::GenerateBlock(id) => Some(id), + _ => None, + } + } + + pub fn as_subroutine(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Subroutine(id) => Some(id), + _ => None, + } + } + + pub fn as_subroutine_port(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::SubroutinePort(id) => Some(id), + _ => None, + } + } + + pub fn as_non_ansi_port(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::NonAnsiPort(id) => Some(id), + _ => None, + } + } + + pub fn as_decl(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Decl(id) => Some(id), + _ => None, + } + } + + pub fn as_typedef(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Typedef(id) => Some(id), + _ => None, + } + } + + pub fn as_instance(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Instance(id) => Some(id), + _ => None, + } + } + + pub fn as_stmt(self, db: &dyn InternDb) -> Option> { + match self.loc(db) { + DefLoc::Stmt(id) => Some(id), + _ => None, + } + } +} + +#[non_exhaustive] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum DefKind { + Module, + Interface, + Package, + Program, + Class, + Covergroup, + Checker, + Udp, + Config, + Library, + Block, + GenerateBlock, + Subroutine, + SubroutinePort, + NonAnsiPort, + Typedef, + Enum, + Struct, + Net, + Variable, + Param, + Port, + Genvar, + Specparam, + Instance, + ClassField, + Method, + Modport, + ClockingBlock, + Sequence, + Property, + Stmt, +} + +impl DefKind { + pub fn symbol_kind(self) -> SymbolKind { + match self { + DefKind::Module => SymbolKind::Module, + DefKind::Interface => SymbolKind::Interface, + DefKind::Package + | DefKind::Program + | DefKind::Class + | DefKind::Covergroup + | DefKind::Checker + | DefKind::Modport + | DefKind::ClockingBlock + | DefKind::Sequence + | DefKind::Property => SymbolKind::Unknown, + DefKind::Udp => SymbolKind::Primitive, + DefKind::Config => SymbolKind::Config, + DefKind::Library => SymbolKind::Library, + DefKind::Block => SymbolKind::Block, + DefKind::GenerateBlock => SymbolKind::Generate, + DefKind::Subroutine | DefKind::Method => SymbolKind::Fn, + DefKind::NonAnsiPort => SymbolKind::NonAnsiPortLabel, + DefKind::SubroutinePort | DefKind::Port => SymbolKind::PortDecl, + DefKind::Typedef | DefKind::Enum => SymbolKind::Typedef, + DefKind::Struct => SymbolKind::Struct, + DefKind::Net => SymbolKind::NetDecl, + DefKind::Variable | DefKind::ClassField => SymbolKind::DataDecl, + DefKind::Param => SymbolKind::ParamDecl, + DefKind::Genvar => SymbolKind::Genvar, + DefKind::Specparam => SymbolKind::Specparam, + DefKind::Instance => SymbolKind::Instance, + DefKind::Stmt => SymbolKind::Stmt, + } + } + + pub fn name_context(self) -> NameContext { + match self { + DefKind::Module + | DefKind::Interface + | DefKind::Package + | DefKind::Program + | DefKind::Class + | DefKind::Typedef + | DefKind::Enum + | DefKind::Struct => NameContext::Type, + _ => NameContext::Value, + } + } +} + +#[non_exhaustive] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ScopeKind { + File, + Package, + Module, + Interface, + Program, + Class, + GenerateBlock, + Block, + Subroutine, + Covergroup, + ClockingBlock, + Checker, +} + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct NameScope { + pub types: FxHashMap>, + pub values: FxHashMap>, + pub assertions: FxHashMap>, + pub imports: SmallVec<[Import; 2]>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Import { + pub package: Ident, + pub name: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum NameContext { + Type, + Value, + Listing, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum NameResolution { + Unique(T), + Ambiguous(SmallVec<[T; 2]>), + Unresolved, +} + +impl NameResolution { + pub fn unique(self) -> Option { + match self { + NameResolution::Unique(value) => Some(value), + NameResolution::Ambiguous(_) | NameResolution::Unresolved => None, + } + } +} + +impl NameScope { + pub fn insert_type(&mut self, ident: &Ident, def_id: DefId) { + Self::insert(&mut self.types, ident, def_id); + } + + pub fn insert_type_opt(&mut self, ident: &Option, def_id: DefId) { + if let Some(ident) = ident { + self.insert_type(ident, def_id); + } + } + + pub fn insert_value(&mut self, ident: &Ident, def_id: DefId) { + Self::insert(&mut self.values, ident, def_id); + } + + pub fn insert_value_opt(&mut self, ident: &Option, def_id: DefId) { + if let Some(ident) = ident { + self.insert_value(ident, def_id); + } + } + + pub fn insert_assertion(&mut self, ident: &Ident, def_id: DefId) { + Self::insert(&mut self.assertions, ident, def_id); + } + + pub fn lookup(&self, ctx: NameContext, ident: &Ident) -> Option> { + match ctx { + NameContext::Type => self.types.get(ident).map(|defs| SmallVec::from_slice(defs)), + NameContext::Value => self.values.get(ident).map(|defs| SmallVec::from_slice(defs)), + NameContext::Listing => self.lookup_listing(ident), + } + } + + pub fn lookup_listing(&self, ident: &Ident) -> Option> { + let mut defs = SmallVec::new(); + if let Some(type_defs) = self.types.get(ident) { + defs.extend_from_slice(type_defs); + } + if let Some(value_defs) = self.values.get(ident) { + defs.extend_from_slice(value_defs); + } + + (!defs.is_empty()).then_some(defs) + } + + pub fn iter_listing(&self) -> impl Iterator)> + '_ { + self.types + .iter() + .map(|(ident, type_defs)| { + let mut defs = SmallVec::from_slice(type_defs); + if let Some(value_defs) = self.values.get(ident) { + defs.extend_from_slice(value_defs); + } + (ident, defs) + }) + .chain( + self.values + .iter() + .filter(|(ident, _)| !self.types.contains_key(*ident)) + .map(|(ident, defs)| (ident, SmallVec::from_slice(defs))), + ) + } + + pub fn module_ids( + &self, + db: &dyn HirDb, + ident: &Ident, + ) -> NameResolution { + let entries = self + .types + .get(ident) + .into_iter() + .flat_map(|defs| defs.iter()) + .filter(|def_id| def_id.kind(db) == DefKind::Module) + .filter_map(|def_id| def_id.as_module(db)) + .collect::>(); + + match entries.as_slice() { + [module_id] => NameResolution::Unique(*module_id), + [] => NameResolution::Unresolved, + _ => NameResolution::Ambiguous(entries), + } + } + + pub fn package_ids( + &self, + db: &dyn HirDb, + ident: &Ident, + ) -> NameResolution { + let entries = self + .types + .get(ident) + .into_iter() + .flat_map(|defs| defs.iter()) + .filter(|def_id| def_id.kind(db) == DefKind::Package) + .filter_map(|def_id| def_id.as_module(db)) + .collect::>(); + + match entries.as_slice() { + [package_id] => NameResolution::Unique(*package_id), + [] => NameResolution::Unresolved, + _ => NameResolution::Ambiguous(entries), + } + } + + pub fn module_names<'a>(&'a self, db: &'a dyn HirDb) -> impl Iterator + 'a { + self.types.iter().filter_map(move |(ident, defs)| { + defs.iter() + .any(|def_id| def_id.kind(db) == DefKind::Module && def_id.as_module(db).is_some()) + .then_some(ident) + }) + } + + pub fn typedef_names<'a>( + &'a self, + db: &'a dyn InternDb, + ) -> impl Iterator + 'a { + self.types.iter().filter_map(move |(ident, defs)| { + defs.iter().any(|def_id| matches!(def_id.loc(db), DefLoc::Typedef(_))).then_some(ident) + }) + } + + fn insert(map: &mut FxHashMap>, ident: &Ident, def_id: DefId) { + let defs = map.entry(ident.clone()).or_default(); + if !defs.contains(&def_id) { + defs.push(def_id); + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum SymbolKind { + Module, + Config, + Primitive, + NonAnsiPortLabel, + PortDecl, + ParamDecl, + NetDecl, + DataDecl, + Genvar, + Specparam, + Typedef, + Struct, + Instance, + Block, + Stmt, + Fn, + Generate, + Specify, + Interface, + Library, + Region, + Unknown, +} diff --git a/crates/hir/src/type_infer.rs b/crates/hir/src/type_infer.rs index 4bf8ba00..abeeb05a 100644 --- a/crates/hir/src/type_infer.rs +++ b/crates/hir/src/type_infer.rs @@ -3,7 +3,7 @@ use triomphe::Arc; use utils::get::GetRef; use crate::{ - container::{ContainerId, InContainer, InGenerateBlock, InModule, InSubroutine}, + container::{InContainer, InSubroutine, ScopeId}, db::HirDb, hir_def::{ Ident, @@ -15,17 +15,18 @@ use crate::{ declarator::{DeclId, DeclaratorParent}, }, literal::Literal, - module::{ModuleId, generate::GenerateBlockId, port::PortDeclId}, + module::{ModuleId, ModuleKind, generate::GenerateBlockId, port::PortDeclId}, stmt::{ForInit, StmtKind}, subroutine::SubroutinePortId, typedef::TypedefId, }, semantics::pathres::{PathResolution, resolve_name}, + symbol::{DefId, DefKind, NameContext}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BuiltinTy { - Data { id: BuiltinDataTyId, container: ContainerId }, + Data { id: BuiltinDataTyId, container: ScopeId }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -72,7 +73,7 @@ pub enum TyClass { String, } -pub fn normalize_data_ty(db: &dyn HirDb, container: ContainerId, data_ty: DataTy) -> TyResult { +pub fn normalize_data_ty(db: &dyn HirDb, container: ScopeId, data_ty: DataTy) -> TyResult { normalize_data_ty_inner(db, container, data_ty, &mut FxHashSet::default()) } @@ -114,32 +115,74 @@ fn type_of_decl_impl(db: &dyn HirDb, decl: InContainer) -> TyResult { } fn type_of_path_resolution_impl(db: &dyn HirDb, res: PathResolution) -> TyResult { - match res { - PathResolution::Module(module_id) => TyResult::new(Ty::Module(module_id)), - PathResolution::Decl(decl) => type_of_decl_impl(db, decl), - PathResolution::Typedef(typedef) => type_of_typedef_impl(db, typedef), - PathResolution::ParamDecl(decl) | PathResolution::AnsiPort(decl) => { - type_of_decl_impl(db, decl.into()) + let mut port_ty = None; + for def_id in res.def_ids() { + let ty = type_of_def_id(db, *def_id); + match def_id.kind(db) { + DefKind::NonAnsiPort => {} + DefKind::Port | DefKind::SubroutinePort => { + port_ty.get_or_insert(ty); + } + _ if !matches!(ty.ty, Ty::Unknown) => return ty, + _ => {} } - PathResolution::NonAnsiPort { port_decl, data_decl, module, .. } => data_decl - .or(port_decl) - .map(|decl| type_of_decl_impl(db, InContainer::new(module.into(), decl))) + } + port_ty.unwrap_or_else(|| TyResult::new(Ty::Unknown)) +} + +fn type_of_def_id(db: &dyn HirDb, def_id: DefId) -> TyResult { + match def_id.kind(db) { + DefKind::Module | DefKind::Package => def_id + .as_module(db) + .map(|module_id| TyResult::new(Ty::Module(module_id))) .unwrap_or_else(|| TyResult::new(Ty::Unknown)), - PathResolution::SubroutinePort(port) => type_of_subroutine_port_impl(db, port), - PathResolution::Instance(instance) => { - instance_target_module_id(db, instance.module_id, instance.value) - .map(|module_id| TyResult::new(Ty::Module(module_id))) - .unwrap_or_else(|| TyResult::new(Ty::Unknown)) - } - PathResolution::GenerateBlock(generate_block_id) => { - TyResult::new(Ty::GenerateBlock(generate_block_id)) - } - PathResolution::Block(block_id) => TyResult::new(Ty::Block(block_id)), - PathResolution::Config(_) - | PathResolution::Library(_) - | PathResolution::Udp(_) - | PathResolution::Subroutine(_) - | PathResolution::Stmt(_) => TyResult::new(Ty::Unknown), + DefKind::Port + | DefKind::Variable + | DefKind::Net + | DefKind::Param + | DefKind::Genvar + | DefKind::Specparam => def_id + .as_decl(db) + .map(|decl| type_of_decl_impl(db, decl)) + .unwrap_or_else(|| TyResult::new(Ty::Unknown)), + DefKind::Typedef | DefKind::Enum | DefKind::Struct => def_id + .as_typedef(db) + .map(|typedef| type_of_typedef_impl(db, typedef)) + .unwrap_or_else(|| TyResult::new(Ty::Unknown)), + DefKind::SubroutinePort => def_id + .as_subroutine_port(db) + .map(|port| type_of_subroutine_port_impl(db, port)) + .unwrap_or_else(|| TyResult::new(Ty::Unknown)), + DefKind::Instance => def_id + .as_instance(db) + .and_then(|instance| instance_target_module_id(db, instance.module_id, instance.value)) + .map(|module_id| TyResult::new(Ty::Module(module_id))) + .unwrap_or_else(|| TyResult::new(Ty::Unknown)), + DefKind::GenerateBlock => def_id + .as_generate_block(db) + .map(|generate_block_id| TyResult::new(Ty::GenerateBlock(generate_block_id))) + .unwrap_or_else(|| TyResult::new(Ty::Unknown)), + DefKind::Block => def_id + .as_block(db) + .map(|block_id| TyResult::new(Ty::Block(block_id))) + .unwrap_or_else(|| TyResult::new(Ty::Unknown)), + DefKind::Interface + | DefKind::Program + | DefKind::Class + | DefKind::Covergroup + | DefKind::Checker + | DefKind::Udp + | DefKind::Config + | DefKind::Library + | DefKind::Subroutine + | DefKind::NonAnsiPort + | DefKind::ClassField + | DefKind::Method + | DefKind::Modport + | DefKind::ClockingBlock + | DefKind::Sequence + | DefKind::Property + | DefKind::Stmt => TyResult::new(Ty::Unknown), } } @@ -149,7 +192,7 @@ fn type_of_expr_impl(db: &dyn HirDb, expr: InContainer) -> TyResult { }; match hir_expr { - Expr::Ident(ident) => resolve_name(db, expr.cont_id, &ident) + Expr::Ident(ident) => resolve_name(db, expr.cont_id, &ident, NameContext::Value) .map(|res| type_of_path_resolution_impl(db, res)) .unwrap_or_else(|| TyResult::new(Ty::Unknown)), Expr::Field { receiver, field } => { @@ -268,7 +311,7 @@ pub fn packed_bit_width(db: &dyn HirDb, ty: &Ty) -> Option { fn normalize_data_ty_inner( db: &dyn HirDb, - container: ContainerId, + container: ScopeId, data_ty: DataTy, seen: &mut FxHashSet>, ) -> TyResult { @@ -290,7 +333,7 @@ fn normalize_data_ty_inner( fn type_of_named_data_ty( db: &dyn HirDb, - container: ContainerId, + container: ScopeId, named: NamedDataTy, seen: &mut FxHashSet>, ) -> TyResult { @@ -301,9 +344,15 @@ fn type_of_named_data_ty( return TyResult::new(Ty::Unknown); }; - match resolve_name(db, container, &ident) { - Some(PathResolution::Typedef(typedef)) => type_of_typedef_inner(db, typedef, seen), - Some(res) => type_of_path_resolution_impl(db, res), + match resolve_name(db, container, &ident, NameContext::Type) { + Some(res) => { + for def_id in res.def_ids() { + if let Some(typedef) = def_id.as_typedef(db) { + return type_of_typedef_inner(db, typedef, seen); + } + } + type_of_path_resolution_impl(db, res) + } None => TyResult::new(Ty::Unknown), } } @@ -358,13 +407,19 @@ fn struct_members(db: &dyn HirDb, struct_id: InContainer) -> Vec Vec { - let mut members: Vec<_> = db - .module_scope(module_id) - .iter() - .map(|(name, entry)| { - let origin = PathResolution::from(InModule::new(module_id, entry)); - let ty = type_of_path_resolution_impl(db, origin).ty; - TyMember { name: name.clone(), ty, origin: Some(origin) } + let file = db.hir_file(crate::file::HirFileId::File(module_id.file_id())); + let scope = if file.get(module_id.value).kind == ModuleKind::Package { + db.package_export_scope(module_id) + } else { + db.module_scope(module_id) + }; + + let mut members: Vec<_> = scope + .iter_listing() + .filter_map(|(name, defs)| { + let origin = PathResolution::from_def_ids(defs)?; + let ty = type_of_path_resolution_impl(db, origin.clone()).ty; + Some(TyMember { name: name.clone(), ty, origin: Some(origin) }) }) .collect(); sort_members(&mut members); @@ -374,11 +429,11 @@ fn module_members(db: &dyn HirDb, module_id: ModuleId) -> Vec { fn generate_block_members(db: &dyn HirDb, generate_block_id: GenerateBlockId) -> Vec { let mut members: Vec<_> = db .generate_block_scope(generate_block_id) - .iter() - .map(|(name, entry)| { - let origin = PathResolution::from(InGenerateBlock::new(generate_block_id, entry)); - let ty = type_of_path_resolution_impl(db, origin).ty; - TyMember { name: name.clone(), ty, origin: Some(origin) } + .iter_listing() + .filter_map(|(name, defs)| { + let origin = PathResolution::from_def_ids(defs)?; + let ty = type_of_path_resolution_impl(db, origin.clone()).ty; + Some(TyMember { name: name.clone(), ty, origin: Some(origin) }) }) .collect(); sort_members(&mut members); @@ -388,11 +443,11 @@ fn generate_block_members(db: &dyn HirDb, generate_block_id: GenerateBlockId) -> fn block_members(db: &dyn HirDb, block_id: crate::hir_def::block::BlockId) -> Vec { let mut members: Vec<_> = db .block_scope(block_id) - .iter() - .map(|(name, entry)| { - let origin = PathResolution::from(crate::container::InBlock::new(block_id, entry)); - let ty = type_of_path_resolution_impl(db, origin).ty; - TyMember { name: name.clone(), ty, origin: Some(origin) } + .iter_listing() + .filter_map(|(name, defs)| { + let origin = PathResolution::from_def_ids(defs)?; + let ty = type_of_path_resolution_impl(db, origin.clone()).ty; + Some(TyMember { name: name.clone(), ty, origin: Some(origin) }) }) .collect(); sort_members(&mut members); @@ -417,8 +472,8 @@ fn data_ty_of_decl(db: &dyn HirDb, decl: InContainer) -> Option } } -fn port_decl_ty(db: &dyn HirDb, cont_id: ContainerId, port_decl_id: PortDeclId) -> Option { - let ContainerId::ModuleId(module_id) = cont_id else { +fn port_decl_ty(db: &dyn HirDb, cont_id: ScopeId, port_decl_id: PortDeclId) -> Option { + let ScopeId::Module(module_id) = cont_id else { return None; }; let module = db.module(module_id); @@ -427,7 +482,7 @@ fn port_decl_ty(db: &dyn HirDb, cont_id: ContainerId, port_decl_id: PortDeclId) fn for_init_decl_ty( db: &dyn HirDb, - cont_id: ContainerId, + cont_id: ScopeId, stmt_id: crate::hir_def::stmt::StmtId, decl_id: DeclId, ) -> Option { @@ -445,7 +500,7 @@ fn type_of_subroutine_port_impl(db: &dyn HirDb, port: InSubroutine usize { @@ -472,7 +527,7 @@ fn int_kind_width(kind: IntKind) -> usize { } } -fn eval_const_i128(db: &dyn HirDb, container: ContainerId, expr_id: ExprId) -> Option { +fn eval_const_i128(db: &dyn HirDb, container: ScopeId, expr_id: ExprId) -> Option { match expr_of(db, InContainer::new(container, expr_id))? { Expr::Literal(Literal::Int(int)) => int.get_single_word().map(|v| v as i128), Expr::Unary { op, expr } => { @@ -510,14 +565,14 @@ fn eval_const_i128(db: &dyn HirDb, container: ContainerId, expr_id: ExprId) -> O fn expr_of(db: &dyn HirDb, expr: InContainer) -> Option { match expr.cont_id { - ContainerId::HirFileId(file_id) => Some(db.hir_file(file_id).get(expr.value).clone()), - ContainerId::ModuleId(module_id) => Some(db.module(module_id).get(expr.value).clone()), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => Some(db.hir_file(file_id).get(expr.value).clone()), + ScopeId::Module(module_id) => Some(db.module(module_id).get(expr.value).clone()), + ScopeId::GenerateBlock(generate_block_id) => { Some(db.generate_block(generate_block_id).get(expr.value).clone()) } - ContainerId::BlockId(block_id) => Some(db.block(block_id).get(expr.value).clone()), - ContainerId::SubroutineId(subroutine_id) => { - Some(db.subroutine(subroutine_id).get(expr.value).clone()) + ScopeId::Block(block_id) => Some(db.block(block_id).get(expr.value).clone()), + ScopeId::Subroutine(subroutine_id) => { + Some(db.subroutine(subroutine_id.as_in_container()).get(expr.value).clone()) } } } @@ -527,14 +582,14 @@ fn decl_of( decl: InContainer, ) -> Option { match decl.cont_id { - ContainerId::HirFileId(file_id) => Some(db.hir_file(file_id).get(decl.value).clone()), - ContainerId::ModuleId(module_id) => Some(db.module(module_id).get(decl.value).clone()), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => Some(db.hir_file(file_id).get(decl.value).clone()), + ScopeId::Module(module_id) => Some(db.module(module_id).get(decl.value).clone()), + ScopeId::GenerateBlock(generate_block_id) => { Some(db.generate_block(generate_block_id).get(decl.value).clone()) } - ContainerId::BlockId(block_id) => Some(db.block(block_id).get(decl.value).clone()), - ContainerId::SubroutineId(subroutine_id) => { - Some(db.subroutine(subroutine_id).get(decl.value).clone()) + ScopeId::Block(block_id) => Some(db.block(block_id).get(decl.value).clone()), + ScopeId::Subroutine(subroutine_id) => { + Some(db.subroutine(subroutine_id.as_in_container()).get(decl.value).clone()) } } } @@ -544,14 +599,14 @@ fn declaration_of( decl: InContainer, ) -> Option { match decl.cont_id { - ContainerId::HirFileId(file_id) => Some(db.hir_file(file_id).get(decl.value).clone()), - ContainerId::ModuleId(module_id) => Some(db.module(module_id).get(decl.value).clone()), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => Some(db.hir_file(file_id).get(decl.value).clone()), + ScopeId::Module(module_id) => Some(db.module(module_id).get(decl.value).clone()), + ScopeId::GenerateBlock(generate_block_id) => { Some(db.generate_block(generate_block_id).get(decl.value).clone()) } - ContainerId::BlockId(block_id) => Some(db.block(block_id).get(decl.value).clone()), - ContainerId::SubroutineId(subroutine_id) => { - Some(db.subroutine(subroutine_id).get(decl.value).clone()) + ScopeId::Block(block_id) => Some(db.block(block_id).get(decl.value).clone()), + ScopeId::Subroutine(subroutine_id) => { + Some(db.subroutine(subroutine_id.as_in_container()).get(decl.value).clone()) } } } @@ -561,14 +616,14 @@ fn typedef_of( typedef: InContainer, ) -> Option { match typedef.cont_id { - ContainerId::HirFileId(file_id) => Some(db.hir_file(file_id).get(typedef.value).clone()), - ContainerId::ModuleId(module_id) => Some(db.module(module_id).get(typedef.value).clone()), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => Some(db.hir_file(file_id).get(typedef.value).clone()), + ScopeId::Module(module_id) => Some(db.module(module_id).get(typedef.value).clone()), + ScopeId::GenerateBlock(generate_block_id) => { Some(db.generate_block(generate_block_id).get(typedef.value).clone()) } - ContainerId::BlockId(block_id) => Some(db.block(block_id).get(typedef.value).clone()), - ContainerId::SubroutineId(subroutine_id) => { - Some(db.subroutine(subroutine_id).get(typedef.value).clone()) + ScopeId::Block(block_id) => Some(db.block(block_id).get(typedef.value).clone()), + ScopeId::Subroutine(subroutine_id) => { + Some(db.subroutine(subroutine_id.as_in_container()).get(typedef.value).clone()) } } } @@ -578,14 +633,14 @@ fn struct_of( struct_id: InContainer, ) -> Option { match struct_id.cont_id { - ContainerId::HirFileId(file_id) => Some(db.hir_file(file_id).get(struct_id.value).clone()), - ContainerId::ModuleId(module_id) => Some(db.module(module_id).get(struct_id.value).clone()), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => Some(db.hir_file(file_id).get(struct_id.value).clone()), + ScopeId::Module(module_id) => Some(db.module(module_id).get(struct_id.value).clone()), + ScopeId::GenerateBlock(generate_block_id) => { Some(db.generate_block(generate_block_id).get(struct_id.value).clone()) } - ContainerId::BlockId(block_id) => Some(db.block(block_id).get(struct_id.value).clone()), - ContainerId::SubroutineId(subroutine_id) => { - Some(db.subroutine(subroutine_id).get(struct_id.value).clone()) + ScopeId::Block(block_id) => Some(db.block(block_id).get(struct_id.value).clone()), + ScopeId::Subroutine(subroutine_id) => { + Some(db.subroutine(subroutine_id.as_in_container()).get(struct_id.value).clone()) } } } @@ -595,14 +650,14 @@ fn stmt_of( stmt: InContainer, ) -> Option { match stmt.cont_id { - ContainerId::HirFileId(file_id) => Some(db.hir_file(file_id).get(stmt.value).clone()), - ContainerId::ModuleId(module_id) => Some(db.module(module_id).get(stmt.value).clone()), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => Some(db.hir_file(file_id).get(stmt.value).clone()), + ScopeId::Module(module_id) => Some(db.module(module_id).get(stmt.value).clone()), + ScopeId::GenerateBlock(generate_block_id) => { Some(db.generate_block(generate_block_id).get(stmt.value).clone()) } - ContainerId::BlockId(block_id) => Some(db.block(block_id).get(stmt.value).clone()), - ContainerId::SubroutineId(subroutine_id) => { - Some(db.subroutine(subroutine_id).get(stmt.value).clone()) + ScopeId::Block(block_id) => Some(db.block(block_id).get(stmt.value).clone()), + ScopeId::Subroutine(subroutine_id) => { + Some(db.subroutine(subroutine_id.as_in_container()).get(stmt.value).clone()) } } } diff --git a/crates/ide/src/code_action/handlers/convert_port_declarations.rs b/crates/ide/src/code_action/handlers/convert_port_declarations.rs index a159330d..76a50125 100644 --- a/crates/ide/src/code_action/handlers/convert_port_declarations.rs +++ b/crates/ide/src/code_action/handlers/convert_port_declarations.rs @@ -14,8 +14,8 @@ use hir::{ port::{PortDecl, PortDeclSrc, Ports}, }, }, - scope::{ModuleEntry, ModuleScope, NonAnsiPortEntry}, source_map::IsSrc, + symbol::{DefKind, NameContext, NameScope}, }; use itertools::Itertools; use syntax::{ @@ -216,18 +216,30 @@ fn non_ansi_port_replacement( ctx: &CodeActionCtx, module: &Module, module_src_map: &ModuleSourceMap, - module_scope: &ModuleScope, + module_scope: &NameScope, name: &Ident, text: &str, ) -> Option { - let ModuleEntry::NonAnsiPortEntry(NonAnsiPortEntry { - port_decl: Some(port_decl), - data_decl, - .. - }) = module_scope.get(name)? - else { + let defs = module_scope.lookup(NameContext::Value, name)?; + if !defs.iter().any(|def_id| def_id.kind(ctx.sema().db) == DefKind::NonAnsiPort) { return None; - }; + } + + let port_decl = defs + .iter() + .filter_map(|def_id| def_id.as_decl(ctx.sema().db)) + .find(|decl_id| { + matches!(module.get(decl_id.value).parent, DeclaratorParent::PortDeclId(_)) + })? + .value; + let data_decl = defs + .iter() + .filter_map(|def_id| def_id.as_decl(ctx.sema().db)) + .find(|decl_id| { + matches!(module.get(decl_id.value).parent, DeclaratorParent::DeclarationId(_)) + }) + .map(|decl_id| decl_id.value); + let DeclaratorParent::PortDeclId(port_decl_id) = module.get(port_decl).parent else { return None; }; diff --git a/crates/ide/src/code_lens.rs b/crates/ide/src/code_lens.rs index 83c71f63..a422c07d 100644 --- a/crates/ide/src/code_lens.rs +++ b/crates/ide/src/code_lens.rs @@ -1,6 +1,5 @@ use hir::{ db::{HirDb, InternDb}, - def_id::ModuleDefOrigin, file::HirFileId, hir_def::{ file::{FileSourceMap, HirFile}, @@ -8,6 +7,7 @@ use hir::{ }, semantics::Semantics, source_map::IsSrc, + symbol::DefId, }; use syntax::{ ast::{self, AstNode}, @@ -87,8 +87,8 @@ pub(crate) fn code_lens_resolve(db: &RootDb, mut kind: CodeLensKind) -> CodeLens }; let module_id = ModuleId::new(hir_file_id, local_module_id); - let def = ModuleDefOrigin::ModuleId(module_id); - let def = hir::def_id::ModuleDef::from_origins([def]) + let def = DefId::new(sema.db, module_id); + let def = hir::def_id::ModuleDef::from_def_ids([def]) .expect("module definition should have an origin"); let def = sema.db.intern_module_def(def); diff --git a/crates/ide/src/completion/engine/expr.rs b/crates/ide/src/completion/engine/expr.rs index c38add31..72cc5f47 100644 --- a/crates/ide/src/completion/engine/expr.rs +++ b/crates/ide/src/completion/engine/expr.rs @@ -1,19 +1,16 @@ use std::collections::BTreeMap; use hir::{ - container::{ContainerId, ContainerParent, InContainer, InSubroutine}, + container::{InContainer, ScopeId, ScopeParent}, db::HirDb, file::HirFileId, hir_def::{ lower_ident_opt, module::ModuleId, - subroutine::{SubroutineId, SubroutineKind}, - }, - scope::{ - AnsiPortEntry, BlockEntry, GenerateBlockEntry, ModuleEntry, NonAnsiPortEntry, - SubroutineEntry, UnitEntry, + subroutine::{LocalSubroutineId, SubroutineKind}, }, semantics::{Semantics, pathres::PathResolution}, + symbol::{DefId, DefKind}, type_infer::{Ty, normalize_data_ty, type_class}, }; use syntax::{ @@ -68,7 +65,7 @@ fn complete_expression_impl( if let Some(container_id) = container_id_at_offset(&sema, file_id, root, position.offset) { current_module_id = module_id_for_container(db, container_id); - for container_id in ContainerParent::start_from(db, container_id) { + for container_id in ScopeParent::start_from(db, container_id) { collect_container_names(db, container_id, &mut names); } } @@ -102,7 +99,7 @@ fn container_id_at_offset( file_id: HirFileId, root: SyntaxNode<'_>, offset: TextSize, -) -> Option { +) -> Option { let elem = root.covering_element(utils::line_index::TextRange::empty(offset)); let node = elem.as_node().or_else(|| elem.parent())?; sema.container_for_node(file_id, node) @@ -110,60 +107,28 @@ fn container_id_at_offset( fn collect_container_names( db: &RootDb, - container_id: ContainerId, + container_id: ScopeId, names: &mut BTreeMap, ) { match container_id { - ContainerId::HirFileId(file_id) => collect_file_names(db, file_id, names), - ContainerId::ModuleId(module_id) => collect_module_names(db, module_id, names), - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::File(file_id) => collect_file_names(db, file_id, names), + ScopeId::Module(module_id) => collect_module_names(db, module_id, names), + ScopeId::GenerateBlock(generate_block_id) => { let scope = db.generate_block_scope(generate_block_id); - for (ident, entry) in scope.iter() { - match entry { - GenerateBlockEntry::DeclId(decl_id) => { - names.entry(ident.to_string()).or_insert(NameKind::Value { - ty: db.type_of_decl(InContainer::new(container_id, decl_id)).ty.clone(), - }); - } - GenerateBlockEntry::SubroutineId(subroutine_id) => { - names.entry(ident.to_string()).or_insert(NameKind::SubroutineCall { - return_ty: subroutine_return_ty(db, subroutine_id), - }); - } - _ => {} - } + for (ident, defs) in scope.iter_listing() { + collect_def_names(db, ident, defs, names); } } - ContainerId::BlockId(block_id) => { + ScopeId::Block(block_id) => { let scope = db.block_scope(block_id); - for (ident, entry) in scope.iter() { - if let BlockEntry::DeclId(decl_id) = entry { - names.entry(ident.to_string()).or_insert(NameKind::Value { - ty: db.type_of_decl(InContainer::new(container_id, decl_id)).ty.clone(), - }); - } + for (ident, defs) in scope.iter_listing() { + collect_def_names(db, ident, defs, names); } } - ContainerId::SubroutineId(subroutine_id) => { - let scope = db.subroutine_scope(subroutine_id); - for (ident, entry) in scope.iter() { - match entry { - SubroutineEntry::DeclId(decl_id) => { - names.entry(ident.to_string()).or_insert(NameKind::Value { - ty: db.type_of_decl(InContainer::new(container_id, decl_id)).ty.clone(), - }); - } - SubroutineEntry::SubroutinePortId(port_id) => { - let ty = db - .type_of_path_resolution(PathResolution::SubroutinePort( - InSubroutine::new(subroutine_id, port_id), - )) - .ty - .clone(); - names.entry(ident.to_string()).or_insert(NameKind::Value { ty }); - } - _ => {} - } + ScopeId::Subroutine(subroutine_id) => { + let scope = db.subroutine_scope(subroutine_id.as_in_container()); + for (ident, defs) in scope.iter_listing() { + collect_def_names(db, ident, defs, names); } } } @@ -171,63 +136,64 @@ fn collect_container_names( fn collect_file_names(db: &RootDb, file_id: HirFileId, names: &mut BTreeMap) { let scope = db.file_scope(file_id); - for (ident, entry) in scope.iter() { - if let UnitEntry::FiledDeclId(decl_id) = entry { - names.entry(ident.to_string()).or_insert(NameKind::Value { - ty: db - .type_of_decl(InContainer::new(ContainerId::HirFileId(file_id), decl_id.value)) - .ty - .clone(), - }); - } + for (ident, defs) in scope.iter_listing() { + collect_def_names(db, ident, defs, names); } } fn collect_module_names(db: &RootDb, module_id: ModuleId, names: &mut BTreeMap) { let scope = db.module_scope(module_id); - for (ident, entry) in scope.iter() { - match entry { - ModuleEntry::DeclId(decl_id) => { - names.entry(ident.to_string()).or_insert(NameKind::Value { - ty: db.type_of_decl(InContainer::new(module_id.into(), decl_id)).ty.clone(), - }); - } - ModuleEntry::AnsiPortEntry(AnsiPortEntry(decl_id)) => { - names.entry(ident.to_string()).or_insert(NameKind::Value { - ty: db.type_of_decl(InContainer::new(module_id.into(), decl_id)).ty.clone(), - }); - } - ModuleEntry::NonAnsiPortEntry(NonAnsiPortEntry { port_decl, data_decl, .. }) => { - let ty = data_decl - .or(port_decl) - .map(|decl_id| { - db.type_of_decl(InContainer::new(module_id.into(), decl_id)).ty.clone() - }) - .unwrap_or(Ty::Unknown); - names.entry(ident.to_string()).or_insert(NameKind::Value { ty }); - } - ModuleEntry::SubroutineId(subroutine_id) => { - names.entry(ident.to_string()).or_insert(NameKind::SubroutineCall { - return_ty: subroutine_return_ty(db, subroutine_id), - }); - } - _ => {} - } + for (ident, defs) in scope.iter_listing() { + collect_def_names(db, ident, defs, names); + } +} + +fn collect_def_names( + db: &RootDb, + ident: &hir::hir_def::Ident, + defs: impl IntoIterator, + names: &mut BTreeMap, +) { + let defs = defs.into_iter().collect::>(); + + if let Some(subroutine_id) = defs.iter().find_map(|def_id| def_id.as_subroutine(db)) { + names.entry(ident.to_string()).or_insert(NameKind::SubroutineCall { + return_ty: subroutine_return_ty(db, subroutine_id), + }); + return; + } + + if defs.iter().any(|def_id| { + matches!( + def_id.kind(db), + DefKind::Variable + | DefKind::Net + | DefKind::Param + | DefKind::Port + | DefKind::Genvar + | DefKind::Specparam + | DefKind::SubroutinePort + | DefKind::NonAnsiPort + ) + }) && let Some(res) = PathResolution::from_def_ids(defs.iter().copied()) + { + let ty = db.type_of_path_resolution(res).ty.clone(); + names.entry(ident.to_string()).or_insert(NameKind::Value { ty }); } } -fn subroutine_return_ty(db: &RootDb, subroutine_id: SubroutineId) -> Ty { +fn subroutine_return_ty(db: &RootDb, subroutine_id: InContainer) -> Ty { match db.subroutine(subroutine_id).kind { SubroutineKind::Function { return_ty: Some(return_ty) } => { - normalize_data_ty(db, ContainerId::SubroutineId(subroutine_id), return_ty).ty + normalize_data_ty(db, subroutine_id.into(), return_ty).ty } SubroutineKind::Function { return_ty: None } | SubroutineKind::Task => Ty::Unknown, } } -fn module_id_for_container(db: &RootDb, container_id: ContainerId) -> Option { - ContainerParent::start_from(db, container_id).find_map(|container_id| match container_id { - ContainerId::ModuleId(module_id) => Some(module_id), +fn module_id_for_container(db: &RootDb, container_id: ScopeId) -> Option { + ScopeParent::start_from(db, container_id).find_map(|container_id| match container_id { + ScopeId::Module(module_id) => Some(module_id), _ => None, }) } diff --git a/crates/ide/src/completion/engine/keywords.rs b/crates/ide/src/completion/engine/keywords.rs index 2fa0f860..99b77a76 100644 --- a/crates/ide/src/completion/engine/keywords.rs +++ b/crates/ide/src/completion/engine/keywords.rs @@ -39,16 +39,13 @@ fn module_instantiation_snippets( ctx: &CompletionContext, enabled: bool, ) -> Vec { - use hir::scope::UnitEntry; - if !enabled || prefix.is_empty() { return Vec::new(); } let mut modules: Vec = db .unit_scope() - .iter() - .filter_map(|(ident, entry)| matches!(entry, UnitEntry::ModuleId(_)).then_some(ident)) + .module_names(db) .map(|ident| ident.to_string()) .filter(|name| name.starts_with(prefix)) .collect(); diff --git a/crates/ide/src/completion/engine/member.rs b/crates/ide/src/completion/engine/member.rs index ecc34164..6f356c6e 100644 --- a/crates/ide/src/completion/engine/member.rs +++ b/crates/ide/src/completion/engine/member.rs @@ -2,10 +2,11 @@ use hir::{ db::HirDb, file::HirFileId, semantics::Semantics, + symbol::NameContext, type_infer::{TyMember, members_of_ty}, }; use syntax::{ - SyntaxAncestors, SyntaxNode, SyntaxNodeExt, + SyntaxAncestors, SyntaxNode, SyntaxNodeExt, SyntaxTokenWithParent, ast::{self, AstNode}, has_text_range::HasTextRange, }; @@ -84,7 +85,7 @@ fn members_for_incomplete_scoped_access( return None; } let left = root.token_before_offset(separator.text_range()?.start())?; - let res = sema.nameres_ident(file_id, left)?; + let res = sema.nameres_ident(file_id, left, NameContext::Type)?; let ty = db.type_of_path_resolution(res); let members = members_of_ty(db, &ty.ty); (!members.is_empty()).then_some(members) @@ -136,6 +137,26 @@ fn members_for_scoped_name( file_id: HirFileId, scoped: ast::ScopedName<'_>, ) -> Option> { + if let Some(left) = scoped_left_token(scoped) { + let res = sema.nameres_ident(file_id, left, NameContext::Type)?; + let ty = db.type_of_path_resolution(res); + let members = members_of_ty(db, &ty.ty); + return (!members.is_empty()).then_some(members); + } + let left = ast::Expression::cast(scoped.left().syntax())?; members_for_expr(db, sema, file_id, left) } + +fn scoped_left_token(scoped: ast::ScopedName<'_>) -> Option> { + use ast::Name::*; + match scoped.left() { + IdentifierName(ident) => { + Some(SyntaxTokenWithParent { parent: ident.syntax(), tok: ident.identifier()? }) + } + IdentifierSelectName(ident) => { + Some(SyntaxTokenWithParent { parent: ident.syntax(), tok: ident.identifier()? }) + } + _ => None, + } +} diff --git a/crates/ide/src/completion/engine/paren_list.rs b/crates/ide/src/completion/engine/paren_list.rs index 848d712e..e907b541 100644 --- a/crates/ide/src/completion/engine/paren_list.rs +++ b/crates/ide/src/completion/engine/paren_list.rs @@ -4,7 +4,6 @@ use hir::{ Ident, lower_ident_opt, module::{ModuleId, ModuleSrc}, }, - scope::{ModuleEntry, UnitEntry}, semantics::Semantics, }; use rustc_hash::FxHashSet; @@ -98,11 +97,8 @@ fn complete_parameter_port_list_with_typedefs( let mut items: Vec = db .unit_scope() - .iter() - .filter_map(|(ident, entry)| matches!(entry, UnitEntry::FiledTypedefId(_)).then_some(ident)) - .chain(db.module_scope(module_id).iter().filter_map(|(ident, entry)| { - matches!(entry, ModuleEntry::TypedefId(_)).then_some(ident) - })) + .typedef_names(db) + .chain(db.module_scope(module_id).typedef_names(db)) .map(|ident| ident.to_string()) .filter(|name| name.starts_with(prefix)) .map(|name| CompletionCandidate::text(name, ctx.replacement)) diff --git a/crates/ide/src/completion/engine/port_list.rs b/crates/ide/src/completion/engine/port_list.rs index 292e289e..42d09bfb 100644 --- a/crates/ide/src/completion/engine/port_list.rs +++ b/crates/ide/src/completion/engine/port_list.rs @@ -1,8 +1,8 @@ use hir::{ db::HirDb, hir_def::module::{ModuleId, ModuleSrc}, - scope::{ModuleEntry, UnitEntry}, semantics::Semantics, + symbol::DefKind, }; use syntax::ast; use utils::get::Get; @@ -71,21 +71,10 @@ fn visible_typedefs_in_module_header(db: &RootDb, position: FilePosition) -> Vec return Vec::new(); }; - let mut names: Vec = db - .unit_scope() - .iter() - .filter_map(|(ident, entry)| matches!(entry, UnitEntry::FiledTypedefId(_)).then_some(ident)) - .map(|ident| ident.to_string()) - .collect(); + let mut names: Vec = + db.unit_scope().typedef_names(db).map(|ident| ident.to_string()).collect(); - names.extend( - db.module_scope(module_id) - .iter() - .filter_map(|(ident, entry)| { - matches!(entry, ModuleEntry::TypedefId(_)).then_some(ident) - }) - .map(|ident| ident.to_string()), - ); + names.extend(db.module_scope(module_id).typedef_names(db).map(|ident| ident.to_string())); names.sort(); names.dedup(); @@ -116,14 +105,11 @@ fn complete_non_ansi_port_list( let scope = db.module_scope(module_id); scope - .iter() - .filter_map(|(ident, entry)| { - matches!( - entry, - hir::scope::ModuleEntry::AnsiPortEntry(_) - | hir::scope::ModuleEntry::NonAnsiPortEntry(_) - ) - .then_some(ident.to_string()) + .iter_listing() + .filter_map(|(ident, defs)| { + defs.iter() + .any(|def_id| matches!(def_id.kind(db), DefKind::Port | DefKind::NonAnsiPort)) + .then(|| ident.to_string()) }) .filter(|name| name.starts_with(prefix)) .map(|name| CompletionCandidate::text(name, ctx.replacement)) diff --git a/crates/ide/src/completion/engine/typed_filter.rs b/crates/ide/src/completion/engine/typed_filter.rs index 3fd85613..e9bb8e69 100644 --- a/crates/ide/src/completion/engine/typed_filter.rs +++ b/crates/ide/src/completion/engine/typed_filter.rs @@ -1,5 +1,5 @@ use hir::{ - container::InContainer, + container::{InContainer, ScopeId}, db::HirDb, hir_def::{ Ident, @@ -7,8 +7,8 @@ use hir::{ expr::declarator::DeclaratorParent, module::{ModuleId, port::Ports}, }, - scope::ModuleEntry, semantics::pathres::PathResolution, + symbol::{DefKind, NameContext}, type_infer::{Ty, TyClass, packed_bit_width, type_class}, }; use utils::get::GetRef; @@ -21,19 +21,15 @@ pub(super) fn expected_port_ty( port_name: &Ident, ) -> Option { let scope = db.module_scope(target_module_id); - let entry = scope.get(port_name)?; - - match entry { - ModuleEntry::AnsiPortEntry(_) | ModuleEntry::NonAnsiPortEntry(_) => Some( - db.type_of_path_resolution(PathResolution::from(hir::container::InModule::new( - target_module_id, - entry, - ))) - .ty - .clone(), - ), - _ => None, - } + let defs = scope.lookup(NameContext::Value, port_name)?; + let res = if defs.iter().any(|def_id| def_id.kind(db) == DefKind::NonAnsiPort) { + PathResolution::from_def_ids(defs)? + } else { + PathResolution::from_def_ids( + defs.into_iter().filter(|def_id| def_id.kind(db) == DefKind::Port), + )? + }; + Some(db.type_of_path_resolution(res).ty.clone()) } pub(super) fn expected_param_ty( @@ -43,21 +39,32 @@ pub(super) fn expected_param_ty( ) -> Option { let target_module = db.module(target_module_id); let scope = db.module_scope(target_module_id); - let ModuleEntry::DeclId(decl_id) = scope.get(param_name)? else { - return None; - }; + let defs = scope.lookup(NameContext::Value, param_name)?; - let DeclaratorParent::DeclarationId(declaration_id) = target_module.get(decl_id).parent else { - return None; - }; - let Declaration::ParamDecl(param_decl) = target_module.get(declaration_id) else { - return None; - }; + for def_id in defs { + if def_id.kind(db) != DefKind::Param { + continue; + } + let Some(decl_id) = def_id.as_decl(db) else { + continue; + }; + if decl_id.cont_id != ScopeId::Module(target_module_id) { + continue; + } + let DeclaratorParent::DeclarationId(declaration_id) = + target_module.get(decl_id.value).parent + else { + continue; + }; + let Declaration::ParamDecl(param_decl) = target_module.get(declaration_id) else { + continue; + }; + if param_decl.kind.is_overridable() { + return Some(db.type_of_decl(decl_id).ty.clone()); + } + } - param_decl - .kind - .is_overridable() - .then(|| db.type_of_decl(InContainer::new(target_module_id.into(), decl_id)).ty.clone()) + None } pub(super) fn value_candidates_in_module(db: &RootDb, module_id: ModuleId) -> Vec<(String, Ty)> { diff --git a/crates/ide/src/definitions.rs b/crates/ide/src/definitions.rs index 5be507ce..b1989ffe 100644 --- a/crates/ide/src/definitions.rs +++ b/crates/ide/src/definitions.rs @@ -1,7 +1,8 @@ use hir::{ - def_id::{ModuleDefId, ModuleDefOrigin}, + def_id::ModuleDefId, file::HirFileId, semantics::{Semantics, pathres::PathResolution}, + symbol::{DefId, NameContext}, }; use smallvec::SmallVec; use syntax::{ @@ -65,7 +66,9 @@ impl DefinitionClass { .and_then(|res| res.to_def_id(sema.db)); if it.open_paren().is_none() && it.close_paren().is_none() { - let local = sema.nameres_ident(file_id, tp).and_then(|res| res.to_def_id(sema.db)); + let local = sema + .nameres_ident(file_id, tp, NameContext::Value) + .and_then(|res| res.to_def_id(sema.db)); match (port, local) { (Some(port), Some(local)) => Self::PortConnShorthand { port, local }, @@ -76,13 +79,16 @@ impl DefinitionClass { port?.into() } }, - _ => sema.nameres_ident(file_id, tp)?.to_def_id(sema.db)?.into(), + _ => sema + .nameres_ident(file_id, tp, name_context_for_token(parent)) + ?.to_def_id(sema.db)? + .into(), }; Some(res) } - pub(crate) fn origins(self, db: &RootDb) -> SmallVec<[ModuleDefOrigin; 6]> { + pub(crate) fn origins(self, db: &RootDb) -> SmallVec<[DefId; 6]> { match self { DefinitionClass::Definition(definition) => definition.origins(db).into_iter().collect(), DefinitionClass::PortConnShorthand { port, local } => { @@ -104,7 +110,9 @@ fn resolve_declaration_name( && module.name() == Some(tok) { let module_id = sema.module_to_def(file_id, module)?; - return Some(PathResolution::Module(module_id).to_def_id(sema.db)?.into()); + return Some( + PathResolution::from_def_id(DefId::new(sema.db, module_id)).to_def_id(sema.db)?.into(), + ); } None @@ -146,13 +154,18 @@ fn resolve_instantiation_type_name( { return match resolve_instantiation_target(sema.db, file_id.file_id(), instantiation) { ModuleResolution::Unique(module_id) - | ModuleResolution::BestEffortProximity { selected: module_id, .. } => { - Some(PathResolution::Module(module_id).to_def_id(sema.db)?.into()) - } + | ModuleResolution::BestEffortProximity { selected: module_id, .. } => Some( + PathResolution::from_def_id(DefId::new(sema.db, module_id)) + .to_def_id(sema.db)? + .into(), + ), ModuleResolution::Ambiguous { candidates, .. } => Some(DefinitionClass::Ambiguous( candidates .into_iter() - .map(|module_id| PathResolution::Module(module_id).to_def_id(sema.db)) + .map(|module_id| { + PathResolution::from_def_id(DefId::new(sema.db, module_id)) + .to_def_id(sema.db) + }) .collect::>>()?, )), ModuleResolution::Unresolved => None, @@ -163,12 +176,25 @@ fn resolve_instantiation_type_name( SyntaxAncestors::start_from(parent).find_map(ast::PrimitiveInstantiation::cast) && instantiation.type_() == Some(tok) { - return Some(sema.nameres_ident(file_id, tp)?.to_def_id(sema.db)?.into()); + return Some( + sema.nameres_ident(file_id, tp, NameContext::Value)?.to_def_id(sema.db)?.into(), + ); } None } +fn name_context_for_token(parent: syntax::SyntaxNode<'_>) -> NameContext { + if SyntaxAncestors::start_from(parent).any(|node| ast::NamedType::cast(node).is_some()) { + NameContext::Type + } else { + // Value is the conservative default for identifier references in IDE + // features; type positions are selected by the syntactic NamedType arm + // above. + NameContext::Value + } +} + fn scoped_right_token(scoped: ast::ScopedName<'_>) -> Option> { use ast::Name::*; match scoped.right() { @@ -184,7 +210,7 @@ mod tests { use hir::{ base_db::{change::Change, source_root::SourceRoot}, - def_id::ModuleDefOrigin, + symbol::DefKind, }; use syntax::SyntaxNodeExt; use triomphe::Arc; @@ -260,11 +286,11 @@ mod tests { let origins = def.origins(db); let (resolution, range) = match origins.first().copied() { - Some(origin @ ModuleDefOrigin::NonAnsiPort(_)) => ( + Some(origin) if origin.kind(db) == DefKind::NonAnsiPort => ( "NonAnsiPort", origin.name_range(db).expect("non-ANSI port label should have a name range"), ), - Some(origin @ ModuleDefOrigin::Decl(_)) => { + Some(origin) if origin.kind(db) == DefKind::Port => { ("AnsiPort", origin.name_range(db).expect("ANSI port should have a name range")) } other => panic!("unexpected definition for {name}: {other:?}"), diff --git a/crates/ide/src/inlay_hint.rs b/crates/ide/src/inlay_hint.rs index 228437e7..79ad0d4e 100644 --- a/crates/ide/src/inlay_hint.rs +++ b/crates/ide/src/inlay_hint.rs @@ -16,8 +16,8 @@ use hir::{ }, }, preproc::{MacroCallResolution, macro_call_resolutions_in_range}, - scope::{AnsiPortEntry, ModuleEntry, ModuleScope, NonAnsiPortEntry}, source_map::{IsNamedSrc, IsSrc}, + symbol::{DefKind, NameContext, NameScope}, }; use syntax::{ast, match_ast_kind}; use utils::{ @@ -383,16 +383,26 @@ fn process_instantiation( match &target_module.ports { Ports::NonAnsi { .. } => { - let (port_id, name, dir) = - non_ansi_port_id_for_conn(target_module, target_scope, conn, idx)?; + let (port_id, name, dir) = non_ansi_port_id_for_conn( + db, + target_module, + target_scope, + conn, + idx, + )?; let target_src = InFile::new(target_file, target_src_map.get(port_id)?); collect_connection_hint( module, src_map, conn_id, name, dir, target_src, collector, ); } Ports::Ansi(_) => { - let (port_decl_id, decl_id) = - ansi_port_decl_id_for_conn(target_module, target_scope, conn, idx)?; + let (port_decl_id, decl_id) = ansi_port_decl_id_for_conn( + db, + target_module, + target_scope, + conn, + idx, + )?; let port_decl = target_module.get(port_decl_id); let name = target_module.get(decl_id).name.as_ref()?; let dir = port_decl.header.dir(); @@ -463,8 +473,9 @@ fn collect_connection_hint( } fn non_ansi_port_id_for_conn<'a>( + db: &RootDb, module: &'a Module, - scope: &ModuleScope, + scope: &NameScope, conn: &'a PortConn, idx: usize, ) -> Option<(NonAnsiPortId, &'a Ident, PortDirection)> { @@ -475,17 +486,18 @@ fn non_ansi_port_id_for_conn<'a>( }; let (port_id, port) = ports.iter().nth(idx)?; let name = port.label.as_ref()?; - let dir = non_ansi_port_dir_by_port_id(module, scope, port_id)?; + let dir = non_ansi_port_dir_by_port_id(db, module, scope, port_id)?; Some((port_id, name, dir)) } PortConn::Named(Some(name), _) => { - let ModuleEntry::NonAnsiPortEntry(NonAnsiPortEntry { label, .. }) = scope.get(name)? - else { - return None; - }; - let port_id = label?; + let defs = scope.lookup(NameContext::Value, name)?; + let port_id = defs + .iter() + .find(|def_id| def_id.kind(db) == DefKind::NonAnsiPort) + .and_then(|def_id| def_id.as_non_ansi_port(db))? + .value; let port_name = module.get(port_id).label.as_ref()?; - let dir = non_ansi_port_dir_by_port_id(module, scope, port_id)?; + let dir = non_ansi_port_dir_by_port_id(db, module, scope, port_id)?; Some((port_id, port_name, dir)) } PortConn::Named(None, _) | PortConn::Wildcard => None, @@ -493,8 +505,9 @@ fn non_ansi_port_id_for_conn<'a>( } fn non_ansi_port_dir_by_port_id( + db: &RootDb, module: &Module, - scope: &ModuleScope, + scope: &NameScope, port_id: NonAnsiPortId, ) -> Option { let port = module.get(port_id); @@ -504,20 +517,21 @@ fn non_ansi_port_dir_by_port_id( let Some(name) = module.get(ref_id).ident.as_ref() else { continue; }; - if let Some(port_decl_id) = scope.non_ansi_port_decl_id_by_name(module, name) { + if let Some(port_decl_id) = scope.non_ansi_port_decl_id_by_name(db, module, name) { return Some(module.get(port_decl_id).header.dir()); } } } let name = port.label.as_ref()?; - let port_decl_id = scope.non_ansi_port_decl_id_by_name(module, name)?; + let port_decl_id = scope.non_ansi_port_decl_id_by_name(db, module, name)?; Some(module.get(port_decl_id).header.dir()) } fn ansi_port_decl_id_for_conn( + db: &RootDb, module: &Module, - scope: &ModuleScope, + scope: &NameScope, conn: &PortConn, idx: usize, ) -> Option<(PortDeclId, DeclId)> { @@ -528,9 +542,15 @@ fn ansi_port_decl_id_for_conn( Some((port_decl_id, decl_id)) } PortConn::Named(Some(name), _) => { - let ModuleEntry::AnsiPortEntry(AnsiPortEntry(decl_id)) = scope.get(name)? else { - return None; - }; + let defs = scope.lookup(NameContext::Value, name)?; + let decl_id = defs + .iter() + .filter(|def_id| def_id.kind(db) == DefKind::Port) + .filter_map(|def_id| def_id.as_decl(db)) + .map(|decl_id| decl_id.value) + .find(|decl_id| { + matches!(module.get(*decl_id).parent, DeclaratorParent::PortDeclId(_)) + })?; let DeclaratorParent::PortDeclId(port_decl_id) = module.get(decl_id).parent else { return None; }; diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 5cba0852..86e0972a 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -2,12 +2,6 @@ #![feature(decl_macro)] pub use hir::base_db::Cancelled; -use hir::hir_def::{ - block::BlockId, - expr::declarator::DeclId, - module::{ModuleId, instantiation::InstanceId, port::NonAnsiPortId}, - stmt::StmtId, -}; pub use range::{ErasedFileAstId, FilePosition, FileRange, RangeInfo}; use syntax::{SyntaxKind, ast, match_ast_kind}; pub type Cancellable = Result; @@ -104,40 +98,32 @@ impl SymbolKind { } } -// TODO: const impl -impl From for SymbolKind { - fn from(_: ModuleId) -> Self { - SymbolKind::Module - } -} - -impl From for SymbolKind { - fn from(_: BlockId) -> Self { - SymbolKind::Block - } -} - -impl From for SymbolKind { - fn from(_: NonAnsiPortId) -> Self { - SymbolKind::NonAnsiPortLabel - } -} - -impl From for SymbolKind { - fn from(_: DeclId) -> Self { - SymbolKind::DataDecl - } -} - -impl From for SymbolKind { - fn from(_: InstanceId) -> Self { - SymbolKind::Instance - } -} - -impl From for SymbolKind { - fn from(_: StmtId) -> Self { - SymbolKind::Stmt +impl From for SymbolKind { + fn from(kind: hir::symbol::SymbolKind) -> Self { + match kind { + hir::symbol::SymbolKind::Module => SymbolKind::Module, + hir::symbol::SymbolKind::Config => SymbolKind::Config, + hir::symbol::SymbolKind::Primitive => SymbolKind::Primitive, + hir::symbol::SymbolKind::NonAnsiPortLabel => SymbolKind::NonAnsiPortLabel, + hir::symbol::SymbolKind::PortDecl => SymbolKind::PortDecl, + hir::symbol::SymbolKind::ParamDecl => SymbolKind::ParamDecl, + hir::symbol::SymbolKind::NetDecl => SymbolKind::NetDecl, + hir::symbol::SymbolKind::DataDecl => SymbolKind::DataDecl, + hir::symbol::SymbolKind::Genvar => SymbolKind::Genvar, + hir::symbol::SymbolKind::Specparam => SymbolKind::Specparam, + hir::symbol::SymbolKind::Typedef => SymbolKind::Typedef, + hir::symbol::SymbolKind::Struct => SymbolKind::Struct, + hir::symbol::SymbolKind::Instance => SymbolKind::Instance, + hir::symbol::SymbolKind::Block => SymbolKind::Block, + hir::symbol::SymbolKind::Stmt => SymbolKind::Stmt, + hir::symbol::SymbolKind::Fn => SymbolKind::Fn, + hir::symbol::SymbolKind::Generate => SymbolKind::Generate, + hir::symbol::SymbolKind::Specify => SymbolKind::Specify, + hir::symbol::SymbolKind::Interface => SymbolKind::Interface, + hir::symbol::SymbolKind::Library => SymbolKind::Library, + hir::symbol::SymbolKind::Region => SymbolKind::Region, + hir::symbol::SymbolKind::Unknown => SymbolKind::Unknown, + } } } diff --git a/crates/ide/src/module_resolution.rs b/crates/ide/src/module_resolution.rs index 0e0b700d..86dc8b27 100644 --- a/crates/ide/src/module_resolution.rs +++ b/crates/ide/src/module_resolution.rs @@ -5,7 +5,7 @@ use hir::{ source_db::{SourceDb, SourceRootDb}, source_root::SourceRootRole, }, - container::InModule, + container::ScopeId, db::HirDb, hir_def::{ Ident, @@ -14,8 +14,8 @@ use hir::{ lower_ident_opt, module::{ModuleId, instantiation::Instantiation}, }, - scope::ModuleEntry, semantics::pathres::PathResolution, + symbol::{DefKind, NameContext}, }; use syntax::{ SyntaxAncestors, @@ -125,11 +125,13 @@ fn resolve_named_port_in_module( module_id: ModuleId, port_name: &Ident, ) -> Option { - let entry = db.module_scope(module_id).get(port_name)?; - if matches!(entry, ModuleEntry::AnsiPortEntry(_) | ModuleEntry::NonAnsiPortEntry(_)) { - Some(PathResolution::from(InModule::new(module_id, entry))) + let defs = db.module_scope(module_id).lookup(NameContext::Value, port_name)?; + if defs.iter().any(|def_id| def_id.kind(db) == DefKind::NonAnsiPort) { + PathResolution::from_def_ids(defs) } else { - None + PathResolution::from_def_ids( + defs.into_iter().filter(|def_id| def_id.kind(db) == DefKind::Port), + ) } } @@ -138,18 +140,25 @@ fn resolve_named_param_in_module( module_id: ModuleId, param_name: &Ident, ) -> Option { - let ModuleEntry::DeclId(decl_id) = db.module_scope(module_id).get(param_name)? else { - return None; - }; + let defs = db.module_scope(module_id).lookup(NameContext::Value, param_name)?; let module = db.module(module_id); - if let DeclaratorParent::DeclarationId(declaration_id) = module.get(decl_id).parent - && let Declaration::ParamDecl(param_decl) = module.get(declaration_id) - && param_decl.kind.is_overridable() - { - Some(PathResolution::ParamDecl(InModule::new(module_id, decl_id))) - } else { - None + + for def_id in defs { + let Some(decl_id) = def_id.as_decl(db) else { + continue; + }; + if decl_id.cont_id != ScopeId::Module(module_id) { + continue; + } + if let DeclaratorParent::DeclarationId(declaration_id) = module.get(decl_id.value).parent + && let Declaration::ParamDecl(param_decl) = module.get(declaration_id) + && param_decl.kind.is_overridable() + { + return Some(PathResolution::from_def_id(def_id)); + } } + + None } fn resolve_module_name_with_policy( @@ -321,8 +330,8 @@ mod tests { use hir::{ base_db::{change::Change, source_db::SourceDb, source_root::SourceRoot}, - container::InModule, semantics::pathres::PathResolution, + symbol::DefLoc, }; use smol_str::SmolStr; use syntax::{SyntaxNodeExt, ast}; @@ -487,7 +496,8 @@ mod tests { .find_node_at_offset::(offset) .expect("named port connection should parse at /*caret*/"); match resolve_named_port_connection(&db, fixture.focus, port_conn) { - Some(PathResolution::AnsiPort(InModule { module_id, .. })) => { + Some(res) if resolution_module_id(&db, &res, DefKind::Port).is_some() => { + let module_id = resolution_module_id(&db, &res, DefKind::Port).unwrap(); format!( "AnsiPort module={}", file_path(&fixture.files, module_id.file_id.file_id()) @@ -504,7 +514,8 @@ mod tests { .find_node_at_offset::(offset) .expect("named parameter assignment should parse at /*caret*/"); match resolve_named_param_assignment(&db, fixture.focus, param_assign) { - Some(PathResolution::ParamDecl(InModule { module_id, .. })) => { + Some(res) if resolution_module_id(&db, &res, DefKind::Param).is_some() => { + let module_id = resolution_module_id(&db, &res, DefKind::Param).unwrap(); format!( "ParamDecl module={}", file_path(&fixture.files, module_id.file_id.file_id()) @@ -516,6 +527,22 @@ mod tests { } } + fn resolution_module_id(db: &RootDb, res: &PathResolution, kind: DefKind) -> Option { + res.def_ids().iter().find_map(|def_id| { + if def_id.kind(db) != kind { + return None; + } + match def_id.loc(db) { + DefLoc::Decl(decl_id) => match decl_id.cont_id { + ScopeId::Module(module_id) => Some(module_id), + _ => None, + }, + DefLoc::NonAnsiPort(nonansi_port_id) => Some(nonansi_port_id.module_id), + _ => None, + } + }) + } + fn format_module_resolution(files: &[(String, String)], result: ModuleResolution) -> String { match result { ModuleResolution::Unique(module_id) => { diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 7b699343..de9db63b 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -1,8 +1,7 @@ use hir::{ base_db::intern::Lookup, - container::{ContainerId, InContainer, InFile, InModule, InSubroutine}, + container::{InContainer, InFile, InModule, InSubroutine, ScopeId}, db::HirDb, - def_id::ModuleDefOrigin, hir_def::{ block::{BlockId, BlockLoc}, declaration::Declaration, @@ -15,17 +14,14 @@ use hir::{ port::NonAnsiPortId, }, stmt::StmtId, - subroutine::{SubroutineId, SubroutinePortId}, + subroutine::{LocalSubroutineId, SubroutinePortId}, typedef::TypedefId, }, - source_map::{IsNamedSrc, IsSrc, ToAstNode}, + source_map::{IsNamedSrc, IsSrc}, + symbol::DefId, }; use smol_str::SmolStr; -use syntax::{ - SyntaxTokenWithParent, - ast::AstNode, - has_text_range::{HasTextRange, HasTextRangeIn}, -}; +use syntax::{SyntaxTokenWithParent, has_text_range::HasTextRange}; use utils::{ get::{Get, GetRef}, line_index::TextRange, @@ -57,23 +53,18 @@ pub(crate) trait ToNav { fn to_nav(&self, db: &RootDb) -> Option; } -impl ToNav for ModuleDefOrigin { +impl ToNav for DefId { fn to_nav(&self, db: &RootDb) -> Option { - match self { - ModuleDefOrigin::ModuleId(module_id) => module_id.to_nav(db), - ModuleDefOrigin::Config(config_id) => config_id.to_nav(db), - ModuleDefOrigin::Library(library_id) => library_id.to_nav(db), - ModuleDefOrigin::Udp(udp_id) => udp_id.to_nav(db), - ModuleDefOrigin::BlockId(block_id) => block_id.to_nav(db), - ModuleDefOrigin::GenerateBlockId(generate_block_id) => generate_block_id.to_nav(db), - ModuleDefOrigin::SubroutineId(subroutine_id) => subroutine_id.to_nav(db), - ModuleDefOrigin::SubroutinePort(subroutine_port_id) => subroutine_port_id.to_nav(db), - ModuleDefOrigin::NonAnsiPort(nonansi_port_id) => nonansi_port_id.to_nav(db), - ModuleDefOrigin::Decl(decl_id) => decl_id.to_nav(db), - ModuleDefOrigin::Typedef(typedef_id) => typedef_id.to_nav(db), - ModuleDefOrigin::Instance(instance_id) => instance_id.to_nav(db), - ModuleDefOrigin::Stmt(stmt_id) => stmt_id.to_nav(db), - } + let InFile { file_id, value: full_range } = self.range(db)?; + let focus_range = self.name_range(db).map(|range| range.value); + let name = self.name(db); + let kind = self.kind(db).symbol_kind().into(); + let container_name = match self.container_id(db) { + ScopeId::File(_) => None, + cont_id => cont_id.to_container(db).name().cloned(), + }; + + Some(build(file_id.file_id(), focus_range, full_range, name, kind, container_name)) } } @@ -167,51 +158,15 @@ impl ToNav for GenerateBlockId { } } -impl ToNav for SubroutineId { +impl ToNav for InContainer { fn to_nav(&self, db: &RootDb) -> Option { - let loc = self.lookup(db); - let cont_id: ContainerId = loc.cont_id.into(); - let cont_name = cont_id.to_container(db).name().cloned(); - let name = db.subroutine(*self).name.clone(); - let focus_range = loc.src.value.name_range(); - - let file_id = loc.src.file_id.file_id(); - Some(build(file_id, focus_range, loc.src.value.range(), name, SymbolKind::Fn, cont_name)) + DefId::new(db, *self).to_nav(db) } } impl ToNav for InSubroutine { fn to_nav(&self, db: &RootDb) -> Option { - let InSubroutine { subroutine, value } = *self; - let loc = subroutine.lookup(db); - let cont_name = db.subroutine(subroutine).name.clone(); - let subroutine_src = loc.src; - let tree = db.parse(subroutine_src.file_id); - let func = subroutine_src.value.to_node(&tree)?; - let ports = func.prototype().port_list()?; - let port = ports - .ports() - .children() - .nth(value.0 as usize) - .and_then(|port| port.as_function_port())?; - let name = db - .subroutine(subroutine) - .ports - .get(value.0 as usize) - .and_then(|port| port.name.clone()); - let declarator = port.declarator(); - let focus_range = - declarator.name().and_then(|name| name.text_range_in(declarator.syntax())); - let full_range = port.syntax().text_range()?; - - Some(build( - subroutine_src.file_id.file_id(), - focus_range, - full_range, - name, - SymbolKind::PortDecl, - cont_name, - )) + DefId::new(db, *self).to_nav(db) } } diff --git a/crates/ide/src/references/search.rs b/crates/ide/src/references/search.rs index 5509e50f..e5e90790 100644 --- a/crates/ide/src/references/search.rs +++ b/crates/ide/src/references/search.rs @@ -5,10 +5,11 @@ use hir::{ source_db::{SourceDb, SourceRootDb}, source_root::SourceRootId, }, - container::{ContainerId, InFile}, + container::{InFile, ScopeId}, def_id::ModuleDefId, semantics::Semantics, source_map::IsSrc, + symbol::DefId, }; use nohash_hasher::IntMap; use rustc_hash::FxHashMap; @@ -46,9 +47,7 @@ impl SearchScope { return search_scope.unwrap_or_default(); }; let container_id = match container_id { - ContainerId::ModuleId(InFile { file_id, .. }) if def.is_port(db) => { - file_id.into() - } + ScopeId::Module(InFile { file_id, .. }) if def.is_port(db) => file_id.into(), cont => cont, }; @@ -73,10 +72,10 @@ impl SearchScope { SearchScope(res) } - fn from_conts(db: &RootDb, cont: ContainerId) -> Self { + fn from_conts(db: &RootDb, cont: ScopeId) -> Self { match cont { - ContainerId::HirFileId(_) => Self::all(db), - ContainerId::ModuleId(InFile { value: local_module_id, file_id }) => { + ScopeId::File(_) => Self::all(db), + ScopeId::Module(InFile { value: local_module_id, file_id }) => { if let Some(range) = file_id.to_container_src_map(db).get(local_module_id).map(|src| src.range()) { @@ -85,17 +84,21 @@ impl SearchScope { Self::all(db) } } - ContainerId::BlockId(block_id) => { + ScopeId::Block(block_id) => { let range = block_id.lookup(db).src.value.range(); Self::single_range(block_id.file_id(db), range) } - ContainerId::GenerateBlockId(generate_block_id) => { + ScopeId::GenerateBlock(generate_block_id) => { let src = generate_block_id.lookup(db).src; Self::single_range(src.file_id.file_id(), src.value.range()) } - ContainerId::SubroutineId(subroutine_id) => { - let src = subroutine_id.lookup(db).src; - Self::single_range(src.file_id.file_id(), src.value.range()) + ScopeId::Subroutine(subroutine_id) => { + let def_id = DefId::new(db, subroutine_id.as_in_container()); + if let Some(InFile { file_id, value: range }) = def_id.range(db) { + Self::single_range(file_id.file_id(), range) + } else { + Self::all(db) + } } } } diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 6f0af8b0..2210ee0b 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -1,8 +1,9 @@ use hir::{ base_db::source_db::SourceDb, container::InFile, - def_id::{ModuleDefId, ModuleDefOrigin}, + def_id::ModuleDefId, semantics::Semantics, + symbol::{DefId, NameContext}, }; use nohash_hasher::IntMap; use rustc_hash::FxHashMap; @@ -143,7 +144,7 @@ pub(crate) fn expanded_rename( let sema = Semantics::new(db); let resolved = resolve_rename_target(&sema, position)?; let targets = recursive_rename_targets(db, &sema, position.file_id, &config, resolved.targets)?; - let mut rename_targets = UniqVec::<(), ModuleDefOrigin>::default(); + let mut rename_targets = UniqVec::<(), DefId>::default(); for target in &targets { rename_targets.push(target.def.origins(db), ()); } @@ -188,13 +189,14 @@ pub(crate) fn rename_conflict_info( }; let new_name = SmolStr::new(new_name); - let mut target_index = UniqVec::<(), ModuleDefOrigin>::default(); + let mut target_index = UniqVec::<(), DefId>::default(); for target in &targets { target_index.push(target.origins(db), ()); } - let mut conflicts = UniqVec::::default(); + let mut conflicts = UniqVec::::default(); for collision in targets.iter().flat_map(|target| target.origins(db)).filter_map(|origin| { - sema.resolve_name(origin.container_id(db), &new_name).and_then(|res| res.to_def_id(db)) + sema.resolve_name(origin.container_id(db), &new_name, origin.kind(db).name_context()) + .and_then(|res| res.to_def_id(db)) }) { if collision.origins(db).iter().any(|origin| target_index.contains(origin)) { continue; @@ -239,7 +241,7 @@ fn resolve_rename_target( }; let (range, tokens) = target.into_parts(); let mut selected_def = None; - let mut targets = UniqVec::::default(); + let mut targets = UniqVec::::default(); for token in tokens { let token_selected = match DefinitionClass::resolve(sema, hir_file_id, token) @@ -291,7 +293,7 @@ fn rename_definition( config: &RenameConfig, def: &ModuleDefId, new_name: &str, - rename_targets: Option<&UniqVec<(), ModuleDefOrigin>>, + rename_targets: Option<&UniqVec<(), DefId>>, ) -> RenameResult { let refs = references_for_definition(db, sema, request_file_id, config, def)?; rename_definition_with_refs(db, sema, def, new_name, rename_targets, &refs, &[]) @@ -313,7 +315,7 @@ fn rename_definition_with_refs( sema: &Semantics<'_, RootDb>, def: &ModuleDefId, new_name: &str, - rename_targets: Option<&UniqVec<(), ModuleDefOrigin>>, + rename_targets: Option<&UniqVec<(), DefId>>, refs: &ReferenceSearchResult, same_name_refs: &[SameNameConnectionRef], ) -> RenameResult { @@ -365,7 +367,7 @@ fn recursive_rename_targets( config: &RenameConfig, initial_targets: Vec, ) -> RenameResult> { - let mut targets = UniqVec::::default(); + let mut targets = UniqVec::::default(); for target in initial_targets { targets.push(target.origins(db), target); } @@ -468,7 +470,7 @@ fn check_same_name_conn( let collapse_end = close_paren.text_range_in(conn.syntax())?.end(); Some(SameNameConnection { port, - local: sema.nameres_ident(file_id, actual_token)?.to_def_id(sema.db)?, + local: sema.nameres_ident(file_id, actual_token, NameContext::Value)?.to_def_id(sema.db)?, collapse_range: TextRange::new(name_range.start(), collapse_end), }) } @@ -490,7 +492,7 @@ fn edits_from_refs( def: &ModuleDefId, old_name: &str, new_name: &str, - rename_targets: Option<&UniqVec<(), ModuleDefOrigin>>, + rename_targets: Option<&UniqVec<(), DefId>>, same_name_refs: &[SameNameConnectionRef], ) -> (FileId, TextEdit) { let mut text_edit = TextEdit::builder(); diff --git a/crates/ide/src/render.rs b/crates/ide/src/render.rs index 854c7b5d..cd574bd0 100644 --- a/crates/ide/src/render.rs +++ b/crates/ide/src/render.rs @@ -1,11 +1,8 @@ use hir::{ - base_db::{ - intern::Lookup, - source_db::{SourceDb, SourceRootDb}, - }, - container::{ContainerId, ContainerParent, InContainer, InFile, InModule, InSubroutine}, + base_db::source_db::{SourceDb, SourceRootDb}, + container::{InContainer, InFile, InModule, InSubroutine, ScopeId, ScopeParent}, db::HirDb, - def_id::{ModuleDefId, ModuleDefOrigin}, + def_id::ModuleDefId, display::HirDisplay, hir_def::{ DEFAULT_NAME, @@ -20,10 +17,11 @@ use hir::{ instantiation::InstanceId, port::{NonAnsiPortId, Ports}, }, - subroutine::{SubroutineId, SubroutineKind, SubroutinePortId}, + subroutine::{LocalSubroutineId, SubroutineKind, SubroutinePortId}, }, region_tree::RegionParent, semantics::Semantics, + symbol::{DefId, DefLoc}, }; use itertools::Itertools; use syntax::{ @@ -180,7 +178,7 @@ pub(crate) fn render_definition_location( fn render_def_origin_location( db: &RootDb, - origin: &ModuleDefOrigin, + origin: &DefId, anchor_file_id: vfs::FileId, ) -> Option { let InFile { value: range, file_id } = origin.range(db)?; @@ -258,7 +256,7 @@ fn has_normal_path_component(path: &utils::paths::AbsPath) -> bool { fn render_def_origin( sema: &Semantics, - origin: &ModuleDefOrigin, + origin: &DefId, anchor_file_id: vfs::FileId, ) -> Markup { let mut res = Markup::new(); @@ -283,24 +281,24 @@ fn render_def_origin( res } -fn render_definition_title(db: &RootDb, origin: &ModuleDefOrigin) -> Option { +fn render_definition_title(db: &RootDb, origin: &DefId) -> Option { let name = origin.name(db)?; - let kind = match origin { - ModuleDefOrigin::ModuleId(_) => "Module", - ModuleDefOrigin::Config(_) => "Config", - ModuleDefOrigin::Library(_) => "Library", - ModuleDefOrigin::Udp(_) => "Primitive", - ModuleDefOrigin::BlockId(_) => "Block", - ModuleDefOrigin::GenerateBlockId(_) => "Generate block", - ModuleDefOrigin::SubroutineId(subroutine_id) => match db.subroutine(*subroutine_id).kind { + let kind = match origin.loc(db) { + DefLoc::Module(_) => "Module", + DefLoc::Config(_) => "Config", + DefLoc::Library(_) => "Library", + DefLoc::Udp(_) => "Primitive", + DefLoc::Block(_) => "Block", + DefLoc::GenerateBlock(_) => "Generate block", + DefLoc::Subroutine(subroutine_id) => match db.subroutine(subroutine_id).kind { SubroutineKind::Task => "Task", SubroutineKind::Function { .. } => "Function", }, - ModuleDefOrigin::SubroutinePort(_) | ModuleDefOrigin::NonAnsiPort(_) => "Port", - ModuleDefOrigin::Decl(decl_id) => render_decl_title_kind(db, *decl_id)?, - ModuleDefOrigin::Typedef(_) => "Typedef", - ModuleDefOrigin::Instance(_) => "Instance", - ModuleDefOrigin::Stmt(_) => "Statement", + DefLoc::SubroutinePort(_) | DefLoc::NonAnsiPort(_) => "Port", + DefLoc::Decl(decl_id) => render_decl_title_kind(db, decl_id)?, + DefLoc::Typedef(_) => "Typedef", + DefLoc::Instance(_) => "Instance", + DefLoc::Stmt(_) => "Statement", }; Some(format!("{kind} {}", inline_code(name.as_str()))) @@ -326,18 +324,16 @@ fn render_decl_title_kind(db: &RootDb, decl_id: InContainer) -> Option<& }) } -fn render_signature(sema: &Semantics, origin: &ModuleDefOrigin) -> Option { +fn render_signature(sema: &Semantics, origin: &DefId) -> Option { let db = sema.db; - match origin { - ModuleDefOrigin::ModuleId(module_id) => render_module_signature(db, *module_id), - ModuleDefOrigin::SubroutineId(subroutine_id) => { - render_subroutine_signature(db, *subroutine_id) - } - ModuleDefOrigin::SubroutinePort(port_id) => render_subroutine_port_signature(db, *port_id), - ModuleDefOrigin::NonAnsiPort(port_id) => render_non_ansi_port_signature(db, *port_id), - ModuleDefOrigin::Decl(decl_id) => render_decl_signature(db, *decl_id), - ModuleDefOrigin::Typedef(typedef) => typedef.display_signature(db).ok(), - ModuleDefOrigin::Instance(instance_id) => render_instance_signature(db, *instance_id), + match origin.loc(db) { + DefLoc::Module(module_id) => render_module_signature(db, module_id), + DefLoc::Subroutine(subroutine_id) => render_subroutine_signature(db, subroutine_id), + DefLoc::SubroutinePort(port_id) => render_subroutine_port_signature(db, port_id), + DefLoc::NonAnsiPort(port_id) => render_non_ansi_port_signature(db, port_id), + DefLoc::Decl(decl_id) => render_decl_signature(db, decl_id), + DefLoc::Typedef(typedef) => typedef.display_signature(db).ok(), + DefLoc::Instance(instance_id) => render_instance_signature(db, instance_id), _ => render_label_signature(db, origin), } } @@ -400,7 +396,7 @@ fn render_subroutine_port_signature( let subroutine = db.subroutine(port_id.subroutine); let port = subroutine.ports.get(port_id.value.0 as usize)?; let name = port.name.as_ref()?; - let container = port_id.subroutine.lookup(db).cont_id.into(); + let container = port_id.subroutine.cont_id; let ty = port.ty.and_then(|ty| render_data_ty(db, container, ty)); let dir = port.direction.display_source(db).ok()?; @@ -412,10 +408,13 @@ fn render_subroutine_port_signature( } } -fn render_subroutine_signature(db: &RootDb, subroutine_id: SubroutineId) -> Option { +fn render_subroutine_signature( + db: &RootDb, + subroutine_id: InContainer, +) -> Option { let subroutine = db.subroutine(subroutine_id); let name = subroutine.name.as_ref()?; - let container = subroutine_id.lookup(db).cont_id.into(); + let container = subroutine_id.cont_id; let mut signature = match subroutine.kind { SubroutineKind::Task => format!("task {name}"), SubroutineKind::Function { return_ty } => { @@ -526,7 +525,7 @@ fn render_decl_signature(db: &RootDb, decl_id: InContainer) -> Option { - let ContainerId::ModuleId(module_id) = decl_id.cont_id else { + let ScopeId::Module(module_id) = decl_id.cont_id else { return None; }; let module = db.module(module_id); @@ -556,7 +555,7 @@ fn render_decl_signature(db: &RootDb, decl_id: InContainer) -> Option Option { let ty = render_data_ty(db, cont_id, declaration.ty()).unwrap_or_default(); @@ -616,31 +615,31 @@ fn render_initializer(db: &RootDb, decl_id: InContainer) -> Option Option { +fn render_data_ty(db: &RootDb, container: ScopeId, ty: DataTy) -> Option { InContainer::new(container, ty).display_source(db).ok() } -fn render_label_signature(db: &RootDb, origin: &ModuleDefOrigin) -> Option { +fn render_label_signature(db: &RootDb, origin: &DefId) -> Option { let name = origin.name(db)?; - let kind = match origin { - ModuleDefOrigin::Config(_) => "config", - ModuleDefOrigin::Library(_) => "library", - ModuleDefOrigin::Udp(_) => "primitive", - ModuleDefOrigin::BlockId(_) => "block", - ModuleDefOrigin::GenerateBlockId(_) => "generate", - ModuleDefOrigin::Instance(_) => "instance", - ModuleDefOrigin::Stmt(_) => "statement", - ModuleDefOrigin::Typedef(_) => "typedef", - ModuleDefOrigin::ModuleId(_) - | ModuleDefOrigin::SubroutineId(_) - | ModuleDefOrigin::SubroutinePort(_) - | ModuleDefOrigin::NonAnsiPort(_) - | ModuleDefOrigin::Decl(_) => return None, + let kind = match origin.loc(db) { + DefLoc::Config(_) => "config", + DefLoc::Library(_) => "library", + DefLoc::Udp(_) => "primitive", + DefLoc::Block(_) => "block", + DefLoc::GenerateBlock(_) => "generate", + DefLoc::Instance(_) => "instance", + DefLoc::Stmt(_) => "statement", + DefLoc::Typedef(_) => "typedef", + DefLoc::Module(_) + | DefLoc::Subroutine(_) + | DefLoc::SubroutinePort(_) + | DefLoc::NonAnsiPort(_) + | DefLoc::Decl(_) => return None, }; Some(format!("{kind} {name}")) } -fn render_side_comments(sema: &Semantics<'_, RootDb>, origin: &ModuleDefOrigin) -> Option { +fn render_side_comments(sema: &Semantics<'_, RootDb>, origin: &DefId) -> Option { let db = sema.db; let InFile { value: range, file_id } = origin.range(db)?; @@ -678,7 +677,7 @@ fn render_side_comments(sema: &Semantics<'_, RootDb>, origin: &ModuleDefOrigin) } } -fn render_scope_fact(sema: &Semantics, origin: &ModuleDefOrigin) -> Option { +fn render_scope_fact(sema: &Semantics, origin: &DefId) -> Option { // elaboration? let db = sema.db; let InFile { value: range, .. } = origin.range(db)?; @@ -686,7 +685,7 @@ fn render_scope_fact(sema: &Semantics, origin: &ModuleDefOrigin) -> Opti let mut containers = Vec::new(); - for cont_id in ContainerParent::start_from(db, cont_id) { + for cont_id in ScopeParent::start_from(db, cont_id) { let src_map = cont_id.to_container_src_map(db); if let Some(region_tree) = src_map.region_tree() @@ -697,7 +696,7 @@ fn render_scope_fact(sema: &Semantics, origin: &ModuleDefOrigin) -> Opti } } - if !matches!(cont_id, ContainerId::HirFileId(_)) { + if !matches!(cont_id, ScopeId::File(_)) { if let Some(name) = cont_id.to_container(db).name() { containers.push(name.to_string()); } else { @@ -714,7 +713,7 @@ fn render_scope_fact(sema: &Semantics, origin: &ModuleDefOrigin) -> Opti fn render_definition_metadata( sema: &Semantics, - origin: &ModuleDefOrigin, + origin: &DefId, anchor_file_id: vfs::FileId, ) -> Option { let mut parts = Vec::new(); diff --git a/crates/ide/src/semantic_index.rs b/crates/ide/src/semantic_index.rs index 3e8ba205..c7322f54 100644 --- a/crates/ide/src/semantic_index.rs +++ b/crates/ide/src/semantic_index.rs @@ -5,12 +5,12 @@ use hir::{ }, container::InFile, db::HirDb, - def_id::{ModuleDefId, ModuleDefOrigin}, + def_id::ModuleDefId, file::HirFileId, hir_def::{Ident, module::ModuleId}, - scope::UnitEntry, semantics::Semantics, source_map::IsSrc, + symbol::{DefId, DefKind}, }; use itertools::Itertools; use rustc_hash::FxHashMap; @@ -117,14 +117,17 @@ impl ModuleIndex { for file_id in source_root.iter() { let hir_file_id = HirFileId::from(file_id); - for (_, entry) in db.file_scope(hir_file_id).iter() { - let UnitEntry::ModuleId(module_id) = entry else { - continue; - }; - let Some(module) = SemanticModuleDefinition::new(db, module_id) else { - continue; - }; - modules_by_name.entry(module.name.clone()).or_default().push(module); + for (_, defs) in db.file_scope(hir_file_id).iter_listing() { + for module_id in defs + .iter() + .filter(|def_id| def_id.kind(db) == DefKind::Module) + .filter_map(|def_id| def_id.as_module(db)) + { + let Some(module) = SemanticModuleDefinition::new(db, module_id) else { + continue; + }; + modules_by_name.entry(module.name.clone()).or_default().push(module); + } } } @@ -163,7 +166,7 @@ impl ModuleIndex { impl SemanticModuleDefinition { fn new(db: &dyn HirDb, module_id: ModuleId) -> Option { - let origin = ModuleDefOrigin::ModuleId(module_id); + let origin = DefId::new(db, module_id); let name = origin.name(db)?; let InFile { file_id, value: name_range } = origin.name_range(db)?; let InFile { value: full_range, .. } = origin.range(db)?; diff --git a/crates/ide/src/semantic_tokens.rs b/crates/ide/src/semantic_tokens.rs index d4a26cbe..8374d5ea 100644 --- a/crates/ide/src/semantic_tokens.rs +++ b/crates/ide/src/semantic_tokens.rs @@ -1,13 +1,17 @@ use bitflags::bitflags; use collector::SemaTokenCollectorTree; use hir::{ - container::{InContainer, InModule}, + container::{InContainer, ScopeId}, db::HirDb, file::HirFileId, hir_def::{ Ident, block::{BlockId, BlockInfo}, - expr::{Expr, declarator::DeclaratorParent}, + expr::{ + Expr, ExprId, + data_ty::{DataTy, NamedDataTy}, + declarator::DeclaratorParent, + }, module::{ ModuleId, instantiation::{ParamAssign, PortConn}, @@ -15,10 +19,11 @@ use hir::{ stmt::StmtKind, }, preproc::macro_references_in_range, - scope::NonAnsiPortEntry, semantics::{Semantics, pathres::PathResolution}, source_map::{IsNamedSrc, IsSrc, ToAstNode}, + symbol::{DefKind, DefLoc, NameContext}, }; +use rustc_hash::FxHashSet; use smol_str::SmolStr; use syntax::{ast, has_text_range::HasTextRange}; use utils::{ @@ -197,7 +202,35 @@ fn collect_file( collect_ident_like(sema, name_in_cont, range, collector); }; + let mut type_expr_ids = FxHashSet::default(); + for (_, declaration) in hir_file.declarations.iter() { + if let Some(expr_id) = named_data_ty_expr_id(declaration.ty()) { + type_expr_ids.insert(expr_id); + let Some(range) = file_src_map.get(expr_id).map(|src| src.range()) else { + continue; + }; + check_range!(collector, range); + collect_type_ident_like(sema, file_id.into(), hir_file.get(expr_id), range, collector); + } + } + for (_, typedef) in hir_file.typedefs.iter() { + let Some(ty) = typedef.ty else { + continue; + }; + if let Some(expr_id) = named_data_ty_expr_id(ty) { + type_expr_ids.insert(expr_id); + let Some(range) = file_src_map.get(expr_id).map(|src| src.range()) else { + continue; + }; + check_range!(collector, range); + collect_type_ident_like(sema, file_id.into(), hir_file.get(expr_id), range, collector); + } + } + for (expr_id, expr) in hir_file.exprs.iter() { + if type_expr_ids.contains(&expr_id) { + continue; + } match expr { Expr::Field { .. } => {} Expr::Ident(name) => { @@ -260,6 +293,31 @@ fn collect_module( collect_ident_like(sema, name_in_cont, range, collector); }; + let mut type_expr_ids = FxHashSet::default(); + for (_, declaration) in module.declarations.iter() { + if let Some(expr_id) = named_data_ty_expr_id(declaration.ty()) { + type_expr_ids.insert(expr_id); + let Some(range) = module_src_map.get(expr_id).map(|src| src.range()) else { + continue; + }; + check_range!(collector, range); + collect_type_ident_like(sema, module_id.into(), module.get(expr_id), range, collector); + } + } + for (_, typedef) in module.typedefs.iter() { + let Some(ty) = typedef.ty else { + continue; + }; + if let Some(expr_id) = named_data_ty_expr_id(ty) { + type_expr_ids.insert(expr_id); + let Some(range) = module_src_map.get(expr_id).map(|src| src.range()) else { + continue; + }; + check_range!(collector, range); + collect_type_ident_like(sema, module_id.into(), module.get(expr_id), range, collector); + } + } + for (instance_id, _) in module.instances.iter() { if let Some(range) = module_src_map.get(instance_id).and_then(|src| src.name_range()) { check_range!(collector, range); @@ -273,6 +331,9 @@ fn collect_module( collect_named_port_connections(sema, module_id, collector); for (expr_id, expr) in module.exprs.iter() { + if type_expr_ids.contains(&expr_id) { + continue; + } match expr { Expr::Field { .. } => {} Expr::Ident(name) => { @@ -334,7 +395,35 @@ fn collect_block( collect_ident_like(sema, name_in_cont, range, collector); }; + let mut type_expr_ids = FxHashSet::default(); + for (_, declaration) in block.declarations.iter() { + if let Some(expr_id) = named_data_ty_expr_id(declaration.ty()) { + type_expr_ids.insert(expr_id); + let Some(range) = block_src_map.get(expr_id).map(|src| src.range()) else { + continue; + }; + check_range!(collector, range); + collect_type_ident_like(sema, block_id.into(), block.get(expr_id), range, collector); + } + } + for (_, typedef) in block.typedefs.iter() { + let Some(ty) = typedef.ty else { + continue; + }; + if let Some(expr_id) = named_data_ty_expr_id(ty) { + type_expr_ids.insert(expr_id); + let Some(range) = block_src_map.get(expr_id).map(|src| src.range()) else { + continue; + }; + check_range!(collector, range); + collect_type_ident_like(sema, block_id.into(), block.get(expr_id), range, collector); + } + } + for (expr_id, expr) in block.exprs.iter() { + if type_expr_ids.contains(&expr_id) { + continue; + } match expr { Expr::Field { .. } => {} Expr::Ident(name) => { @@ -453,10 +542,31 @@ fn collect_ident_like( range: TextRange, collector: &mut SemaTokenCollector, ) -> Option<()> { - let res = sema.name_to_def(in_cont)?; + let res = sema.resolve_name(in_cont.cont_id, &in_cont.value, NameContext::Value)?; + collect_resolved_path(sema, res, range, collector) +} + +fn collect_type_ident_like( + sema: &Semantics<'_, RootDb>, + cont_id: ScopeId, + expr: &Expr, + range: TextRange, + collector: &mut SemaTokenCollector, +) -> Option<()> { + let Expr::Ident(name) = expr else { + return None; + }; + let res = sema.resolve_name(cont_id, name, NameContext::Type)?; collect_resolved_path(sema, res, range, collector) } +fn named_data_ty_expr_id(ty: DataTy) -> Option { + match ty { + DataTy::Named(NamedDataTy::Ident(expr_id) | NamedDataTy::Field(expr_id)) => Some(expr_id), + DataTy::Builtin(_) | DataTy::Struct(_) => None, + } +} + fn collect_resolved_path( sema: &Semantics<'_, RootDb>, res: PathResolution, @@ -465,40 +575,58 @@ fn collect_resolved_path( ) -> Option<()> { let db = sema.db; - match res { - PathResolution::NonAnsiPort { label, port_decl, data_decl, module } => { - let module = db.module(module); - let name = module.get(port_decl?).name.as_ref()?; - let entry = NonAnsiPortEntry { label, port_decl, data_decl }; - let (dir, ty) = port::resolve_non_ansi_port(module.as_ref(), &entry.into())?; - port::add_port_token(db, name, dir, ty, range, collector); - } - PathResolution::AnsiPort(InModule { value: decl_id, module_id }) => { - let module = db.module(module_id); - let name = module.get(decl_id).name.as_ref()?; + if res.def_ids().iter().any(|def_id| def_id.kind(db) == DefKind::NonAnsiPort) { + let module_id = res.def_ids().iter().find_map(|def_id| match def_id.loc(db) { + DefLoc::NonAnsiPort(port_id) => Some(port_id.module_id), + DefLoc::Decl(decl_id) => match decl_id.cont_id { + ScopeId::Module(module_id) => Some(module_id), + _ => None, + }, + _ => None, + })?; + let module = db.module(module_id); + let (name, dir, ty) = port::resolve_non_ansi_port(db, module.as_ref(), res.def_ids())?; + port::add_port_token(db, name, dir, ty, range, collector); + return Some(()); + } - let DeclaratorParent::PortDeclId(port_declaration_id) = module.get(decl_id).parent - else { - return None; - }; - let port_decl = module.get(port_declaration_id); - let header = &port_decl.header; - let (dir, ty) = (Some(header.dir()), header.ty()); - port::add_port_token(db, name, dir, ty, range, collector); - } - PathResolution::Instance(_) => { - let sema_token = - SemaToken { range, tag: SemaTokenTag::Instance, mods: SemaTokenModifier::empty() }; - collector.tokens.add(sema_token); - } - PathResolution::Typedef(_) => { - collector.tokens.add(SemaToken { - range, - tag: SemaTokenTag::Type, - mods: SemaTokenModifier::REF, - }); + for def_id in res.def_ids() { + match def_id.kind(db) { + DefKind::Port => { + let decl_id = def_id.as_decl(db)?; + let ScopeId::Module(module_id) = decl_id.cont_id else { + return None; + }; + let module = db.module(module_id); + let name = module.get(decl_id.value).name.as_ref()?; + + let DeclaratorParent::PortDeclId(port_declaration_id) = + module.get(decl_id.value).parent + else { + return None; + }; + let port_decl = module.get(port_declaration_id); + let header = &port_decl.header; + let (dir, ty) = (Some(header.dir()), header.ty()); + port::add_port_token(db, name, dir, ty, range, collector); + } + DefKind::Instance => { + let sema_token = SemaToken { + range, + tag: SemaTokenTag::Instance, + mods: SemaTokenModifier::empty(), + }; + collector.tokens.add(sema_token); + } + DefKind::Typedef | DefKind::Struct | DefKind::Enum => { + collector.tokens.add(SemaToken { + range, + tag: SemaTokenTag::Type, + mods: SemaTokenModifier::REF, + }); + } + _ => {} } - _ => {} } Some(()) diff --git a/crates/ide/src/semantic_tokens/port.rs b/crates/ide/src/semantic_tokens/port.rs index 5ffa32ac..9ae87d11 100644 --- a/crates/ide/src/semantic_tokens/port.rs +++ b/crates/ide/src/semantic_tokens/port.rs @@ -13,9 +13,9 @@ use hir::{ port::{NonAnsiPort, PortDirection, Ports}, }, }, - scope::{ModuleEntry, NonAnsiPortEntry}, semantics::Semantics, source_map::{IsNamedSrc, IsSrc}, + symbol::{DefId, NameContext}, }; use regex::{Regex, RegexBuilder}; use smallvec::SmallVec; @@ -61,8 +61,8 @@ pub(super) fn collect_port( check_range!(collector, name_range); let name = module.get(ref_id).ident.as_ref()?; - let entry = module_scope.get(name)?; - let (dir, ty) = resolve_non_ansi_port(module, &entry)?; + let defs = module_scope.lookup(NameContext::Value, name)?; + let (_, dir, ty) = resolve_non_ansi_port(db, module, &defs)?; add_port_token(db, name, dir, ty, name_range, collector); }; } @@ -80,8 +80,8 @@ pub(super) fn collect_port( check_range!(collector, name_range); let name = decl.name.as_ref()?; - let entry = module_scope.get(name)?; - let (dir, ty) = resolve_non_ansi_port(module, &entry)?; + let defs = module_scope.lookup(NameContext::Value, name)?; + let (_, dir, ty) = resolve_non_ansi_port(db, module, &defs)?; add_port_token(db, name, dir, ty, name_range, collector); }; } @@ -112,19 +112,22 @@ pub(super) fn collect_port( } } -pub(super) fn resolve_non_ansi_port( - module: &Module, - entry: &ModuleEntry, -) -> Option<(Option, DataTy)> { - let ModuleEntry::NonAnsiPortEntry(NonAnsiPortEntry { - port_decl: Some(port_decl_id), - data_decl: data_decl_id, - .. - }) = *entry - else { - return None; - }; +pub(super) fn resolve_non_ansi_port<'a>( + db: &RootDb, + module: &'a Module, + defs: &[DefId], +) -> Option<(&'a hir::hir_def::Ident, Option, DataTy)> { + let port_decl_id = + defs.iter().filter_map(|def_id| def_id.as_decl(db)).map(|decl_id| decl_id.value).find( + |decl_id| matches!(module.get(*decl_id).parent, DeclaratorParent::PortDeclId(_)), + )?; + let data_decl_id = + defs.iter().filter_map(|def_id| def_id.as_decl(db)).map(|decl_id| decl_id.value).find( + |decl_id| matches!(module.get(*decl_id).parent, DeclaratorParent::DeclarationId(_)), + ); + let port_decl = module.get(port_decl_id); + let name = port_decl.name.as_ref()?; let port_declaration = match port_decl.parent { DeclaratorParent::PortDeclId(port_declaration_id) => module.get(port_declaration_id), _ => return None, @@ -144,7 +147,7 @@ pub(super) fn resolve_non_ansi_port( header.ty() }; - Some((dir, ty)) + Some((name, dir, ty)) } pub(super) fn add_port_token(