diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 129be8da75..837daded19 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -5,7 +5,7 @@ use common::diagnostics::{CompleteDiagnostic, GlobalErrorCode}; -use crate::span::db::SpannedHirDb; +use crate::SpannedHirDb; /// All diagnostics accumulated in salsa-db should implement /// [`DiagnosticVoucher`] which defines the conversion into diff --git a/crates/hir/src/hir_def/body.rs b/crates/hir/src/hir_def/body.rs index dfe2a31dc0..be3ff48e00 100644 --- a/crates/hir/src/hir_def/body.rs +++ b/crates/hir/src/hir_def/body.rs @@ -9,9 +9,9 @@ use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap}; use parser::ast::{self, prelude::*}; use rustc_hash::FxHashMap; -use crate::span::{HirOrigin, LocalOrigin}; +use crate::span::HirOrigin; -use super::{Expr, ExprId, Partial, Pat, PatId, Stmt, StmtId, TrackedItemId}; +use super::{Expr, ExprId, Partial, Pat, PatId, Stmt, StmtId, TopLevelMod, TrackedItemId}; #[salsa::tracked] pub struct Body { @@ -24,10 +24,10 @@ pub struct Body { pub exprs: NodeStore>, #[return_ref] pub pats: NodeStore>, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) source_map: BodySourceMap, - #[return_ref] pub(crate) origin: HirOrigin, } @@ -57,8 +57,8 @@ where Ast: SourceAst, Node: EntityRef, { - pub node_to_source: SecondaryMap>, - pub source_to_node: FxHashMap, Node>, + pub node_to_source: SecondaryMap>, + pub source_to_node: FxHashMap, Node>, } impl SourceNodeMap @@ -66,12 +66,12 @@ where Ast: SourceAst, Node: EntityRef, { - pub(crate) fn insert(&mut self, node: Node, ast: LocalOrigin) { + pub(crate) fn insert(&mut self, node: Node, ast: HirOrigin) { self.node_to_source[node] = ast.clone(); self.source_to_node.insert(ast, node); } - pub(crate) fn node_to_source(&self, node: Node) -> &LocalOrigin { + pub(crate) fn node_to_source(&self, node: Node) -> &HirOrigin { &self.node_to_source[node] } } diff --git a/crates/hir/src/hir_def/item.rs b/crates/hir/src/hir_def/item.rs index 8cf77c1441..ed621c6cf1 100644 --- a/crates/hir/src/hir_def/item.rs +++ b/crates/hir/src/hir_def/item.rs @@ -3,10 +3,12 @@ // that may take many arguments depending on the number of fields in the struct. #![allow(clippy::too_many_arguments)] +use common::{InputFile, InputIngot}; use parser::ast; use crate::{ hir_def::TraitRef, + lower, span::{ item::{ LazyConstSpan, LazyContractSpan, LazyEnumSpan, LazyExternFnSpan, LazyFnSpan, @@ -15,10 +17,12 @@ use crate::{ }, HirOrigin, }, + HirDb, }; use super::{ - AttrListId, Body, FnParamListId, GenericParamListId, IdentId, Partial, TypeId, WhereClauseId, + ingot_module_tree_impl, AttrListId, Body, FnParamListId, GenericParamListId, IdentId, + IngotModuleTree, ItemTree, Partial, TypeId, WhereClauseId, }; #[derive( @@ -58,13 +62,21 @@ pub struct TopLevelMod { // of `module_item_tree`. pub name: IdentId, - #[return_ref] - pub(crate) origin: HirOrigin, + pub(crate) ingot: InputIngot, + pub(crate) file: InputFile, } impl TopLevelMod { pub fn lazy_span(self) -> LazyTopLevelModSpan { LazyTopLevelModSpan::new(self) } + + pub fn module_item_tree(self, db: &dyn HirDb) -> &ItemTree { + lower::module_item_tree_impl(db, self) + } + + pub fn ingot_module_tree(self, db: &dyn HirDb) -> &IngotModuleTree { + ingot_module_tree_impl(db, self.ingot(db)) + } } #[salsa::tracked] @@ -76,6 +88,8 @@ pub struct Mod { pub attributes: AttrListId, pub is_pub: bool, + pub top_mod: TopLevelMod, + #[return_ref] pub(crate) origin: HirOrigin, } @@ -98,6 +112,7 @@ pub struct Func { pub ret_ty: Option, pub modifier: ItemModifier, pub body: Option, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -118,6 +133,7 @@ pub struct ExternFunc { pub params: Partial, pub ret_ty: Option, pub modifier: ItemModifier, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -139,6 +155,7 @@ pub struct Struct { pub generic_params: GenericParamListId, pub where_clause: WhereClauseId, pub fields: RecordFieldListId, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -158,6 +175,7 @@ pub struct Contract { pub attributes: AttrListId, pub is_pub: bool, pub fields: RecordFieldListId, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -179,6 +197,7 @@ pub struct Enum { pub generic_params: GenericParamListId, pub where_clause: WhereClauseId, pub variants: EnumVariantListId, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -200,6 +219,7 @@ pub struct TypeAlias { pub generic_params: GenericParamListId, pub where_clause: WhereClauseId, pub ty: Partial, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -219,6 +239,7 @@ pub struct Impl { pub attributes: AttrListId, pub generic_params: GenericParamListId, pub where_clause: WhereClauseId, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -240,6 +261,7 @@ pub struct Trait { pub is_pub: bool, pub generic_params: GenericParamListId, pub where_clause: WhereClauseId, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -260,6 +282,7 @@ pub struct ImplTrait { pub attributes: AttrListId, pub generic_params: GenericParamListId, pub where_clause: WhereClauseId, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -277,6 +300,7 @@ pub struct Const { pub name: Partial, pub body: Partial, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, @@ -293,6 +317,7 @@ pub struct Use { id: TrackedItemId, pub tree: Partial, + pub top_mod: TopLevelMod, #[return_ref] pub(crate) origin: HirOrigin, diff --git a/crates/hir/src/hir_def/item_tree.rs b/crates/hir/src/hir_def/item_tree.rs index 83701290eb..ca0c675d01 100644 --- a/crates/hir/src/hir_def/item_tree.rs +++ b/crates/hir/src/hir_def/item_tree.rs @@ -1,15 +1,6 @@ use std::collections::{BTreeMap, BTreeSet}; -use common::InputFile; -use parser::{ - ast::{self, prelude::*}, - SyntaxNode, -}; - -use crate::{ - hir_def::{module_tree, TopLevelMod}, - lower, HirDb, -}; +use crate::hir_def::TopLevelMod; use super::ItemKind; @@ -18,7 +9,6 @@ use super::ItemKind; /// `module_tree::TopLevelModule`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ItemTree { - pub file: InputFile, pub top_mod: TopLevelMod, pub(crate) item_tree: BTreeMap, } @@ -51,17 +41,6 @@ pub(crate) struct ItemTreeNode { pub(crate) children: BTreeSet, } -#[salsa::tracked(return_ref)] -pub fn module_item_tree(db: &dyn HirDb, file: InputFile) -> ItemTree { - let node = SyntaxNode::new_root(crate::parse_file(db, file)); - let module_tree = module_tree::ingot_module_tree(db, file.ingot(db.upcast())); - - // This cast never fails even if the file content is empty. - let ast_root = ast::Root::cast(node).unwrap(); - let top_mod_name = module_tree.module_name(file); - lower::lower_file(db, file, top_mod_name, ast_root) -} - #[cfg(test)] mod tests { diff --git a/crates/hir/src/hir_def/module_tree.rs b/crates/hir/src/hir_def/module_tree.rs index 364fca0dfa..187e9fc4e6 100644 --- a/crates/hir/src/hir_def/module_tree.rs +++ b/crates/hir/src/hir_def/module_tree.rs @@ -4,9 +4,9 @@ use camino::Utf8Path; use common::{InputFile, InputIngot}; use cranelift_entity::{entity_impl, PrimaryMap}; -use crate::HirDb; +use crate::{lower::map_file_to_mod_impl, HirDb}; -use super::IdentId; +use super::{IdentId, TopLevelMod}; /// This tree represents the structure of an ingot. /// Internal modules are not included in this tree, instead, they are included @@ -56,22 +56,32 @@ use super::IdentId; /// In this case, the tree is actually a forest. But we don't need to care about it. #[derive(Debug, Clone, PartialEq, Eq)] pub struct IngotModuleTree { - pub(crate) root: ToplevelModuleId, - pub(crate) module_tree: PrimaryMap, - pub(crate) file_map: BTreeMap, + pub(crate) root: ModuleTreeNodeId, + pub(crate) module_tree: PrimaryMap, + pub(crate) mod_map: BTreeMap, pub(crate) ingot: InputIngot, } impl IngotModuleTree { - #[inline] - pub fn module_name(&self, file: InputFile) -> IdentId { - self.module_data(file).name + /// Returns the tree node data of the given id. + pub fn tree_node_data(&self, id: ModuleTreeNodeId) -> &ModuleTreeNode { + &self.module_tree[id] } - fn module_data(&self, file: InputFile) -> &ToplevelModule { - let id = self.file_map[&file]; - &self.module_tree[id] + /// Returns the tree node id of the given top level module. + pub fn tree_node(&self, top_mod: TopLevelMod) -> ModuleTreeNodeId { + self.mod_map[&top_mod] + } + + /// Returns the root of the tree, which corresponds to the ingot root file. + pub fn root(&self) -> ModuleTreeNodeId { + self.root + } + + /// Returns an iterator of all top level modules in this ingot. + pub fn all_modules(&self) -> impl Iterator + '_ { + self.mod_map.keys().copied() } } @@ -79,46 +89,47 @@ impl IngotModuleTree { /// top level modules. This function only depends on an ingot structure and /// external ingot dependency, and not depends on file contents. #[salsa::tracked(return_ref)] -pub fn ingot_module_tree(db: &dyn HirDb, ingot: InputIngot) -> IngotModuleTree { +pub fn ingot_module_tree_impl(db: &dyn HirDb, ingot: InputIngot) -> IngotModuleTree { IngotModuleTreeBuilder::new(db, ingot).build() } /// A top level module that is one-to-one mapped to a file. #[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct ToplevelModule { - /// A name of the module. - pub(crate) name: IdentId, - /// A file that this module is defined by. - pub(crate) file: InputFile, - /// A parent of top level module. - /// This is `None` if 1. the module is a root module or 2. the module is a - /// "floating" module. - pub(crate) parent: Option, +pub struct ModuleTreeNode { + pub top_mod: TopLevelMod, + /// A parent of the top level module. + /// This is `None` if + /// 1. the module is a root module or + /// 2. the module is a "floating" module. + pub parent: Option, /// A list of child top level module. - pub(crate) children: BTreeMap>, + pub children: BTreeMap>, } -impl ToplevelModule { - fn new(name: IdentId, file: InputFile) -> Self { +impl ModuleTreeNode { + fn new(top_mod: TopLevelMod) -> Self { Self { - name, - file, + top_mod, parent: None, children: BTreeMap::new(), } } + fn name(&self, db: &dyn HirDb) -> IdentId { + self.top_mod.name(db) + } } +/// An opaque identifier for a module tree node. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) struct ToplevelModuleId(u32); -entity_impl!(ToplevelModuleId); +pub struct ModuleTreeNodeId(u32); +entity_impl!(ModuleTreeNodeId); struct IngotModuleTreeBuilder<'db> { db: &'db dyn HirDb, ingot: InputIngot, - module_tree: PrimaryMap, - file_map: BTreeMap, - path_map: BTreeMap<&'db Utf8Path, ToplevelModuleId>, + module_tree: PrimaryMap, + mod_map: BTreeMap, + path_map: BTreeMap<&'db Utf8Path, ModuleTreeNodeId>, } impl<'db> IngotModuleTreeBuilder<'db> { @@ -127,7 +138,7 @@ impl<'db> IngotModuleTreeBuilder<'db> { db, ingot, module_tree: PrimaryMap::default(), - file_map: BTreeMap::default(), + mod_map: BTreeMap::default(), path_map: BTreeMap::default(), } } @@ -136,66 +147,62 @@ impl<'db> IngotModuleTreeBuilder<'db> { self.set_modules(); self.build_tree(); - let root_file = self.ingot.root_file(self.db.upcast()); - let root = self.file_map[&root_file]; + let top_mod = map_file_to_mod_impl(self.db, self.ingot.root_file(self.db.upcast())); + let root = self.mod_map[&top_mod]; IngotModuleTree { root, module_tree: self.module_tree, - file_map: self.file_map, + mod_map: self.mod_map, ingot: self.ingot, } } fn set_modules(&mut self) { for &file in self.ingot.files(self.db.upcast()) { - let name = self.module_name(file); + let top_mod = map_file_to_mod_impl(self.db, file); - let module_id = self.module_tree.push(ToplevelModule::new(name, file)); + let module_id = self.module_tree.push(ModuleTreeNode::new(top_mod)); self.path_map.insert(file.path(self.db.upcast()), module_id); - self.file_map.insert(file, module_id); + self.mod_map.insert(top_mod, module_id); } } - fn module_name(&self, file: InputFile) -> IdentId { - let path = file.path(self.db.upcast()); - let name = path.file_stem().unwrap(); - IdentId::new(self.db, name.to_string()) - } - fn build_tree(&mut self) { let root = self.ingot.root_file(self.db.upcast()); - for &file in self.ingot.files(self.db.upcast()) { + for &child in self.ingot.files(self.db.upcast()) { // Ignore the root file because it has no parent. - if file == root { + if child == root { continue; } - let file_path = file.path(self.db.upcast()); let root_path = root.path(self.db.upcast()); + let root_mod = map_file_to_mod_impl(self.db, root); + let child_path = child.path(self.db.upcast()); + let child_mod = map_file_to_mod_impl(self.db, child); // If the file is in the same directory as the root file, the file is a direct // child of the root. - if file_path.parent() == root_path.parent() { - let root_mod = self.file_map[&root]; - let cur_mod = self.file_map[&file]; + if child_path.parent() == root_path.parent() { + let root_mod = self.mod_map[&root_mod]; + let cur_mod = self.mod_map[&child_mod]; self.add_branch(root_mod, cur_mod); continue; } - assert!(file_path + assert!(child_path .parent() .unwrap() .starts_with(root_path.parent().unwrap())); - if let Some(parent_mod) = self.parent_module(file) { - let cur_mod = self.file_map[&file]; + if let Some(parent_mod) = self.parent_module(child) { + let cur_mod = self.mod_map[&child_mod]; self.add_branch(parent_mod, cur_mod); } } } - fn parent_module(&self, file: InputFile) -> Option { + fn parent_module(&self, file: InputFile) -> Option { let file_path = file.path(self.db.upcast()); let file_dir = file_path.parent()?; let parent_dir = file_dir.parent()?; @@ -205,8 +212,8 @@ impl<'db> IngotModuleTreeBuilder<'db> { self.path_map.get(parent_mod_path.as_path()).copied() } - fn add_branch(&mut self, parent: ToplevelModuleId, child: ToplevelModuleId) { - let child_name = self.module_tree[child].name; + fn add_branch(&mut self, parent: ModuleTreeNodeId, child: ModuleTreeNodeId) { + let child_name = self.module_tree[child].name(self.db); self.module_tree[parent] .children .entry(child_name) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index a7d1e6f6da..e8c6a464e2 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1,7 +1,11 @@ use common::{InputDb, Upcast}; +use hir_def::ingot_module_tree_impl; pub use lower::parse::ParseDiagnostic; -use lower::parse::{parse_file, ParseDiagnosticAccumulator}; +use lower::{ + map_file_to_mod_impl, module_item_tree_impl, + parse::{parse_file_impl, ParseDiagnosticAccumulator}, +}; pub mod diagnostics; pub mod hir_def; @@ -42,24 +46,38 @@ pub struct Jar( hir_def::UseTreeId, /// Accumulated diagnostics. ParseDiagnosticAccumulator, - /// Tracked functions - hir_def::ingot_module_tree, - hir_def::module_item_tree, - parse_file, + /// Private tracked functions. These are not part of the public API, and + /// thus, can't be accessed from outside of the crate without implementing + /// [`LowerHirDb`] marker trait. + ingot_module_tree_impl, + module_item_tree_impl, + map_file_to_mod_impl, + parse_file_impl, ); -pub trait HirDb: salsa::DbWithJar + InputDb + Upcast { - /// Returns the diagnostics produced by parsing the given file. - fn diagnostics_for_parse(&self, file: common::InputFile) -> Vec - where - Self: Sized, - { - parse_file(self, file); - parse_file::accumulated::(self, file) - } -} +pub trait HirDb: salsa::DbWithJar + InputDb + Upcast {} impl HirDb for DB where DB: ?Sized + salsa::DbWithJar + InputDb + Upcast {} +/// `LowerHirDb` is a marker trait for lowering AST to HIR items. +/// All code that requires [`LowerHirDb`] is considered have a possibility to +/// invalidate the cache in salsa when a revision is updated. Therefore, +/// implementations relying on `LowerHirDb` are prohibited in all +/// Analysis phases. +pub trait LowerHirDb: HirDb + Upcast {} + +/// `SpannedHirDb` is a marker trait for extracting span-dependent information +/// from HIR Items. +/// All code that requires [`SpannedHirDb`] is considered have a possibility to +/// invalidate the cache in salsa when a revision is updated. Therefore, +/// implementations relying on `SpannedHirDb` are prohibited in all +/// Analysis phases. +/// +/// This marker is mainly used to inject [HirOrigin](crate::span::HirOrigin) to +/// generate [CompleteDiagnostic](common::diagnostics::CompleteDiagnostic) from +/// [DiagnosticVoucher](crate::diagnostics::DiagnosticVoucher). +/// See also `[LazySpan]`[`crate::span::LazySpan`] for more details. +pub trait SpannedHirDb: HirDb + Upcast {} + #[cfg(test)] mod test_db { use std::collections::BTreeSet; @@ -70,8 +88,10 @@ mod test_db { }; use crate::{ - hir_def::{module_item_tree, ItemKind, ItemTree}, - span::{db::SpannedHirDb, LazySpan}, + hir_def::{ItemKind, ItemTree, TopLevelMod}, + lower::{map_file_to_mod, module_item_tree}, + span::LazySpan, + LowerHirDb, SpannedHirDb, }; #[derive(Default)] @@ -79,13 +99,13 @@ mod test_db { pub(crate) struct TestDb { storage: salsa::Storage, } - impl SpannedHirDb for TestDb {} - + impl LowerHirDb for TestDb {} impl salsa::Database for TestDb { fn salsa_event(&self, _: salsa::Event) {} } - + /// Implements `ParallelDatabase` to check the all tracked + /// structs/functions are `Send`. impl salsa::ParallelDatabase for TestDb { fn snapshot(&self) -> salsa::Snapshot { salsa::Snapshot::new(TestDb { @@ -93,7 +113,6 @@ mod test_db { }) } } - impl Upcast for TestDb { fn upcast(&self) -> &(dyn common::InputDb + 'static) { self @@ -106,27 +125,28 @@ mod test_db { } impl TestDb { - pub fn parse_source(&mut self, text: &str) -> (InputFile, &ItemTree) { + pub fn parse_source(&mut self, text: &str) -> (TopLevelMod, &ItemTree) { let file = self.standalone_file(text); - (file, module_item_tree(self, file)) + let top_mod = map_file_to_mod(self, file); + (top_mod, module_item_tree(self, top_mod)) } /// Parses the given source text and returns the first inner item in the /// file. - pub fn parse_source_to_first_item(&mut self, text: &str) -> (InputFile, T) + pub fn parse_source_to_first_item(&mut self, text: &str) -> (TopLevelMod, T) where ItemKind: TryInto, { - let (file, tree) = self.parse_source(text); - let top_mod = tree.top_mod; + let (top_mod, tree) = self.parse_source(text); ( - file, + top_mod, tree.children(top_mod).next().unwrap().try_into().unwrap(), ) } - pub fn text_at(&self, file: InputFile, span: &impl LazySpan) -> &str { + pub fn text_at(&self, top_mod: TopLevelMod, span: &impl LazySpan) -> &str { let range = span.resolve(self).range; + let file = top_mod.file(self.upcast()); let text = file.text(self.upcast()); &text[range.start().into()..range.end().into()] } diff --git a/crates/hir/src/lower/body.rs b/crates/hir/src/lower/body.rs index e9bd26e5f0..2fd069f113 100644 --- a/crates/hir/src/lower/body.rs +++ b/crates/hir/src/lower/body.rs @@ -5,7 +5,7 @@ use crate::{ Body, BodySourceMap, Expr, ExprId, NodeStore, Partial, Pat, PatId, Stmt, StmtId, TrackedBodyId, TrackedItemId, }, - span::{HirOrigin, LocalOrigin}, + span::HirOrigin, }; use super::FileLowerCtxt; @@ -52,14 +52,14 @@ pub(super) struct BodyCtxt<'ctxt, 'db> { } impl<'ctxt, 'db> BodyCtxt<'ctxt, 'db> { - pub(super) fn push_expr(&mut self, expr: Expr, origin: LocalOrigin) -> ExprId { + pub(super) fn push_expr(&mut self, expr: Expr, origin: HirOrigin) -> ExprId { let expr_id = self.exprs.push(Partial::Present(expr)); self.source_map.expr_map.insert(expr_id, origin); expr_id } - pub(super) fn push_invalid_expr(&mut self, origin: LocalOrigin) -> ExprId { + pub(super) fn push_invalid_expr(&mut self, origin: HirOrigin) -> ExprId { let expr_id = self.exprs.push(Partial::Absent); self.source_map.expr_map.insert(expr_id, origin); @@ -68,18 +68,18 @@ impl<'ctxt, 'db> BodyCtxt<'ctxt, 'db> { pub(super) fn push_missing_expr(&mut self) -> ExprId { let expr_id = self.exprs.push(Partial::Absent); - self.source_map.expr_map.insert(expr_id, LocalOrigin::None); + self.source_map.expr_map.insert(expr_id, HirOrigin::None); expr_id } - pub(super) fn push_stmt(&mut self, stmt: Stmt, origin: LocalOrigin) -> StmtId { + pub(super) fn push_stmt(&mut self, stmt: Stmt, origin: HirOrigin) -> StmtId { let stmt_id = self.stmts.push(Partial::Present(stmt)); self.source_map.stmt_map.insert(stmt_id, origin); stmt_id } - pub(super) fn push_pat(&mut self, pat: Pat, origin: LocalOrigin) -> PatId { + pub(super) fn push_pat(&mut self, pat: Pat, origin: HirOrigin) -> PatId { let pat_id = self.pats.push(Partial::Present(pat)); self.source_map.pat_map.insert(pat_id, origin); pat_id @@ -87,7 +87,7 @@ impl<'ctxt, 'db> BodyCtxt<'ctxt, 'db> { pub(super) fn push_missing_pat(&mut self) -> PatId { let pat_id = self.pats.push(Partial::Absent); - self.source_map.pat_map.insert(pat_id, LocalOrigin::None); + self.source_map.pat_map.insert(pat_id, HirOrigin::None); pat_id } @@ -104,13 +104,14 @@ impl<'ctxt, 'db> BodyCtxt<'ctxt, 'db> { } fn build(self, ast: &ast::Expr) -> Body { - let origin = HirOrigin::raw(self.f_ctxt.file, ast); + let origin = HirOrigin::raw(ast); let body = Body::new( self.f_ctxt.db, self.bid, self.stmts, self.exprs, self.pats, + self.f_ctxt.top_mod, self.source_map, origin, ); diff --git a/crates/hir/src/lower/expr.rs b/crates/hir/src/lower/expr.rs index ac61bd8034..61add0b2cc 100644 --- a/crates/hir/src/lower/expr.rs +++ b/crates/hir/src/lower/expr.rs @@ -2,7 +2,7 @@ use parser::ast::{self, prelude::*}; use crate::{ hir_def::{expr::*, Body, GenericArgListId, IdentId, IntegerId, LitKind, Pat, PathId, Stmt}, - span::LocalOrigin, + span::HirOrigin, }; use super::body::BodyCtxt; @@ -15,7 +15,7 @@ impl Expr { let lit = LitKind::lower_ast(ctxt.f_ctxt, lit); Self::Lit(lit) } else { - return ctxt.push_invalid_expr(LocalOrigin::raw(&ast)); + return ctxt.push_invalid_expr(HirOrigin::raw(&ast)); } } @@ -166,7 +166,7 @@ impl Expr { } }; - ctxt.push_expr(expr, LocalOrigin::raw(&ast)) + ctxt.push_expr(expr, HirOrigin::raw(&ast)) } pub(super) fn push_to_body_opt(ctxt: &mut BodyCtxt<'_, '_>, ast: Option) -> ExprId { diff --git a/crates/hir/src/lower/item.rs b/crates/hir/src/lower/item.rs index 80e553c803..ea0cecb45b 100644 --- a/crates/hir/src/lower/item.rs +++ b/crates/hir/src/lower/item.rs @@ -10,18 +10,54 @@ use crate::{ use super::FileLowerCtxt; -impl TopLevelMod { - pub(crate) fn lower_ast(ctxt: &mut FileLowerCtxt<'_>, name: IdentId, ast: ast::Root) -> Self { - ctxt.enter_scope(); - - let id = TrackedItemId::TopLevelMod(name); - if let Some(items) = ast.items() { - lower_module_items(ctxt, id, items); +pub(crate) fn lower_module_items( + ctxt: &mut FileLowerCtxt<'_>, + id: TrackedItemId, + items: ast::ItemList, +) { + for item in items { + match item.kind() { + ast::ItemKind::Mod(mod_) => { + Mod::lower_ast(ctxt, id.clone(), mod_); + } + ast::ItemKind::Fn(fn_) => { + Func::lower_ast(ctxt, id.clone(), fn_); + } + ast::ItemKind::Struct(struct_) => { + Struct::lower_ast(ctxt, id.clone(), struct_); + } + ast::ItemKind::Contract(contract) => { + Contract::lower_ast(ctxt, id.clone(), contract); + } + ast::ItemKind::Enum(enum_) => { + Enum::lower_ast(ctxt, id.clone(), enum_); + } + ast::ItemKind::TypeAlias(alias) => { + TypeAlias::lower_ast(ctxt, id.clone(), alias); + } + ast::ItemKind::Impl(impl_) => { + Impl::lower_ast(ctxt, id.clone(), impl_); + } + ast::ItemKind::Trait(trait_) => { + Trait::lower_ast(ctxt, id.clone(), trait_); + } + ast::ItemKind::ImplTrait(impl_trait) => { + ImplTrait::lower_ast(ctxt, id.clone(), impl_trait); + } + ast::ItemKind::Const(const_) => { + Const::lower_ast(ctxt, id.clone(), const_); + } + ast::ItemKind::Use(use_) => { + Use::lower_ast(ctxt, id.clone(), use_); + } + ast::ItemKind::Extern(extern_) => { + if let Some(extern_block) = extern_.extern_block() { + for fn_ in extern_block { + ExternFunc::lower_ast(ctxt, id.clone(), fn_); + } + } + } } - - let origin = HirOrigin::raw(ctxt.file, &ast); - let top_mod = Self::new(ctxt.db, name, origin); - ctxt.leave_scope(top_mod) } } @@ -41,8 +77,8 @@ impl Mod { lower_module_items(ctxt, id.clone(), items); } - let origin = HirOrigin::raw(ctxt.file, &ast); - let mod_ = Self::new(ctxt.db, id, name, attributes, is_pub, origin); + let origin = HirOrigin::raw(&ast); + let mod_ = Self::new(ctxt.db, id, name, attributes, is_pub, ctxt.top_mod, origin); ctxt.leave_scope(mod_) } } @@ -74,7 +110,7 @@ impl Func { ast::Expr::cast(body.syntax().clone()).unwrap(), ) }); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); let fn_ = Self::new( ctxt.db, @@ -87,6 +123,7 @@ impl Func { ret_ty, modifier, body, + ctxt.top_mod, origin, ); ctxt.leave_scope(fn_) @@ -109,7 +146,7 @@ impl Struct { let generic_params = GenericParamListId::lower_ast_opt(ctxt, ast.generic_params()); let where_clause = WhereClauseId::lower_ast_opt(ctxt, ast.where_clause()); let fields = RecordFieldListId::lower_ast_opt(ctxt, ast.fields()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); let struct_ = Self::new( ctxt.db, @@ -120,6 +157,7 @@ impl Struct { generic_params, where_clause, fields, + ctxt.top_mod, origin, ); ctxt.leave_scope(struct_) @@ -140,9 +178,18 @@ impl Contract { let attributes = AttrListId::lower_ast_opt(ctxt, ast.attr_list()); let is_pub = ItemModifier::lower_ast(ast.modifier()).is_pub(); let fields = RecordFieldListId::lower_ast_opt(ctxt, ast.fields()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); - let contract = Self::new(ctxt.db, id, name, attributes, is_pub, fields, origin); + let contract = Self::new( + ctxt.db, + id, + name, + attributes, + is_pub, + fields, + ctxt.top_mod, + origin, + ); ctxt.leave_scope(contract) } } @@ -163,7 +210,7 @@ impl Enum { let generic_params = GenericParamListId::lower_ast_opt(ctxt, ast.generic_params()); let where_clause = WhereClauseId::lower_ast_opt(ctxt, ast.where_clause()); let variants = EnumVariantListId::lower_ast_opt(ctxt, ast.variants()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); let enum_ = Self::new( ctxt.db, @@ -174,6 +221,7 @@ impl Enum { generic_params, where_clause, variants, + ctxt.top_mod, origin, ); ctxt.leave_scope(enum_) @@ -196,7 +244,7 @@ impl TypeAlias { let generic_params = GenericParamListId::lower_ast_opt(ctxt, ast.generic_params()); let where_clause = WhereClauseId::lower_ast_opt(ctxt, ast.where_clause()); let ty = TypeId::lower_ast_partial(ctxt, ast.ty()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); let alias = Self::new( ctxt.db, @@ -207,6 +255,7 @@ impl TypeAlias { generic_params, where_clause, ty, + ctxt.top_mod, origin, ); ctxt.leave_scope(alias) @@ -227,7 +276,7 @@ impl Impl { let attributes = AttrListId::lower_ast_opt(ctxt, ast.attr_list()); let generic_params = GenericParamListId::lower_ast_opt(ctxt, ast.generic_params()); let where_clause = WhereClauseId::lower_ast_opt(ctxt, ast.where_clause()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); if let Some(item_list) = ast.item_list() { for impl_item in item_list { @@ -242,6 +291,7 @@ impl Impl { attributes, generic_params, where_clause, + ctxt.top_mod, origin, ); ctxt.leave_scope(impl_) @@ -263,7 +313,7 @@ impl Trait { let is_pub = ItemModifier::lower_ast(ast.modifier()).is_pub(); let generic_params = GenericParamListId::lower_ast_opt(ctxt, ast.generic_params()); let where_clause = WhereClauseId::lower_ast_opt(ctxt, ast.where_clause()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); if let Some(item_list) = ast.item_list() { for impl_item in item_list { @@ -279,6 +329,7 @@ impl Trait { is_pub, generic_params, where_clause, + ctxt.top_mod, origin, ); @@ -301,7 +352,7 @@ impl ImplTrait { let attributes = AttrListId::lower_ast_opt(ctxt, ast.attr_list()); let generic_params = GenericParamListId::lower_ast_opt(ctxt, ast.generic_params()); let where_clause = WhereClauseId::lower_ast_opt(ctxt, ast.where_clause()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); if let Some(item_list) = ast.item_list() { for impl_item in item_list { @@ -317,6 +368,7 @@ impl ImplTrait { attributes, generic_params, where_clause, + ctxt.top_mod, origin, ); ctxt.leave_scope(impl_trait) @@ -337,9 +389,9 @@ impl Const { .value() .map(|ast| Body::lower_ast(ctxt, id.clone(), ast)) .into(); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); - let const_ = Self::new(ctxt.db, id, name, body, origin); + let const_ = Self::new(ctxt.db, id, name, body, ctxt.top_mod, origin); ctxt.leave_scope(const_) } } @@ -355,8 +407,8 @@ impl Use { let tree = UseTreeId::lower_ast_partial(ctxt, ast.use_tree()); let id = TrackedItemId::Use(tree).join(parent_id); - let origin = HirOrigin::raw(ctxt.file, &ast); - let use_ = Self::new(ctxt.db, id, tree, origin); + let origin = HirOrigin::raw(&ast); + let use_ = Self::new(ctxt.db, id, tree, ctxt.top_mod, origin); ctxt.leave_scope(use_) } } @@ -379,10 +431,18 @@ impl ExternFunc { .into(); let ret_ty = ast.ret_ty().map(|ty| TypeId::lower_ast(ctxt, ty)); let modifier = ItemModifier::lower_ast(ast.modifier()); - let origin = HirOrigin::raw(ctxt.file, &ast); + let origin = HirOrigin::raw(&ast); let extern_func = Self::new( - ctxt.db, id, name, attributes, params, ret_ty, modifier, origin, + ctxt.db, + id, + name, + attributes, + params, + ret_ty, + modifier, + ctxt.top_mod, + origin, ); ctxt.leave_scope(extern_func) } @@ -451,50 +511,3 @@ impl EnumVariant { Self { name, ty } } } - -fn lower_module_items(ctxt: &mut FileLowerCtxt<'_>, id: TrackedItemId, items: ast::ItemList) { - for item in items { - match item.kind() { - ast::ItemKind::Mod(mod_) => { - Mod::lower_ast(ctxt, id.clone(), mod_); - } - ast::ItemKind::Fn(fn_) => { - Func::lower_ast(ctxt, id.clone(), fn_); - } - ast::ItemKind::Struct(struct_) => { - Struct::lower_ast(ctxt, id.clone(), struct_); - } - ast::ItemKind::Contract(contract) => { - Contract::lower_ast(ctxt, id.clone(), contract); - } - ast::ItemKind::Enum(enum_) => { - Enum::lower_ast(ctxt, id.clone(), enum_); - } - ast::ItemKind::TypeAlias(alias) => { - TypeAlias::lower_ast(ctxt, id.clone(), alias); - } - ast::ItemKind::Impl(impl_) => { - Impl::lower_ast(ctxt, id.clone(), impl_); - } - ast::ItemKind::Trait(trait_) => { - Trait::lower_ast(ctxt, id.clone(), trait_); - } - ast::ItemKind::ImplTrait(impl_trait) => { - ImplTrait::lower_ast(ctxt, id.clone(), impl_trait); - } - ast::ItemKind::Const(const_) => { - Const::lower_ast(ctxt, id.clone(), const_); - } - ast::ItemKind::Use(use_) => { - Use::lower_ast(ctxt, id.clone(), use_); - } - ast::ItemKind::Extern(extern_) => { - if let Some(extern_block) = extern_.extern_block() { - for fn_ in extern_block { - ExternFunc::lower_ast(ctxt, id.clone(), fn_); - } - } - } - } - } -} diff --git a/crates/hir/src/lower/mod.rs b/crates/hir/src/lower/mod.rs index e9f9209003..b8846d52c7 100644 --- a/crates/hir/src/lower/mod.rs +++ b/crates/hir/src/lower/mod.rs @@ -3,14 +3,22 @@ use std::collections::{BTreeMap, BTreeSet}; use common::InputFile; use num_bigint::BigUint; use num_traits::Num; -use parser::{ast, SyntaxToken}; +use parser::{ + ast::{self, prelude::*}, + GreenNode, SyntaxNode, SyntaxToken, +}; use crate::{ hir_def::{ IdentId, IntegerId, ItemKind, ItemTree, ItemTreeNode, LitKind, Partial, StringId, - TopLevelMod, + TopLevelMod, TrackedItemId, }, - HirDb, + HirDb, LowerHirDb, ParseDiagnostic, +}; + +use self::{ + item::lower_module_items, + parse::{parse_file_impl, ParseDiagnosticAccumulator}, }; pub(crate) mod parse; @@ -26,38 +34,87 @@ mod stmt; mod types; mod use_tree; -pub(super) fn lower_file( - db: &dyn HirDb, - file: InputFile, - top_mod_name: IdentId, - root_node: ast::Root, -) -> ItemTree { - let mut ctxt = FileLowerCtxt::new(db, file); - let top_mod = TopLevelMod::lower_ast(&mut ctxt, top_mod_name, root_node); - ctxt.build(top_mod) +/// Maps the given file to a top-level module. +/// This function just maps the file to a top-level module, and doesn't perform +/// any parsing or lowering. +/// To perform the actual lowering, use `module_item_tree` function. +pub fn map_file_to_mod(db: &dyn LowerHirDb, file: InputFile) -> TopLevelMod { + map_file_to_mod_impl(db.upcast(), file) +} + +/// Returns the item tree of the given top-level module. +pub fn module_item_tree(db: &dyn LowerHirDb, top_mod: TopLevelMod) -> &ItemTree { + module_item_tree_impl(db.upcast(), top_mod) +} + +/// Returns the root node of the given top-level module. +/// This function also returns the diagnostics produced by parsing the file. +pub fn parse_file_with_diag( + db: &dyn LowerHirDb, + top_mod: TopLevelMod, +) -> (GreenNode, Vec) { + ( + parse_file_impl(db.upcast(), top_mod), + parse_file_impl::accumulated::(db.upcast(), top_mod), + ) +} + +/// Returns the root node of the given top-level module. +/// If diagnostics are needed, use [`parse_file_with_diag`] instead. +pub fn parse_file(db: &dyn LowerHirDb, top_mod: TopLevelMod) -> GreenNode { + parse_file_impl(db.upcast(), top_mod) +} + +#[salsa::tracked] +pub(crate) fn map_file_to_mod_impl(db: &dyn HirDb, file: InputFile) -> TopLevelMod { + let path = file.path(db.upcast()); + let name = path.file_stem().unwrap(); + let mod_name = IdentId::new(db, name.to_string()); + let ingot = file.ingot(db.upcast()); + TopLevelMod::new(db, mod_name, ingot, file) +} + +#[salsa::tracked(return_ref)] +pub(crate) fn module_item_tree_impl(db: &dyn HirDb, top_mod: TopLevelMod) -> ItemTree { + let ast = top_mod_ast(db, top_mod); + let mut ctxt = FileLowerCtxt::new(db, top_mod); + + ctxt.enter_scope(); + let id = TrackedItemId::TopLevelMod(top_mod.name(db)); + if let Some(items) = ast.items() { + lower_module_items(&mut ctxt, id, items); + } + ctxt.leave_scope(top_mod); + + ctxt.build() +} + +pub(crate) fn top_mod_ast(db: &dyn HirDb, top_mod: TopLevelMod) -> ast::Root { + let node = SyntaxNode::new_root(parse_file_impl(db, top_mod)); + // This cast never fails even if the file content is empty. + ast::Root::cast(node).unwrap() } pub struct FileLowerCtxt<'db> { db: &'db dyn HirDb, - file: InputFile, scope_stack: Vec>, item_tree: BTreeMap, + top_mod: TopLevelMod, } impl<'db> FileLowerCtxt<'db> { - pub(super) fn new(db: &'db dyn HirDb, file: InputFile) -> Self { + pub(super) fn new(db: &'db dyn HirDb, top_mod: TopLevelMod) -> Self { Self { db, - file, scope_stack: vec![], item_tree: BTreeMap::new(), + top_mod, } } - pub(super) fn build(self, top_mod: TopLevelMod) -> ItemTree { + pub(super) fn build(self) -> ItemTree { ItemTree { - file: self.file, - top_mod, + top_mod: self.top_mod, item_tree: self.item_tree, } } diff --git a/crates/hir/src/lower/parse.rs b/crates/hir/src/lower/parse.rs index 777e167942..29f60cb7f5 100644 --- a/crates/hir/src/lower/parse.rs +++ b/crates/hir/src/lower/parse.rs @@ -4,10 +4,11 @@ use common::{ }; use parser::GreenNode; -use crate::{diagnostics::DiagnosticVoucher, span::db::SpannedHirDb, HirDb}; +use crate::{diagnostics::DiagnosticVoucher, hir_def::TopLevelMod, HirDb, SpannedHirDb}; #[salsa::tracked] -pub(crate) fn parse_file(db: &dyn HirDb, file: InputFile) -> GreenNode { +pub(crate) fn parse_file_impl(db: &dyn HirDb, top_mod: TopLevelMod) -> GreenNode { + let file = top_mod.file(db); let text = file.text(db.upcast()); let (node, parse_errors) = parser::parse_source_file(text); diff --git a/crates/hir/src/lower/pat.rs b/crates/hir/src/lower/pat.rs index 5ee28a6b33..6a17669d1c 100644 --- a/crates/hir/src/lower/pat.rs +++ b/crates/hir/src/lower/pat.rs @@ -2,7 +2,7 @@ use parser::ast; use crate::{ hir_def::{pat::*, IdentId, LitKind, PathId}, - span::LocalOrigin, + span::HirOrigin, }; use super::body::BodyCtxt; @@ -63,7 +63,7 @@ impl Pat { } }; - ctxt.push_pat(pat, LocalOrigin::raw(&ast)) + ctxt.push_pat(pat, HirOrigin::raw(&ast)) } pub(super) fn lower_ast_opt(ctxt: &mut BodyCtxt<'_, '_>, ast: Option) -> PatId { diff --git a/crates/hir/src/lower/stmt.rs b/crates/hir/src/lower/stmt.rs index 26321f4e86..6c90971754 100644 --- a/crates/hir/src/lower/stmt.rs +++ b/crates/hir/src/lower/stmt.rs @@ -2,7 +2,7 @@ use parser::ast::{self, prelude::*}; use crate::{ hir_def::{stmt::*, ArithBinOp, Expr, Pat, PathId, TypeId}, - span::{AugAssignDesugared, LocalOrigin}, + span::{AugAssignDesugared, HirOrigin}, }; use super::body::BodyCtxt; @@ -16,7 +16,7 @@ impl Stmt { .type_annotation() .map(|ty| TypeId::lower_ast(ctxt.f_ctxt, ty)); let init = let_.initializer().map(|init| Expr::lower_ast(ctxt, init)); - (Stmt::Let(pat, ty, init), LocalOrigin::raw(&ast)) + (Stmt::Let(pat, ty, init), HirOrigin::raw(&ast)) } ast::StmtKind::Assign(assign) => { let lhs = assign @@ -28,7 +28,7 @@ impl Stmt { .expr() .map(|expr| Expr::lower_ast(ctxt, expr)) .unwrap_or_else(|| ctxt.push_missing_expr()); - (Stmt::Assign(lhs, rhs), LocalOrigin::raw(&ast)) + (Stmt::Assign(lhs, rhs), HirOrigin::raw(&ast)) } ast::StmtKind::AugAssign(aug_assign) => desugar_aug_assign(ctxt, &aug_assign), @@ -42,7 +42,7 @@ impl Stmt { .and_then(|body| ast::Expr::cast(body.syntax().clone())), ); - (Stmt::For(bind, iter, body), LocalOrigin::raw(&ast)) + (Stmt::For(bind, iter, body), HirOrigin::raw(&ast)) } ast::StmtKind::While(while_) => { @@ -54,23 +54,23 @@ impl Stmt { .and_then(|body| ast::Expr::cast(body.syntax().clone())), ); - (Stmt::While(cond, body), LocalOrigin::raw(&ast)) + (Stmt::While(cond, body), HirOrigin::raw(&ast)) } - ast::StmtKind::Continue(_) => (Stmt::Continue, LocalOrigin::raw(&ast)), + ast::StmtKind::Continue(_) => (Stmt::Continue, HirOrigin::raw(&ast)), - ast::StmtKind::Break(_) => (Stmt::Break, LocalOrigin::raw(&ast)), + ast::StmtKind::Break(_) => (Stmt::Break, HirOrigin::raw(&ast)), ast::StmtKind::Return(ret) => { let expr = ret .has_value() .then(|| Expr::push_to_body_opt(ctxt, ret.expr())); - (Stmt::Return(expr), LocalOrigin::raw(&ast)) + (Stmt::Return(expr), HirOrigin::raw(&ast)) } ast::StmtKind::Expr(expr) => { let expr = Expr::push_to_body_opt(ctxt, expr.expr()); - (Stmt::Expr(expr), LocalOrigin::raw(&ast)) + (Stmt::Expr(expr), HirOrigin::raw(&ast)) } }; @@ -81,7 +81,7 @@ impl Stmt { fn desugar_aug_assign( ctxt: &mut BodyCtxt<'_, '_>, ast: &ast::AugAssignStmt, -) -> (Stmt, LocalOrigin) { +) -> (Stmt, HirOrigin) { let lhs_ident = ast.ident(); let path = lhs_ident .clone() @@ -91,7 +91,7 @@ fn desugar_aug_assign( let lhs_pat = if let Some(path) = path { ctxt.push_pat( Pat::Path(Some(path).into()), - LocalOrigin::desugared(lhs_origin.clone()), + HirOrigin::desugared(lhs_origin.clone()), ) } else { ctxt.push_missing_pat() @@ -100,7 +100,7 @@ fn desugar_aug_assign( let binop_lhs = if let Some(path) = path { ctxt.push_expr( Expr::Path(Some(path).into()), - LocalOrigin::desugared(lhs_origin), + HirOrigin::desugared(lhs_origin), ) } else { ctxt.push_missing_expr() @@ -114,11 +114,11 @@ fn desugar_aug_assign( let binop = ast.op().map(|op| ArithBinOp::lower_ast(op).into()).into(); let expr = ctxt.push_expr( Expr::Bin(binop_lhs, binop_rhs, binop), - LocalOrigin::desugared(AugAssignDesugared::stmt(ast)), + HirOrigin::desugared(AugAssignDesugared::stmt(ast)), ); ( Stmt::Assign(lhs_pat, expr), - LocalOrigin::desugared(AugAssignDesugared::stmt(ast)), + HirOrigin::desugared(AugAssignDesugared::stmt(ast)), ) } diff --git a/crates/hir/src/span/db.rs b/crates/hir/src/span/db.rs deleted file mode 100644 index d51ef4f5fc..0000000000 --- a/crates/hir/src/span/db.rs +++ /dev/null @@ -1,84 +0,0 @@ -use common::Upcast; -use parser::ast; - -use crate::{ - hir_def::{ - Body, Const, Contract, Enum, ExternFunc, Func, Impl, ImplTrait, Mod, Struct, TopLevelMod, - Trait, TypeAlias, Use, - }, - HirDb, -}; - -use super::HirOrigin; - -/// `SpannedHirDb` is a feature gate for extracting span-dependent information -/// from HIR Items. All code that requires [`SpannedHirDb`] is considered to -/// invalidate the cache in salsa when a revision is updated. -/// Therefore, implementations relying on `SpannedHirDb` are prohibited in all -/// Analysis phases. -/// -/// SpanDb is mainly used to inject information about -/// [HirOrigin] to generate -/// [CompleteDiagnostic](common::diagnostics::CompleteDiagnostic) from -/// [DiagnosticVoucher](crate::diagnostics::DiagnosticVoucher). -pub trait SpannedHirDb: HirDb + Upcast { - fn toplevel_ast(&self, item: TopLevelMod) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn mod_ast(&self, item: Mod) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn func_ast(&self, item: Func) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn extern_func_ast(&self, item: ExternFunc) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn struct_ast(&self, item: Struct) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn contract_ast(&self, item: Contract) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn enum_ast(&self, item: Enum) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn type_alias_ast(&self, item: TypeAlias) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn impl_ast(&self, item: Impl) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn trait_ast(&self, item: Trait) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn impl_trait_ast(&self, item: ImplTrait) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn const_ast(&self, item: Const) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn use_ast(&self, item: Use) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn body_ast(&self, item: Body) -> &HirOrigin { - item.origin(self.upcast()) - } - - fn body_source_map(&self, item: Body) -> &crate::hir_def::BodySourceMap { - item.source_map(self.upcast()) - } -} diff --git a/crates/hir/src/span/expr.rs b/crates/hir/src/span/expr.rs index 32d2057b67..32ca52910c 100644 --- a/crates/hir/src/span/expr.rs +++ b/crates/hir/src/span/expr.rs @@ -3,13 +3,12 @@ use parser::{ast, SyntaxNode}; use crate::{ hir_def::{Body, ExprId}, - parse_file, span::{params::LazyGenericArgListSpan, path::LazyPathSpan, LazySpanAtom}, + SpannedHirDb, }; use super::{ - db::SpannedHirDb, - define_lazy_span_node, + body_ast, body_source_map, define_lazy_span_node, transition::{ChainRoot, SpanTransitionChain}, }; @@ -171,15 +170,13 @@ struct ExprRoot { impl ChainRoot for ExprRoot { fn root(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode) { - let body_ast = db.body_ast(self.body); - let file = body_ast.file; - let source_map = db.body_source_map(self.body); - let pat_source = source_map.expr_map.node_to_source(self.expr); - let ptr = pat_source + let source_map = body_source_map(db, self.body); + let expr_source = source_map.expr_map.node_to_source(self.expr); + let ptr = expr_source .syntax_ptr() - .unwrap_or_else(|| body_ast.syntax_ptr().unwrap()); + .unwrap_or_else(|| body_ast(db, self.body).syntax_ptr().unwrap()); - let root_node = SyntaxNode::new_root(parse_file(db.upcast(), file)); + let (file, root_node) = self.body.top_mod(db.upcast()).root(db); let node = ptr.to_node(&root_node); (file, node) } diff --git a/crates/hir/src/span/mod.rs b/crates/hir/src/span/mod.rs index cd670b3dee..43fe74112d 100644 --- a/crates/hir/src/span/mod.rs +++ b/crates/hir/src/span/mod.rs @@ -3,12 +3,18 @@ use parser::{ TextRange, }; -use common::{diagnostics::Span, InputFile}; - -use self::db::SpannedHirDb; +use common::diagnostics::Span; + +use crate::{ + hir_def::{ + Body, Const, Contract, Enum, ExternFunc, Func, Impl, ImplTrait, Mod, Struct, TopLevelMod, + Trait, TypeAlias, Use, + }, + lower::top_mod_ast, + SpannedHirDb, +}; pub mod attr; -pub mod db; pub mod expr; pub mod item; pub mod params; @@ -20,44 +26,80 @@ pub mod use_tree; mod transition; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct HirOrigin -where - T: AstNode, -{ - pub file: InputFile, - pub kind: LocalOrigin, +/// The trait provides a way to extract [`Span`](common::diagnostics::Span) from +/// types which don't have a span information directly, but can be resolved into +/// a span lazily. +pub trait LazySpan { + fn resolve(&self, db: &dyn crate::SpannedHirDb) -> Span; } -impl HirOrigin -where - T: AstNode, -{ - fn syntax_ptr(&self) -> Option { - self.kind.syntax_ptr() - } +pub fn toplevel_ast(db: &dyn SpannedHirDb, item: TopLevelMod) -> HirOrigin { + HirOrigin::raw(&top_mod_ast(db.upcast(), item)) } -impl HirOrigin -where - T: AstNode, -{ - pub(crate) fn new(file: InputFile, origin: LocalOrigin) -> Self { - HirOrigin { file, kind: origin } - } +pub fn mod_ast(db: &dyn SpannedHirDb, item: Mod) -> &HirOrigin { + item.origin(db.upcast()) +} - pub(crate) fn raw(file: InputFile, ast: &T) -> Self { - Self::new(file, LocalOrigin::raw(ast)) - } +pub fn func_ast(db: &dyn SpannedHirDb, item: Func) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn extern_func_ast(db: &dyn SpannedHirDb, item: ExternFunc) -> &HirOrigin { + item.origin(db.upcast()) } -/// This enum represents the origin of the HIR node is a file. +pub fn struct_ast(db: &dyn SpannedHirDb, item: Struct) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn contract_ast(db: &dyn SpannedHirDb, item: Contract) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn enum_ast(db: &dyn SpannedHirDb, item: Enum) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn type_alias_ast(db: &dyn SpannedHirDb, item: TypeAlias) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn impl_ast(db: &dyn SpannedHirDb, item: Impl) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn trait_ast(db: &dyn SpannedHirDb, item: Trait) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn impl_trait_ast(db: &dyn SpannedHirDb, item: ImplTrait) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn const_ast(db: &dyn SpannedHirDb, item: Const) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn use_ast(db: &dyn SpannedHirDb, item: Use) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn body_ast(db: &dyn SpannedHirDb, item: Body) -> &HirOrigin { + item.origin(db.upcast()) +} + +pub fn body_source_map(db: &dyn SpannedHirDb, item: Body) -> &crate::hir_def::BodySourceMap { + item.source_map(db.upcast()) +} + +/// This enum represents the origin of the HIR node in a file. /// The origin has three possible kinds. /// 1. `Raw` is used for nodes that are created by the parser and not /// 2. `Expanded` is used for nodes that are created by the compiler and not /// 3. `Desugared` is used for nodes that are created by the compiler and not #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum LocalOrigin +pub enum HirOrigin where T: AstNode, { @@ -78,7 +120,7 @@ where None, } -impl LocalOrigin +impl HirOrigin where T: AstNode, { @@ -88,8 +130,8 @@ where fn syntax_ptr(&self) -> Option { match self { - LocalOrigin::Raw(ptr) => Some(ptr.syntax_node_ptr()), - LocalOrigin::Expanded(ptr) => Some(ptr.clone()), + HirOrigin::Raw(ptr) => Some(ptr.syntax_node_ptr()), + HirOrigin::Expanded(ptr) => Some(ptr.clone()), _ => None, } } @@ -99,7 +141,7 @@ where } } -impl Default for LocalOrigin +impl Default for HirOrigin where T: AstNode, { @@ -134,12 +176,6 @@ impl AugAssignDesugared { } } -/// The trait provides a way to extract [`Span`](common::diagnostics::Span) from -/// types which don't have a span information directly, but can be resolved into -/// a span lazily. -pub trait LazySpan { - fn resolve(&self, db: &dyn SpannedHirDb) -> Span; -} - use transition::define_lazy_span_node; + define_lazy_span_node!(LazySpanAtom); diff --git a/crates/hir/src/span/pat.rs b/crates/hir/src/span/pat.rs index 67d07d17bf..48e54bcbf5 100644 --- a/crates/hir/src/span/pat.rs +++ b/crates/hir/src/span/pat.rs @@ -3,13 +3,12 @@ use parser::{ast, SyntaxNode}; use crate::{ hir_def::{Body, PatId}, - parse_file, span::path::LazyPathSpan, + SpannedHirDb, }; use super::{ - db::SpannedHirDb, - define_lazy_span_node, + body_ast, body_source_map, define_lazy_span_node, transition::{ChainRoot, SpanTransitionChain}, }; @@ -82,15 +81,13 @@ struct PatRoot { impl ChainRoot for PatRoot { fn root(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode) { - let body_ast = db.body_ast(self.body); - let file = body_ast.file; - let source_map = db.body_source_map(self.body); + let source_map = body_source_map(db, self.body); let pat_source = source_map.pat_map.node_to_source(self.pat); let ptr = pat_source .syntax_ptr() - .unwrap_or_else(|| body_ast.syntax_ptr().unwrap()); + .unwrap_or_else(|| body_ast(db, self.body).syntax_ptr().unwrap()); - let root_node = SyntaxNode::new_root(parse_file(db.upcast(), file)); + let (file, root_node) = self.body.top_mod(db.upcast()).root(db); let node = ptr.to_node(&root_node); (file, node) } diff --git a/crates/hir/src/span/stmt.rs b/crates/hir/src/span/stmt.rs index bbb717568a..b435fb9809 100644 --- a/crates/hir/src/span/stmt.rs +++ b/crates/hir/src/span/stmt.rs @@ -3,13 +3,12 @@ use parser::{ast, SyntaxNode}; use crate::{ hir_def::{Body, StmtId}, - parse_file, span::types::LazyTypeSpan, + SpannedHirDb, }; use super::{ - db::SpannedHirDb, - define_lazy_span_node, + body_ast, body_source_map, define_lazy_span_node, transition::{ChainRoot, SpanTransitionChain}, }; @@ -41,15 +40,13 @@ struct StmtRoot { impl ChainRoot for StmtRoot { fn root(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode) { - let body_ast = db.body_ast(self.body); - let file = body_ast.file; - let source_map = db.body_source_map(self.body); - let pat_source = source_map.stmt_map.node_to_source(self.stmt); - let ptr = pat_source + let source_map = body_source_map(db, self.body); + let stmt_source = source_map.stmt_map.node_to_source(self.stmt); + let ptr = stmt_source .syntax_ptr() - .unwrap_or_else(|| body_ast.syntax_ptr().unwrap()); + .unwrap_or_else(|| body_ast(db, self.body).syntax_ptr().unwrap()); - let root_node = SyntaxNode::new_root(parse_file(db.upcast(), file)); + let (file, root_node) = self.body.top_mod(db.upcast()).root(db); let node = ptr.to_node(&root_node); (file, node) } diff --git a/crates/hir/src/span/transition.rs b/crates/hir/src/span/transition.rs index e4153abd41..0a1a644218 100644 --- a/crates/hir/src/span/transition.rs +++ b/crates/hir/src/span/transition.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use common::{diagnostics::Span, InputFile}; -use parser::{syntax_node::NodeOrToken, SyntaxNode}; +use parser::{ast::prelude::*, syntax_node::NodeOrToken, SyntaxNode}; use smallvec::SmallVec; use crate::{ @@ -9,10 +9,13 @@ use crate::{ Body, Const, Contract, Enum, ExternFunc, Func, Impl, ImplTrait, Mod, Struct, TopLevelMod, Trait, TypeAlias, Use, }, - parse_file, + lower::top_mod_ast, }; -use super::{db::SpannedHirDb, LazySpan}; +use super::{ + body_ast, const_ast, contract_ast, enum_ast, extern_func_ast, func_ast, impl_ast, + impl_trait_ast, mod_ast, struct_ast, trait_ast, type_alias_ast, use_ast, LazySpan, +}; type TransitionFn = Arc Option>; @@ -39,7 +42,7 @@ impl SpanTransitionChain { } impl LazySpan for SpanTransitionChain { - fn resolve(&self, db: &dyn SpannedHirDb) -> Span { + fn resolve(&self, db: &dyn crate::SpannedHirDb) -> Span { let (file, mut node) = self.root.root(db); for transition in &self.chain { @@ -59,26 +62,33 @@ impl LazySpan for SpanTransitionChain { } pub(super) trait ChainRoot { - fn root(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode); + fn root(&self, db: &dyn crate::SpannedHirDb) -> (InputFile, SyntaxNode); +} + +impl ChainRoot for TopLevelMod { + fn root(&self, db: &dyn crate::SpannedHirDb) -> (InputFile, SyntaxNode) { + let file = self.file(db.upcast()); + let ast = top_mod_ast(db.upcast(), *self); + (file, ast.syntax().clone()) + } } macro_rules! impl_chain_root { ($(($ty:ty, $fn:ident),)*) => { $( impl ChainRoot for $ty { - fn root(&self, db: &dyn SpannedHirDb) -> (InputFile, SyntaxNode) { - let ast = db.$fn(*self); - let file = ast.file; + fn root(&self, db: &dyn crate::SpannedHirDb) -> (InputFile, SyntaxNode) { + let ast = $fn(db, *self); + let (file, root) = self.top_mod(db.upcast()).root(db); let ptr = ast.syntax_ptr().unwrap(); - let root_node = SyntaxNode::new_root(parse_file(db.upcast(), file)); - let node = ptr.to_node(&root_node); + let node = ptr.to_node(&root); (file, node) } })* }; } + impl_chain_root! { - (TopLevelMod, toplevel_ast), (Mod, mod_ast), (Func, func_ast), (ExternFunc, extern_func_ast), @@ -160,7 +170,7 @@ macro_rules! define_lazy_span_node { impl crate::span::LazySpan for $name { - fn resolve(&self, db: &dyn crate::span::SpannedHirDb) -> common::diagnostics::Span { + fn resolve(&self, db: &dyn crate::SpannedHirDb) -> common::diagnostics::Span { self.0.resolve(db) } }