diff --git a/crates/mun_hir/src/adt.rs b/crates/mun_hir/src/adt.rs index add0cf76c..e9d139e6f 100644 --- a/crates/mun_hir/src/adt.rs +++ b/crates/mun_hir/src/adt.rs @@ -3,13 +3,15 @@ use std::{fmt, sync::Arc}; use crate::type_ref::{LocalTypeRefId, TypeRefBuilder, TypeRefMap, TypeRefSourceMap}; use crate::{ arena::{Arena, Idx}, - ids::{AstItemDef, StructId, TypeAliasId}, + ids::{StructId, TypeAliasId}, AsName, DefDatabase, Name, }; use mun_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; pub use mun_syntax::ast::StructMemoryKind; +use crate::ids::Lookup; + /// A single field of a record /// ```mun /// struct Foo { @@ -61,21 +63,18 @@ pub struct StructData { impl StructData { pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc { - let src = id.source(db); - let name = src - .value - .name() - .map(|n| n.as_name()) - .unwrap_or_else(Name::missing); + let loc = id.lookup(db); + let item_tree = db.item_tree(loc.id.file_id); + let strukt = &item_tree[loc.id.value]; + let src = item_tree.source(db, loc.id); let memory_kind = src - .value .memory_type_specifier() .map(|s| s.kind()) .unwrap_or_default(); let mut type_ref_builder = TypeRefBuilder::default(); - let (fields, kind) = match src.value.kind() { + let (fields, kind) = match src.kind() { ast::StructKind::Record(r) => { let fields = r .fields() @@ -102,7 +101,7 @@ impl StructData { let (type_ref_map, type_ref_source_map) = type_ref_builder.finish(); Arc::new(StructData { - name, + name: strukt.name.clone(), fields, kind, memory_kind, @@ -132,18 +131,16 @@ impl TypeAliasData { db: &dyn DefDatabase, id: TypeAliasId, ) -> Arc { - let src = id.source(db); - let name = src - .value - .name() - .map(|n| n.as_name()) - .unwrap_or_else(Name::missing); + let loc = id.lookup(db); + let item_tree = db.item_tree(loc.id.file_id); + let alias = &item_tree[loc.id.value]; + let src = item_tree.source(db, loc.id); let mut type_ref_builder = TypeRefBuilder::default(); - let type_ref_opt = src.value.type_ref(); + let type_ref_opt = src.type_ref(); let type_ref_id = type_ref_builder.alloc_from_node_opt(type_ref_opt.as_ref()); let (type_ref_map, type_ref_source_map) = type_ref_builder.finish(); Arc::new(TypeAliasData { - name, + name: alias.name.clone(), type_ref_id, type_ref_map, type_ref_source_map, diff --git a/crates/mun_hir/src/code_model.rs b/crates/mun_hir/src/code_model.rs index 0b6d45534..edf48bf40 100644 --- a/crates/mun_hir/src/code_model.rs +++ b/crates/mun_hir/src/code_model.rs @@ -1,24 +1,22 @@ pub(crate) mod src; -use self::src::HasSource; use crate::adt::{LocalStructFieldId, StructData, TypeAliasData}; use crate::builtin_type::BuiltinType; use crate::code_model::diagnostics::ModuleDefinitionDiagnostic; use crate::diagnostics::DiagnosticSink; use crate::expr::validator::{ExprValidator, TypeAliasValidator}; use crate::expr::{Body, BodySourceMap}; -use crate::ids::AstItemDef; -use crate::ids::LocationCtx; +use crate::ids::{FunctionLoc, Intern, Lookup, StructLoc, TypeAliasLoc}; +use crate::item_tree::ModItem; use crate::name_resolution::Namespace; -use crate::raw::{DefKind, RawFileItem}; use crate::resolve::{Resolution, Resolver}; use crate::ty::{lower::LowerBatchResult, InferenceResult}; use crate::type_ref::{LocalTypeRefId, TypeRefBuilder, TypeRefMap, TypeRefSourceMap}; use crate::{ ids::{FunctionId, StructId, TypeAliasId}, - AsName, DefDatabase, FileId, HirDatabase, Name, Ty, + DefDatabase, FileId, HirDatabase, InFile, Name, Ty, }; -use mun_syntax::ast::{ExternOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner}; +use mun_syntax::ast::{TypeAscriptionOwner, VisibilityOwner}; use rustc_hash::FxHashMap; use std::sync::Arc; @@ -75,41 +73,47 @@ pub struct ModuleScope { impl ModuleData { pub(crate) fn module_data_query(db: &dyn DefDatabase, file_id: FileId) -> Arc { - let items = db.raw_items(file_id); + let items = db.item_tree(file_id); let mut data = ModuleData::default(); - let loc_ctx = LocationCtx::new(db, file_id); let mut definition_by_name = FxHashMap::default(); - for item in items.items().iter() { + for item in items.top_level_items() { + let name = match item { + ModItem::Function(item) => items[*item].name.clone(), + ModItem::Struct(item) => items[*item].name.clone(), + ModItem::TypeAlias(item) => items[*item].name.clone(), + }; + + if let Some(prev_definition) = definition_by_name.get(&name) { + data.diagnostics + .push(diagnostics::ModuleDefinitionDiagnostic::DuplicateName { + name, + definition: *item, + first_definition: *prev_definition, + }) + } else { + definition_by_name.insert(name, *item); + } + match item { - RawFileItem::Definition(def) => { - if let Some(prev_definition) = definition_by_name.get(&items[*def].name) { - data.diagnostics.push( - diagnostics::ModuleDefinitionDiagnostic::DuplicateName { - name: items[*def].name.clone(), - definition: *def, - first_definition: *prev_definition, - }, - ) - } else { - definition_by_name.insert(items[*def].name.clone(), *def); + ModItem::Function(item) => data.definitions.push(ModuleDef::Function(Function { + id: FunctionLoc { + id: InFile::new(file_id, *item), } - match items[*def].kind { - DefKind::Function(ast_id) => { - data.definitions.push(ModuleDef::Function(Function { - id: FunctionId::from_ast_id(loc_ctx, ast_id), - })) - } - DefKind::Struct(ast_id) => { - data.definitions.push(ModuleDef::Struct(Struct { - id: StructId::from_ast_id(loc_ctx, ast_id), - })) - } - DefKind::TypeAlias(ast_id) => { - data.definitions.push(ModuleDef::TypeAlias(TypeAlias { - id: TypeAliasId::from_ast_id(loc_ctx, ast_id), - })) - } + .intern(db), + })), + ModItem::Struct(item) => data.definitions.push(ModuleDef::Struct(Struct { + id: StructLoc { + id: InFile::new(file_id, *item), } + .intern(db), + })), + ModItem::TypeAlias(item) => { + data.definitions.push(ModuleDef::TypeAlias(TypeAlias { + id: TypeAliasLoc { + id: InFile::new(file_id, *item), + } + .intern(db), + })) } }; } @@ -230,7 +234,7 @@ pub struct Function { } #[derive(Debug, PartialEq, Eq)] -pub struct FnData { +pub struct FunctionData { name: Name, params: Vec, visibility: Visibility, @@ -240,31 +244,29 @@ pub struct FnData { is_extern: bool, } -impl FnData { - pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: Function) -> Arc { - let src = func.source(db); +impl FunctionData { + pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc { + let loc = func.lookup(db); + let item_tree = db.item_tree(loc.id.file_id); + let func = &item_tree[loc.id.value]; + let src = item_tree.source(db, loc.id); + let mut type_ref_builder = TypeRefBuilder::default(); - let name = src - .value - .name() - .map(|n| n.as_name()) - .unwrap_or_else(Name::missing); let visibility = src - .value .visibility() .map(|_v| Visibility::Public) .unwrap_or(Visibility::Private); let mut params = Vec::new(); - if let Some(param_list) = src.value.param_list() { + if let Some(param_list) = src.param_list() { for param in param_list.params() { let type_ref = type_ref_builder.alloc_from_node_opt(param.ascribed_type().as_ref()); params.push(type_ref); } } - let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) { + let ret_type = if let Some(type_ref) = src.ret_type().and_then(|rt| rt.type_ref()) { type_ref_builder.alloc_from_node(&type_ref) } else { type_ref_builder.unit() @@ -272,16 +274,14 @@ impl FnData { let (type_ref_map, type_ref_source_map) = type_ref_builder.finish(); - let is_extern = src.value.is_extern(); - - Arc::new(FnData { - name, + Arc::new(FunctionData { + name: func.name.clone(), params, visibility, ret_type, type_ref_map, type_ref_source_map, - is_extern, + is_extern: func.is_extern, }) } @@ -313,7 +313,7 @@ impl FnData { impl Function { pub fn module(self, db: &dyn DefDatabase) -> Module { Module { - file_id: self.id.file_id(db), + file_id: self.id.lookup(db).id.file_id, } } @@ -325,8 +325,8 @@ impl Function { self.data(db).visibility() } - pub fn data(self, db: &dyn HirDatabase) -> Arc { - db.fn_data(self) + pub fn data(self, db: &dyn HirDatabase) -> Arc { + db.fn_data(self.id) } pub fn body(self, db: &dyn HirDatabase) -> Arc { @@ -343,7 +343,7 @@ impl Function { } pub fn is_extern(self, db: &dyn HirDatabase) -> bool { - db.fn_data(self).is_extern + db.fn_data(self.id).is_extern } pub(crate) fn body_source_map(self, db: &dyn HirDatabase) -> Arc { @@ -396,7 +396,7 @@ impl StructField { impl Struct { pub fn module(self, db: &dyn DefDatabase) -> Module { Module { - file_id: self.id.file_id(db), + file_id: self.id.lookup(db).id.file_id, } } @@ -458,7 +458,7 @@ pub struct TypeAlias { impl TypeAlias { pub fn module(self, db: &dyn DefDatabase) -> Module { Module { - file_id: self.id.file_id(db), + file_id: self.id.lookup(db).id.file_id, } } @@ -501,7 +501,7 @@ impl TypeAlias { mod diagnostics { use super::Module; use crate::diagnostics::{DiagnosticSink, DuplicateDefinition}; - use crate::raw::{DefKind, LocalDefId}; + use crate::item_tree::{ItemTreeId, ModItem}; use crate::{DefDatabase, Name}; use mun_syntax::{AstNode, SyntaxNodePtr}; @@ -509,21 +509,23 @@ mod diagnostics { pub(super) enum ModuleDefinitionDiagnostic { DuplicateName { name: Name, - definition: LocalDefId, - first_definition: LocalDefId, + definition: ModItem, + first_definition: ModItem, }, } - fn syntax_ptr_from_def(db: &dyn DefDatabase, owner: Module, kind: DefKind) -> SyntaxNodePtr { - match kind { - DefKind::Function(id) => { - SyntaxNodePtr::new(id.with_file_id(owner.file_id).to_node(db).syntax()) + fn syntax_ptr_from_def(db: &dyn DefDatabase, owner: Module, item: ModItem) -> SyntaxNodePtr { + let file_id = owner.file_id; + let item_tree = db.item_tree(file_id); + match item { + ModItem::Function(id) => { + SyntaxNodePtr::new(item_tree.source(db, ItemTreeId::new(file_id, id)).syntax()) } - DefKind::Struct(id) => { - SyntaxNodePtr::new(id.with_file_id(owner.file_id).to_node(db).syntax()) + ModItem::Struct(id) => { + SyntaxNodePtr::new(item_tree.source(db, ItemTreeId::new(file_id, id)).syntax()) } - DefKind::TypeAlias(id) => { - SyntaxNodePtr::new(id.with_file_id(owner.file_id).to_node(db).syntax()) + ModItem::TypeAlias(id) => { + SyntaxNodePtr::new(item_tree.source(db, ItemTreeId::new(file_id, id)).syntax()) } } } @@ -540,19 +542,12 @@ mod diagnostics { name, definition, first_definition, - } => { - let raw_items = db.raw_items(owner.file_id); - sink.push(DuplicateDefinition { - file: owner.file_id, - name: name.to_string(), - definition: syntax_ptr_from_def(db, owner, raw_items[*definition].kind), - first_definition: syntax_ptr_from_def( - db, - owner, - raw_items[*first_definition].kind, - ), - }) - } + } => sink.push(DuplicateDefinition { + file: owner.file_id, + name: name.to_string(), + definition: syntax_ptr_from_def(db, owner, *definition), + first_definition: syntax_ptr_from_def(db, owner, *first_definition), + }), } } } diff --git a/crates/mun_hir/src/code_model/src.rs b/crates/mun_hir/src/code_model/src.rs index b24eb2458..00fad3435 100644 --- a/crates/mun_hir/src/code_model/src.rs +++ b/crates/mun_hir/src/code_model/src.rs @@ -1,32 +1,49 @@ use crate::code_model::{Function, Struct, StructField, TypeAlias}; -use crate::ids::AstItemDef; +use crate::ids::Lookup; use crate::in_file::InFile; -use crate::DefDatabase; +use crate::item_tree::ItemTreeNode; +use crate::{DefDatabase, ItemLoc}; use mun_syntax::ast; pub trait HasSource { type Ast; - fn source(self, db: &dyn DefDatabase) -> InFile; + fn source(&self, db: &dyn DefDatabase) -> InFile; +} + +impl HasSource for ItemLoc { + type Ast = N::Source; + + fn source(&self, db: &dyn DefDatabase) -> InFile { + let tree = db.item_tree(self.id.file_id); + let ast_id_map = db.ast_id_map(self.id.file_id); + let root = db.parse(self.id.file_id); + let node = &tree[self.id.value]; + + InFile::new( + self.id.file_id, + ast_id_map.get(node.ast_id()).to_node(&root.syntax_node()), + ) + } } impl HasSource for Function { type Ast = ast::FunctionDef; - fn source(self, db: &dyn DefDatabase) -> InFile { - self.id.source(db) + fn source(&self, db: &dyn DefDatabase) -> InFile { + self.id.lookup(db).source(db) } } impl HasSource for Struct { type Ast = ast::StructDef; - fn source(self, db: &dyn DefDatabase) -> InFile { - self.id.source(db) + fn source(&self, db: &dyn DefDatabase) -> InFile { + self.id.lookup(db).source(db) } } impl HasSource for StructField { type Ast = ast::RecordFieldDef; - fn source(self, db: &dyn DefDatabase) -> InFile { + fn source(&self, db: &dyn DefDatabase) -> InFile { let src = self.parent.source(db); let file_id = src.file_id; let field_sources = if let ast::StructKind::Record(r) = src.value.kind() { @@ -48,7 +65,7 @@ impl HasSource for StructField { impl HasSource for TypeAlias { type Ast = ast::TypeAliasDef; - fn source(self, db: &dyn DefDatabase) -> InFile { - self.id.source(db) + fn source(&self, db: &dyn DefDatabase) -> InFile { + self.id.lookup(db).source(db) } } diff --git a/crates/mun_hir/src/db.rs b/crates/mun_hir/src/db.rs index c955933e6..f890fd02c 100644 --- a/crates/mun_hir/src/db.rs +++ b/crates/mun_hir/src/db.rs @@ -1,17 +1,19 @@ #![allow(clippy::type_repetition_in_bounds)] +use crate::ids::FunctionId; use crate::input::{SourceRoot, SourceRootId}; +use crate::item_tree::{self, ItemTree}; use crate::name_resolution::Namespace; use crate::ty::lower::LowerBatchResult; use crate::ty::{CallableDef, FnSig, Ty, TypableDef}; use crate::{ adt::{StructData, TypeAliasData}, - code_model::{DefWithBody, FnData, Function, ModuleData}, + code_model::{DefWithBody, FunctionData, ModuleData}, ids, line_index::LineIndex, name_resolution::ModuleScope, ty::InferenceResult, - AstIdMap, ExprScopes, FileId, RawItems, Struct, TypeAlias, + AstIdMap, ExprScopes, FileId, Struct, TypeAlias, }; use mun_syntax::{ast, Parse, SourceFile}; use mun_target::abi; @@ -66,18 +68,17 @@ pub trait AstDatabase: SourceDatabase { #[salsa::query_group(InternDatabaseStorage)] pub trait InternDatabase: SourceDatabase { #[salsa::interned] - fn intern_function(&self, loc: ids::ItemLoc) -> ids::FunctionId; + fn intern_function(&self, loc: ids::FunctionLoc) -> ids::FunctionId; #[salsa::interned] - fn intern_struct(&self, loc: ids::ItemLoc) -> ids::StructId; + fn intern_struct(&self, loc: ids::StructLoc) -> ids::StructId; #[salsa::interned] - fn intern_type_alias(&self, loc: ids::ItemLoc) -> ids::TypeAliasId; + fn intern_type_alias(&self, loc: ids::TypeAliasLoc) -> ids::TypeAliasId; } #[salsa::query_group(DefDatabaseStorage)] pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { - /// Returns the top level items of a file. - #[salsa::invoke(RawItems::raw_file_items_query)] - fn raw_items(&self, file_id: FileId) -> Arc; + #[salsa::invoke(item_tree::ItemTree::item_tree_query)] + fn item_tree(&self, file_id: FileId) -> Arc; #[salsa::invoke(StructData::struct_data_query)] fn struct_data(&self, id: ids::StructId) -> Arc; @@ -85,8 +86,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { #[salsa::invoke(TypeAliasData::type_alias_data_query)] fn type_alias_data(&self, id: ids::TypeAliasId) -> Arc; - #[salsa::invoke(crate::FnData::fn_data_query)] - fn fn_data(&self, func: Function) -> Arc; + #[salsa::invoke(crate::FunctionData::fn_data_query)] + fn fn_data(&self, func: FunctionId) -> Arc; /// Returns the module data of the specified file #[salsa::invoke(crate::code_model::ModuleData::module_data_query)] diff --git a/crates/mun_hir/src/expr/validator/tests.rs b/crates/mun_hir/src/expr/validator/tests.rs index d885090d5..42394f4bc 100644 --- a/crates/mun_hir/src/expr/validator/tests.rs +++ b/crates/mun_hir/src/expr/validator/tests.rs @@ -1,12 +1,10 @@ use crate::{ - db::{AstDatabase, Upcast}, + db::DefDatabase, diagnostics::DiagnosticSink, expr::validator::{ExprValidator, TypeAliasValidator}, - ids::LocationCtx, mock::MockDatabase, - Function, TypeAlias, + ModuleDef, }; -use mun_syntax::{ast, AstNode}; use std::fmt::Write; #[test] @@ -82,7 +80,6 @@ fn test_free_type_alias_without_type_ref() { fn diagnostics(content: &str) -> String { let (db, file_id) = MockDatabase::with_single_file(content); - let source_file = db.parse(file_id).ok().unwrap(); let mut diags = String::new(); @@ -90,21 +87,18 @@ fn diagnostics(content: &str) -> String { write!(diags, "{}: {}\n", diag.highlight_range(), diag.message()).unwrap(); }); - let ctx = LocationCtx::new(db.upcast(), file_id); - for node in source_file.syntax().descendants() { - if let Some(def) = ast::FunctionDef::cast(node.clone()) { - let fun = Function { - id: ctx.to_def(&def), - }; - ExprValidator::new(fun, &db).validate_body(&mut diag_sink); - } - if let Some(def) = ast::TypeAliasDef::cast(node.clone()) { - let type_alias = TypeAlias { - id: ctx.to_def(&def), - }; - TypeAliasValidator::new(type_alias, &db).validate_target_type_existence(&mut diag_sink); + for item in db.module_data(file_id).definitions() { + match item { + ModuleDef::Function(item) => { + ExprValidator::new(*item, &db).validate_body(&mut diag_sink); + } + ModuleDef::TypeAlias(item) => { + TypeAliasValidator::new(*item, &db).validate_target_type_existence(&mut diag_sink); + } + _ => {} } } + drop(diag_sink); diags } diff --git a/crates/mun_hir/src/ids.rs b/crates/mun_hir/src/ids.rs index 22d756395..7b89a6e6d 100644 --- a/crates/mun_hir/src/ids.rs +++ b/crates/mun_hir/src/ids.rs @@ -1,134 +1,96 @@ -use crate::in_file::InFile; -use crate::source_id::{AstId, FileAstId}; -use crate::{DefDatabase, FileId}; -use mun_syntax::{ast, AstNode}; +use crate::item_tree::{Function, ItemTreeId, ItemTreeNode, Struct, TypeAlias}; +use crate::DefDatabase; use std::hash::{Hash, Hasher}; -macro_rules! impl_intern_key { - ($name:ident) => { - impl salsa::InternKey for $name { - fn from_intern_id(v: salsa::InternId) -> Self { - $name(v) - } - fn as_intern_id(&self) -> salsa::InternId { - self.0 - } - } - }; -} - #[derive(Debug)] -pub struct ItemLoc { - ast_id: AstId, +pub struct ItemLoc { + pub id: ItemTreeId, } -impl PartialEq for ItemLoc { +impl PartialEq for ItemLoc { fn eq(&self, other: &Self) -> bool { - self.ast_id == other.ast_id + self.id == other.id } } -impl Eq for ItemLoc {} -impl Hash for ItemLoc { +impl Eq for ItemLoc {} + +impl Hash for ItemLoc { fn hash(&self, hasher: &mut H) { - self.ast_id.hash(hasher); + self.id.hash(hasher); } } -impl Clone for ItemLoc { +impl Clone for ItemLoc { fn clone(&self) -> ItemLoc { - ItemLoc { - ast_id: self.ast_id, - } + ItemLoc { id: self.id } } } +impl Copy for ItemLoc {} -#[derive(Clone, Copy)] -pub(crate) struct LocationCtx { - db: DB, - file_id: FileId, -} - -impl<'a> LocationCtx<&'a dyn DefDatabase> { - pub(crate) fn new( - db: &'a dyn DefDatabase, - file_id: FileId, - ) -> LocationCtx<&'a dyn DefDatabase> { - LocationCtx { db, file_id } - } - - pub(crate) fn to_def(self, ast: &N) -> DEF - where - N: AstNode, - DEF: AstItemDef, - { - DEF::from_ast(self, ast) - } +macro_rules! impl_intern_key { + ($name:ident) => { + impl salsa::InternKey for $name { + fn from_intern_id(v: salsa::InternId) -> Self { + $name(v) + } + fn as_intern_id(&self) -> salsa::InternId { + self.0 + } + } + }; } -pub(crate) trait AstItemDef: salsa::InternKey + Clone { - fn intern(db: &dyn DefDatabase, loc: ItemLoc) -> Self; - fn lookup_intern(self, db: &dyn DefDatabase) -> ItemLoc; - - fn from_ast(ctx: LocationCtx<&dyn DefDatabase>, ast: &N) -> Self { - let items = ctx.db.ast_id_map(ctx.file_id); - let item_id = items.ast_id(ast); - Self::from_ast_id(ctx, item_id) - } - - fn from_ast_id(ctx: LocationCtx<&dyn DefDatabase>, ast_id: FileAstId) -> Self { - let loc = ItemLoc { - ast_id: ast_id.with_file_id(ctx.file_id), - }; - Self::intern(ctx.db, loc) - } +macro_rules! impl_intern { + ($id:ident, $loc:ident, $intern:ident, $lookup:ident) => { + impl_intern_key!($id); - fn source(self, db: &dyn DefDatabase) -> InFile { - let loc = self.lookup_intern(db); - let ast = loc.ast_id.to_node(db); - InFile::new(loc.ast_id.file_id, ast) - } + impl Intern for $loc { + type ID = $id; + fn intern(self, db: &dyn DefDatabase) -> $id { + db.$intern(self) + } + } - fn file_id(self, db: &dyn DefDatabase) -> FileId { - self.lookup_intern(db).ast_id.file_id - } + impl Lookup for $id { + type Data = $loc; + fn lookup(&self, db: &dyn DefDatabase) -> $loc { + db.$lookup(*self) + } + } + }; } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct FunctionId(salsa::InternId); -impl_intern_key!(FunctionId); - -impl AstItemDef for FunctionId { - fn intern(db: &dyn DefDatabase, loc: ItemLoc) -> Self { - db.intern_function(loc) - } - fn lookup_intern(self, db: &dyn DefDatabase) -> ItemLoc { - db.lookup_intern_function(self) - } -} +pub(crate) type FunctionLoc = ItemLoc; +impl_intern!( + FunctionId, + FunctionLoc, + intern_function, + lookup_intern_function +); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct StructId(salsa::InternId); -impl_intern_key!(StructId); - -impl AstItemDef for StructId { - fn intern(db: &dyn DefDatabase, loc: ItemLoc) -> Self { - db.intern_struct(loc) - } - - fn lookup_intern(self, db: &dyn DefDatabase) -> ItemLoc { - db.lookup_intern_struct(self) - } -} +pub(crate) type StructLoc = ItemLoc; +impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TypeAliasId(salsa::InternId); -impl_intern_key!(TypeAliasId); +pub(crate) type TypeAliasLoc = ItemLoc; +impl_intern!( + TypeAliasId, + TypeAliasLoc, + intern_type_alias, + lookup_intern_type_alias +); + +pub trait Intern { + type ID; + fn intern(self, db: &dyn DefDatabase) -> Self::ID; +} -impl AstItemDef for TypeAliasId { - fn intern(db: &dyn DefDatabase, loc: ItemLoc) -> Self { - db.intern_type_alias(loc) - } - fn lookup_intern(self, db: &dyn DefDatabase) -> ItemLoc { - db.lookup_intern_type_alias(self) - } +pub trait Lookup { + type Data; + fn lookup(&self, db: &dyn DefDatabase) -> Self::Data; } diff --git a/crates/mun_hir/src/item_tree.rs b/crates/mun_hir/src/item_tree.rs new file mode 100644 index 000000000..e12ae1ea3 --- /dev/null +++ b/crates/mun_hir/src/item_tree.rs @@ -0,0 +1,294 @@ +mod lower; +#[cfg(test)] +mod tests; + +use crate::{ + arena::{Arena, Idx}, + source_id::FileAstId, + type_ref::TypeRef, + DefDatabase, FileId, InFile, Name, +}; +use mun_syntax::{ast, AstNode}; +use std::{ + any::type_name, + fmt, + fmt::Formatter, + hash::{Hash, Hasher}, + marker::PhantomData, + ops::{Index, Range}, + sync::Arc, +}; + +/// An `ItemTree` is a derivative of an AST that only contains the items defined in the AST. +#[derive(Debug, Eq, PartialEq)] +pub struct ItemTree { + top_level: Vec, + data: ItemTreeData, +} + +impl ItemTree { + /// Constructs a new `ItemTree` for the specified `file_id` + pub fn item_tree_query(db: &dyn DefDatabase, file_id: FileId) -> Arc { + let syntax = db.parse(file_id); + let item_tree = lower::Context::new(db, file_id).lower_module_items(&syntax.tree()); + Arc::new(item_tree) + } + + /// Returns a slice over all items located at the top level of the `FileId` for which this + /// `ItemTree` was constructed. + pub fn top_level_items(&self) -> &[ModItem] { + &self.top_level + } + + /// Returns the source location of the specified item. Note that the `file_id` of the item must + /// be the same `file_id` that was used to create this `ItemTree`. + pub fn source(&self, db: &dyn DefDatabase, item: ItemTreeId) -> S::Source { + let root = db.parse(item.file_id); + + let id = self[item.value].ast_id(); + let map = db.ast_id_map(item.file_id); + let ptr = map.get(id); + ptr.to_node(&root.syntax_node()) + } +} + +#[derive(Default, Debug, Eq, PartialEq)] +struct ItemTreeData { + functions: Arena, + structs: Arena, + fields: Arena, + type_aliases: Arena, +} + +/// Trait implemented by all item nodes in the item tree. +pub trait ItemTreeNode: Clone { + type Source: AstNode + Into; + + /// Returns the AST id for this instance + fn ast_id(&self) -> FileAstId; + + /// Looks up an instance of `Self` in an item tree. + fn lookup(tree: &ItemTree, index: Idx) -> &Self; + + /// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type + fn id_from_mod_item(mod_item: ModItem) -> Option>; + + /// Upcasts a `FileItemTreeId` to a generic ModItem. + fn id_to_mod_item(id: LocalItemTreeId) -> ModItem; +} + +/// The typed Id of an item in an `ItemTree` +pub struct LocalItemTreeId { + index: Idx, + _p: PhantomData, +} + +impl Clone for LocalItemTreeId { + fn clone(&self) -> Self { + Self { + index: self.index, + _p: PhantomData, + } + } +} +impl Copy for LocalItemTreeId {} + +impl PartialEq for LocalItemTreeId { + fn eq(&self, other: &Self) -> bool { + self.index == other.index + } +} +impl Eq for LocalItemTreeId {} + +impl Hash for LocalItemTreeId { + fn hash(&self, state: &mut H) { + self.index.hash(state) + } +} + +impl fmt::Debug for LocalItemTreeId { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.index.fmt(f) + } +} + +/// Represents the Id of an item in the ItemTree of a file. +pub type ItemTreeId = InFile>; + +macro_rules! mod_items { + ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)?) => { + #[derive(Debug,Copy,Clone,Eq,PartialEq,Hash)] + pub enum ModItem { + $( + $typ(LocalItemTreeId<$typ>), + )+ + } + + $( + impl From> for ModItem { + fn from(id: LocalItemTreeId<$typ>) -> ModItem { + ModItem::$typ(id) + } + } + )+ + + $( + impl ItemTreeNode for $typ { + type Source = $ast; + + fn ast_id(&self) -> FileAstId { + self.ast_id + } + + fn lookup(tree: &ItemTree, index: Idx) -> &Self { + &tree.data.$fld[index] + } + + fn id_from_mod_item(mod_item: ModItem) -> Option> { + if let ModItem::$typ(id) = mod_item { + Some(id) + } else { + None + } + } + + fn id_to_mod_item(id: LocalItemTreeId) -> ModItem { + ModItem::$typ(id) + } + } + + impl Index> for ItemTree { + type Output = $typ; + + fn index(&self, index: Idx<$typ>) -> &Self::Output { + &self.data.$fld[index] + } + } + )+ + }; +} + +mod_items! { + Function in functions -> ast::FunctionDef, + Struct in structs -> ast::StructDef, + TypeAlias in type_aliases -> ast::TypeAliasDef, +} + +macro_rules! impl_index { + ( $($fld:ident: $t:ty),+ $(,)? ) => { + $( + impl Index> for ItemTree { + type Output = $t; + + fn index(&self, index: Idx<$t>) -> &Self::Output { + &self.data.$fld[index] + } + } + )+ + }; +} + +impl_index!(fields: Field); + +impl Index> for ItemTree { + type Output = N; + fn index(&self, id: LocalItemTreeId) -> &N { + N::lookup(self, id.index) + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Function { + pub name: Name, + pub is_extern: bool, + pub params: Box<[TypeRef]>, + pub ret_type: TypeRef, + pub ast_id: FileAstId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Struct { + pub name: Name, + pub fields: Fields, + pub ast_id: FileAstId, + pub kind: StructDefKind, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypeAlias { + pub name: Name, + pub type_ref: Option, + pub ast_id: FileAstId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum StructDefKind { + /// `struct S { ... }` - type namespace only. + Record, + /// `struct S(...);` + Tuple, + /// `struct S;` + Unit, +} + +/// A set of fields +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Fields { + Record(IdRange), + Tuple(IdRange), + Unit, +} + +/// A single field of an enum variant or struct +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Field { + pub name: Name, + pub type_ref: TypeRef, +} + +/// A range of Ids +pub struct IdRange { + range: Range, + _p: PhantomData, +} + +impl IdRange { + fn new(range: Range>) -> Self { + Self { + range: range.start.into_raw().into()..range.end.into_raw().into(), + _p: PhantomData, + } + } +} + +impl Iterator for IdRange { + type Item = Idx; + fn next(&mut self) -> Option { + self.range.next().map(|raw| Idx::from_raw(raw.into())) + } +} + +impl fmt::Debug for IdRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple(&format!("IdRange::<{}>", type_name::())) + .field(&self.range) + .finish() + } +} + +impl Clone for IdRange { + fn clone(&self) -> Self { + Self { + range: self.range.clone(), + _p: PhantomData, + } + } +} + +impl PartialEq for IdRange { + fn eq(&self, other: &Self) -> bool { + self.range == other.range + } +} + +impl Eq for IdRange {} diff --git a/crates/mun_hir/src/item_tree/lower.rs b/crates/mun_hir/src/item_tree/lower.rs new file mode 100644 index 000000000..a3bdfdac8 --- /dev/null +++ b/crates/mun_hir/src/item_tree/lower.rs @@ -0,0 +1,204 @@ +//! This module implements the logic to convert an AST to an `ItemTree`. + +use super::{ + Field, Fields, Function, IdRange, ItemTree, ItemTreeData, ItemTreeNode, LocalItemTreeId, + ModItem, Struct, StructDefKind, TypeAlias, +}; +use crate::{ + arena::{Idx, RawId}, + name::AsName, + source_id::AstIdMap, + type_ref::TypeRef, + DefDatabase, FileId, Name, +}; +use mun_syntax::{ + ast, + ast::{ExternOwner, ModuleItemOwner, NameOwner, StructKind, TypeAscriptionOwner}, +}; +use std::{convert::TryInto, marker::PhantomData, sync::Arc}; + +impl From> for LocalItemTreeId { + fn from(index: Idx) -> Self { + LocalItemTreeId { + index, + _p: PhantomData, + } + } +} + +pub(super) struct Context { + file: FileId, + source_ast_id_map: Arc, + data: ItemTreeData, +} + +impl Context { + /// Constructs a new `Context` for the specified file + pub(super) fn new(db: &dyn DefDatabase, file: FileId) -> Self { + Self { + file, + source_ast_id_map: db.ast_id_map(file), + data: ItemTreeData::default(), + } + } + + /// Lowers all the items in the specified `ModuleItemOwner` and returns an `ItemTree` + pub(super) fn lower_module_items(mut self, item_owner: &impl ModuleItemOwner) -> ItemTree { + let top_level = item_owner + .items() + .flat_map(|item| self.lower_mod_item(&item)) + .collect(); + ItemTree { + top_level, + data: self.data, + } + } + + /// Lowers a single module item + fn lower_mod_item(&mut self, item: &ast::ModuleItem) -> Option { + match item.kind() { + ast::ModuleItemKind::FunctionDef(ast) => self.lower_function(&ast).map(Into::into), + ast::ModuleItemKind::StructDef(ast) => self.lower_struct(&ast).map(Into::into), + ast::ModuleItemKind::TypeAliasDef(ast) => self.lower_type_alias(&ast).map(Into::into), + } + } + + /// Lowers a function + fn lower_function(&mut self, func: &ast::FunctionDef) -> Option> { + let name = func.name()?.as_name(); + + // Lower all the params + let mut params = Vec::new(); + if let Some(param_list) = func.param_list() { + for param in param_list.params() { + let type_ref = self.lower_type_ref_opt(param.ascribed_type()); + params.push(type_ref); + } + } + + // Lowers the return type + let ret_type = func + .ret_type() + .and_then(|rt| rt.type_ref()) + .map_or_else(|| TypeRef::Empty, |ty| self.lower_type_ref(&ty)); + + let is_extern = func.is_extern(); + + let ast_id = self.source_ast_id_map.ast_id(func); + let res = Function { + name, + is_extern, + params: params.into_boxed_slice(), + ret_type, + ast_id, + }; + + Some(self.data.functions.alloc(res).into()) + } + + /// Lowers a struct + fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option> { + let name = strukt.name()?.as_name(); + let fields = self.lower_fields(&strukt.kind()); + let ast_id = self.source_ast_id_map.ast_id(strukt); + let kind = match strukt.kind() { + StructKind::Record(_) => StructDefKind::Record, + StructKind::Tuple(_) => StructDefKind::Tuple, + StructKind::Unit => StructDefKind::Unit, + }; + let res = Struct { + name, + fields, + ast_id, + kind, + }; + Some(self.data.structs.alloc(res).into()) + } + + /// Lowers the fields of a struct or enum + fn lower_fields(&mut self, struct_kind: &ast::StructKind) -> Fields { + match struct_kind { + StructKind::Record(it) => { + let range = self.lower_record_fields(it); + Fields::Record(range) + } + StructKind::Tuple(it) => { + let range = self.lower_tuple_fields(it); + Fields::Tuple(range) + } + StructKind::Unit => Fields::Unit, + } + } + + /// Lowers records fields (e.g. `{ a: i32, b: i32 }`) + fn lower_record_fields(&mut self, fields: &ast::RecordFieldDefList) -> IdRange { + let start = self.next_field_idx(); + for field in fields.fields() { + if let Some(data) = self.lower_record_field(&field) { + let _idx = self.data.fields.alloc(data); + } + } + let end = self.next_field_idx(); + IdRange::new(start..end) + } + + /// Lowers a record field (e.g. `a:i32`) + fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option { + let name = field.name()?.as_name(); + let type_ref = self.lower_type_ref_opt(field.ascribed_type()); + let res = Field { name, type_ref }; + Some(res) + } + + /// Lowers tuple fields (e.g. `(i32, u8)`) + fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> IdRange { + let start = self.next_field_idx(); + for (i, field) in fields.fields().enumerate() { + let data = self.lower_tuple_field(i, &field); + let _idx = self.data.fields.alloc(data); + } + let end = self.next_field_idx(); + IdRange::new(start..end) + } + + /// Lowers a tuple field (e.g. `i32`) + fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Field { + let name = Name::new_tuple_field(idx); + let type_ref = self.lower_type_ref_opt(field.type_ref()); + Field { name, type_ref } + } + + /// Lowers a type alias (e.g. `type Foo = Bar`) + fn lower_type_alias( + &mut self, + type_alias: &ast::TypeAliasDef, + ) -> Option> { + let name = type_alias.name()?.as_name(); + let type_ref = type_alias.type_ref().map(|ty| self.lower_type_ref(&ty)); + let ast_id = self.source_ast_id_map.ast_id(type_alias); + let res = TypeAlias { + name, + type_ref, + ast_id, + }; + Some(self.data.type_aliases.alloc(res).into()) + } + + /// Lowers an `ast::TypeRef` + fn lower_type_ref(&self, type_ref: &ast::TypeRef) -> TypeRef { + TypeRef::from_ast(type_ref.clone()) + } + + /// Lowers an optional `ast::TypeRef` + fn lower_type_ref_opt(&self, type_ref: Option) -> TypeRef { + type_ref + .map(|ty| self.lower_type_ref(&ty)) + .unwrap_or(TypeRef::Error) + } + + /// Returns the `Idx` of the next `Field` + fn next_field_idx(&self) -> Idx { + let idx: u32 = self.data.fields.len().try_into().expect("too many fields"); + Idx::from_raw(RawId::from(idx)) + } +} diff --git a/crates/mun_hir/src/item_tree/snapshots/mun_hir__item_tree__tests__top_level_items.snap b/crates/mun_hir/src/item_tree/snapshots/mun_hir__item_tree__tests__top_level_items.snap new file mode 100644 index 000000000..c53a7880c --- /dev/null +++ b/crates/mun_hir/src/item_tree/snapshots/mun_hir__item_tree__tests__top_level_items.snap @@ -0,0 +1,25 @@ +--- +source: crates/mun_hir/src/item_tree/tests.rs +expression: "print_item_tree(r#\"\n fn foo(a:i32, b:u8, c:String) -> i32 {}\n fn bar(a:i32, b:u8, c:String) -> {}\n fn baz(a:i32, b:, c:String) -> {}\n extern fn eval(a:String) -> bool;\n\n struct Foo {\n a: i32,\n b: u8,\n c: String,\n }\n struct Foo2 {\n a: i32,\n b: ,\n c: String,\n }\n struct Bar (i32, u32, String)\n struct Baz;\n\n type FooBar = Foo;\n type FooBar = package::Foo;\n \"#).unwrap()" +--- +top-level items: +Function { name: Name(Text("foo")), is_extern: false, params: [Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("i32")) }] }), Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("u8")) }] }), Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("String")) }] })], ret_type: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("i32")) }] }), ast_id: FileAstId { raw: Idx::(0), _ty: PhantomData } } +Function { name: Name(Text("bar")), is_extern: false, params: [Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("i32")) }] }), Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("u8")) }] }), Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("String")) }] })], ret_type: Empty, ast_id: FileAstId { raw: Idx::(1), _ty: PhantomData } } +Function { name: Name(Text("baz")), is_extern: false, params: [Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("i32")) }] }), Error, Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("String")) }] })], ret_type: Empty, ast_id: FileAstId { raw: Idx::(2), _ty: PhantomData } } +Function { name: Name(Text("eval")), is_extern: true, params: [Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("String")) }] })], ret_type: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("bool")) }] }), ast_id: FileAstId { raw: Idx::(3), _ty: PhantomData } } +Struct { name: Name(Text("Foo")), fields: Record(IdRange::(0..3)), ast_id: FileAstId { raw: Idx::(4), _ty: PhantomData }, kind: Record } +> Field { name: Name(Text("a")), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("i32")) }] }) } +> Field { name: Name(Text("b")), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("u8")) }] }) } +> Field { name: Name(Text("c")), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("String")) }] }) } +Struct { name: Name(Text("Foo2")), fields: Record(IdRange::(3..6)), ast_id: FileAstId { raw: Idx::(5), _ty: PhantomData }, kind: Record } +> Field { name: Name(Text("a")), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("i32")) }] }) } +> Field { name: Name(Text("b")), type_ref: Error } +> Field { name: Name(Text("c")), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("String")) }] }) } +Struct { name: Name(Text("Bar")), fields: Tuple(IdRange::(6..9)), ast_id: FileAstId { raw: Idx::(6), _ty: PhantomData }, kind: Tuple } +> Field { name: Name(TupleField(0)), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("i32")) }] }) } +> Field { name: Name(TupleField(1)), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("u32")) }] }) } +> Field { name: Name(TupleField(2)), type_ref: Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("String")) }] }) } +Struct { name: Name(Text("Baz")), fields: Unit, ast_id: FileAstId { raw: Idx::(7), _ty: PhantomData }, kind: Unit } +TypeAlias { name: Name(Text("FooBar")), type_ref: Some(Path(Path { kind: Plain, segments: [PathSegment { name: Name(Text("Foo")) }] })), ast_id: FileAstId { raw: Idx::(8), _ty: PhantomData } } +TypeAlias { name: Name(Text("FooBar")), type_ref: None, ast_id: FileAstId { raw: Idx::(9), _ty: PhantomData } } + diff --git a/crates/mun_hir/src/item_tree/tests.rs b/crates/mun_hir/src/item_tree/tests.rs new file mode 100644 index 000000000..4e7a00cee --- /dev/null +++ b/crates/mun_hir/src/item_tree/tests.rs @@ -0,0 +1,82 @@ +use crate::item_tree::Fields; +use crate::{ + item_tree::{ItemTree, ModItem}, + mock::MockDatabase, + DefDatabase, +}; +use std::{fmt, fmt::Write, sync::Arc}; + +fn item_tree(text: &str) -> Arc { + let (db, file_id) = MockDatabase::with_single_file(text); + db.item_tree(file_id.into()) +} + +fn print_item_tree(text: &str) -> Result { + let tree = item_tree(text); + let mut out = String::new(); + write!(&mut out, "top-level items:\n")?; + for item in tree.top_level_items() { + format_mod_item(&mut out, &tree, *item)?; + write!(&mut out, "\n")?; + } + + Ok(out) +} + +fn format_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) -> fmt::Result { + let mut children = String::new(); + match item { + ModItem::Function(item) => { + write!(out, "{:?}", tree[item])?; + } + ModItem::Struct(item) => { + write!(out, "{:?}", tree[item])?; + match &tree[item].fields { + Fields::Record(a) | Fields::Tuple(a) => { + for field in a.clone() { + write!(children, "{:?}\n", tree[field])?; + } + } + _ => {} + }; + } + ModItem::TypeAlias(item) => { + write!(out, "{:?}", tree[item])?; + } + } + + for line in children.lines() { + write!(out, "\n> {}", line)?; + } + + Ok(()) +} + +#[test] +fn top_level_items() { + insta::assert_snapshot!(print_item_tree( + r#" + fn foo(a:i32, b:u8, c:String) -> i32 {} + fn bar(a:i32, b:u8, c:String) -> {} + fn baz(a:i32, b:, c:String) -> {} + extern fn eval(a:String) -> bool; + + struct Foo { + a: i32, + b: u8, + c: String, + } + struct Foo2 { + a: i32, + b: , + c: String, + } + struct Bar (i32, u32, String) + struct Baz; + + type FooBar = Foo; + type FooBar = package::Foo; + "# + ) + .unwrap()); +} diff --git a/crates/mun_hir/src/lib.rs b/crates/mun_hir/src/lib.rs index 58860bfa0..79651cd68 100644 --- a/crates/mun_hir/src/lib.rs +++ b/crates/mun_hir/src/lib.rs @@ -19,12 +19,12 @@ mod expr; mod ids; mod in_file; mod input; +mod item_tree; pub mod line_index; mod model; mod name; mod name_resolution; mod path; -mod raw; mod resolve; mod source_id; mod ty; @@ -59,7 +59,6 @@ pub use crate::{ name::Name, name_resolution::PerNs, path::{Path, PathKind}, - raw::RawItems, resolve::{Resolution, Resolver}, ty::{ lower::CallableDef, ApplicationTy, FloatTy, InferenceResult, IntTy, ResolveBitness, Ty, @@ -67,10 +66,9 @@ pub use crate::{ }, }; -use crate::{ - name::AsName, - source_id::{AstIdMap, FileAstId}, -}; +use crate::{name::AsName, source_id::AstIdMap}; pub use self::adt::StructMemoryKind; -pub use self::code_model::{FnData, Function, Module, ModuleDef, Struct, TypeAlias, Visibility}; +pub use self::code_model::{ + Function, FunctionData, Module, ModuleDef, Struct, TypeAlias, Visibility, +}; diff --git a/crates/mun_hir/src/source_id.rs b/crates/mun_hir/src/source_id.rs index 4adbbde2f..7bc7a3128 100644 --- a/crates/mun_hir/src/source_id.rs +++ b/crates/mun_hir/src/source_id.rs @@ -27,7 +27,7 @@ impl AstId { } #[derive(Debug)] -pub(crate) struct FileAstId { +pub struct FileAstId { raw: ErasedFileAstId, _ty: PhantomData N>, } diff --git a/crates/mun_hir/src/ty/tests.rs b/crates/mun_hir/src/ty/tests.rs index e9ef25add..262b3678b 100644 --- a/crates/mun_hir/src/ty/tests.rs +++ b/crates/mun_hir/src/ty/tests.rs @@ -1,8 +1,7 @@ use crate::{ - db::AstDatabase, diagnostics::DiagnosticSink, expr::BodySourceMap, ids::LocationCtx, - mock::MockDatabase, Function, HirDisplay, InferenceResult, TypeAlias, + db::DefDatabase, diagnostics::DiagnosticSink, expr::BodySourceMap, mock::MockDatabase, + HirDisplay, InferenceResult, ModuleDef, }; -use mun_syntax::{ast, AstNode}; use std::{fmt::Write, sync::Arc}; #[test] @@ -430,7 +429,6 @@ fn infer_snapshot(text: &str) { fn infer(content: &str) -> String { let (db, file_id) = MockDatabase::with_single_file(content); - let source_file = db.parse(file_id).ok().unwrap(); let mut acc = String::new(); @@ -484,24 +482,20 @@ fn infer(content: &str) -> String { write!(diags, "{}: {}\n", diag.highlight_range(), diag.message()).unwrap(); }); - let ctx = LocationCtx::new(&db, file_id); - for node in source_file.syntax().descendants() { - if let Some(def) = ast::FunctionDef::cast(node.clone()) { - let fun = Function { - id: ctx.to_def(&def), - }; - let source_map = fun.body_source_map(&db); - let infer_result = fun.infer(&db); + for item in db.module_data(file_id).definitions() { + match item { + ModuleDef::Function(fun) => { + let source_map = fun.body_source_map(&db); + let infer_result = fun.infer(&db); - fun.diagnostics(&db, &mut diag_sink); + fun.diagnostics(&db, &mut diag_sink); - infer_def(infer_result, source_map); - } - if let Some(def) = ast::TypeAliasDef::cast(node.clone()) { - let type_alias = TypeAlias { - id: ctx.to_def(&def), - }; - type_alias.diagnostics(&db, &mut diag_sink); + infer_def(infer_result, source_map); + } + ModuleDef::TypeAlias(item) => { + item.diagnostics(&db, &mut diag_sink); + } + _ => {} } }