From bca8029a6ed7bed26bdea284738f64f10c063ba8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 17 Apr 2023 17:31:39 +0200 Subject: [PATCH] Move Expander and LowerCtx into separate modules --- crates/hir-def/src/attr.rs | 86 +++-- crates/hir-def/src/body.rs | 347 +++--------------- crates/hir-def/src/body/lower.rs | 32 +- crates/hir-def/src/data.rs | 33 +- crates/hir-def/src/data/adt.rs | 3 +- crates/hir-def/src/db.rs | 6 + crates/hir-def/src/expander.rs | 211 +++++++++++ crates/hir-def/src/generics.rs | 42 ++- crates/hir-def/src/hir/type_ref.rs | 2 +- crates/hir-def/src/import_map.rs | 2 +- crates/hir-def/src/item_tree/lower.rs | 4 +- crates/hir-def/src/lib.rs | 26 +- crates/hir-def/src/lower.rs | 46 +++ crates/hir-def/src/nameres/collector.rs | 6 +- .../hir-def/src/nameres/tests/incremental.rs | 2 +- crates/hir-def/src/path.rs | 2 +- crates/hir-def/src/path/lower.rs | 6 +- crates/hir-ty/src/display.rs | 6 +- crates/hir-ty/src/interner.rs | 2 +- crates/hir-ty/src/lower.rs | 6 +- crates/hir/src/semantics.rs | 8 +- crates/hir/src/source_analyzer.rs | 6 +- crates/ide-db/src/line_index.rs | 3 + crates/ide-db/src/symbol_index.rs | 1 + crates/limit/src/lib.rs | 1 + 25 files changed, 487 insertions(+), 402 deletions(-) create mode 100644 crates/hir-def/src/expander.rs create mode 100644 crates/hir-def/src/lower.rs diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index a00337ae9c..1ebbe160b0 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -28,8 +28,8 @@ use crate::{ lang_item::LangItem, nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource}, - AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId, - VariantId, + AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId, + LocalFieldId, Lookup, MacroId, VariantId, }; /// Holds documentation @@ -421,23 +421,24 @@ impl AttrsWithOwner { AttrDefId::EnumVariantId(it) => { return db.variants_attrs(it.parent)[it.local_id].clone(); } + // FIXME: DRY this up AttrDefId::AdtId(it) => match it { - AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AdtId::StructId(it) => attrs_from_item_tree_loc(db, it), + AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it), + AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it), }, - AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it), + AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it), AttrDefId::MacroId(it) => match it { - MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db), - MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db), - MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db), + MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id), + MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id), + MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id), }, - AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it), + AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it), + AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it), + AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it), + AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it), AttrDefId::GenericParamId(it) => match it { GenericParamId::ConstParamId(it) => { let src = it.parent().child_source(db); @@ -458,7 +459,7 @@ impl AttrsWithOwner { RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id])) } }, - AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it), }; let attrs = raw_attrs.filter(db.upcast(), def.krate(db)); @@ -506,28 +507,28 @@ impl AttrsWithOwner { InFile::new(file_id, owner) } AttrDefId::AdtId(adt) => match adt { - AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AdtId::StructId(id) => any_has_attrs(db, id), + AdtId::UnionId(id) => any_has_attrs(db, id), + AdtId::EnumId(id) => any_has_attrs(db, id), }, - AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::FunctionId(id) => any_has_attrs(db, id), AttrDefId::EnumVariantId(id) => { let map = db.variants_attrs_source_map(id.parent); let file_id = id.parent.lookup(db).id.file_id(); let root = db.parse_or_expand(file_id); InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root))) } - AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::StaticId(id) => any_has_attrs(db, id), + AttrDefId::ConstId(id) => any_has_attrs(db, id), + AttrDefId::TraitId(id) => any_has_attrs(db, id), + AttrDefId::TraitAliasId(id) => any_has_attrs(db, id), + AttrDefId::TypeAliasId(id) => any_has_attrs(db, id), AttrDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + MacroId::Macro2Id(id) => any_has_attrs(db, id), + MacroId::MacroRulesId(id) => any_has_attrs(db, id), + MacroId::ProcMacroId(id) => any_has_attrs(db, id), }, - AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::ImplId(id) => any_has_attrs(db, id), AttrDefId::GenericParamId(id) => match id { GenericParamId::ConstParamId(id) => id .parent() @@ -542,7 +543,7 @@ impl AttrsWithOwner { .child_source(db) .map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())), }, - AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::ExternBlockId(id) => any_has_attrs(db, id), }; AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs)) @@ -769,12 +770,35 @@ impl<'attr> AttrQuery<'attr> { } } -fn attrs_from_item_tree(id: ItemTreeId, db: &dyn DefDatabase) -> RawAttrs { +fn any_has_attrs( + db: &dyn DefDatabase, + id: impl Lookup>, +) -> InFile { + id.lookup(db).source(db).map(ast::AnyHasAttrs::new) +} + +fn attrs_from_item_tree(db: &dyn DefDatabase, id: ItemTreeId) -> RawAttrs { let tree = id.item_tree(db); let mod_item = N::id_to_mod_item(id.value); tree.raw_attrs(mod_item.into()).clone() } +fn attrs_from_item_tree_loc( + db: &dyn DefDatabase, + lookup: impl Lookup>, +) -> RawAttrs { + let id = lookup.lookup(db).id; + attrs_from_item_tree(db, id) +} + +fn attrs_from_item_tree_assoc( + db: &dyn DefDatabase, + lookup: impl Lookup>, +) -> RawAttrs { + let id = lookup.lookup(db).id; + attrs_from_item_tree(db, id) +} + pub(crate) fn variants_attrs_source_map( db: &dyn DefDatabase, def: EnumId, diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index cf2227b379..0c0e8ac820 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -10,284 +10,25 @@ use std::{ops::Index, sync::Arc}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use drop_bomb::DropBomb; use either::Either; -use hir_expand::{ - ast_id_map::AstIdMap, attrs::RawAttrs, hygiene::Hygiene, name::Name, AstId, ExpandError, - ExpandResult, HirFileId, InFile, MacroCallId, -}; +use hir_expand::{name::Name, HirFileId, InFile}; use la_arena::{Arena, ArenaMap}; -use limit::Limit; -use once_cell::unsync::OnceCell; use profile::Count; use rustc_hash::FxHashMap; -use syntax::{ast, AstPtr, Parse, SyntaxNode, SyntaxNodePtr}; +use syntax::{ast, AstPtr, SyntaxNodePtr}; use crate::{ - attr::Attrs, db::DefDatabase, + expander::Expander, hir::{ dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat, }, - item_scope::BuiltinShadowMode, - macro_id_to_def_id, nameres::DefMap, path::{ModPath, Path}, src::{HasChildSource, HasSource}, - AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId, - UnresolvedMacro, + BlockId, DefWithBodyId, HasModule, Lookup, }; -pub struct LowerCtx<'a> { - pub db: &'a dyn DefDatabase, - hygiene: Hygiene, - ast_id_map: Option<(HirFileId, OnceCell>)>, -} - -impl<'a> LowerCtx<'a> { - pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self { - LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) } - } - - pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self { - LowerCtx { - db, - hygiene: Hygiene::new(db.upcast(), file_id), - ast_id_map: Some((file_id, OnceCell::new())), - } - } - - pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self { - LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None } - } - - pub(crate) fn hygiene(&self) -> &Hygiene { - &self.hygiene - } - - pub(crate) fn lower_path(&self, ast: ast::Path) -> Option { - Path::from_src(ast, self) - } - - pub(crate) fn ast_id(&self, item: &N) -> Option> { - let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?; - let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id)); - Some(InFile::new(file_id, ast_id_map.ast_id(item))) - } -} - -/// A subset of Expander that only deals with cfg attributes. We only need it to -/// avoid cyclic queries in crate def map during enum processing. -#[derive(Debug)] -pub(crate) struct CfgExpander { - cfg_options: CfgOptions, - hygiene: Hygiene, - krate: CrateId, -} - -#[derive(Debug)] -pub struct Expander { - cfg_expander: CfgExpander, - def_map: Arc, - current_file_id: HirFileId, - module: LocalModuleId, - /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. - recursion_depth: usize, -} - -impl CfgExpander { - pub(crate) fn new( - db: &dyn DefDatabase, - current_file_id: HirFileId, - krate: CrateId, - ) -> CfgExpander { - let hygiene = Hygiene::new(db.upcast(), current_file_id); - let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - CfgExpander { cfg_options, hygiene, krate } - } - - pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) - } - - pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool { - let attrs = self.parse_attrs(db, owner); - attrs.is_cfg_enabled(&self.cfg_options) - } - - pub(crate) fn hygiene(&self) -> &Hygiene { - &self.hygiene - } -} - -impl Expander { - pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { - let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); - let def_map = module.def_map(db); - Expander { - cfg_expander, - def_map, - current_file_id, - module: module.local_id, - recursion_depth: 0, - } - } - - pub fn enter_expand( - &mut self, - db: &dyn DefDatabase, - macro_call: ast::MacroCall, - ) -> Result)>>, UnresolvedMacro> { - // FIXME: within_limit should support this, instead of us having to extract the error - let mut unresolved_macro_err = None; - - let result = self.within_limit(db, |this| { - let macro_call = InFile::new(this.current_file_id, ¯o_call); - - let resolver = - |path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it)); - - match macro_call.as_call_id_with_errors(db, this.def_map.krate(), resolver) { - Ok(call_id) => call_id, - Err(resolve_err) => { - unresolved_macro_err = Some(resolve_err); - ExpandResult { value: None, err: None } - } - } - }); - - if let Some(err) = unresolved_macro_err { - Err(err) - } else { - Ok(result) - } - } - - pub fn enter_expand_id( - &mut self, - db: &dyn DefDatabase, - call_id: MacroCallId, - ) -> ExpandResult)>> { - self.within_limit(db, |_this| ExpandResult::ok(Some(call_id))) - } - - fn enter_expand_inner( - db: &dyn DefDatabase, - call_id: MacroCallId, - error: Option, - ) -> ExpandResult>>> { - let file_id = call_id.as_file(); - let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id); - - ExpandResult { value: Some(InFile::new(file_id, value)), err: error.or(err) } - } - - pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); - self.current_file_id = mark.file_id; - if self.recursion_depth == usize::MAX { - // Recursion limit has been reached somewhere in the macro expansion tree. Reset the - // depth only when we get out of the tree. - if !self.current_file_id.is_macro() { - self.recursion_depth = 0; - } - } else { - self.recursion_depth -= 1; - } - mark.bomb.defuse(); - } - - pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> { - LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id) - } - - pub(crate) fn to_source(&self, value: T) -> InFile { - InFile { file_id: self.current_file_id, value } - } - - pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - self.cfg_expander.parse_attrs(db, owner) - } - - pub(crate) fn cfg_options(&self) -> &CfgOptions { - &self.cfg_expander.cfg_options - } - - pub fn current_file_id(&self) -> HirFileId { - self.current_file_id - } - - fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option { - let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene); - Path::from_src(path, &ctx) - } - - fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { - self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros() - } - - fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit { - let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _; - - #[cfg(not(test))] - return Limit::new(limit); - - // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug - #[cfg(test)] - return Limit::new(std::cmp::min(32, limit)); - } - - fn within_limit( - &mut self, - db: &dyn DefDatabase, - op: F, - ) -> ExpandResult)>> - where - F: FnOnce(&mut Self) -> ExpandResult>, - { - if self.recursion_depth == usize::MAX { - // Recursion limit has been reached somewhere in the macro expansion tree. We should - // stop expanding other macro calls in this tree, or else this may result in - // exponential number of macro expansions, leading to a hang. - // - // The overflow error should have been reported when it occurred (see the next branch), - // so don't return overflow error here to avoid diagnostics duplication. - cov_mark::hit!(overflow_but_not_me); - return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned); - } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() { - self.recursion_depth = usize::MAX; - cov_mark::hit!(your_stack_belongs_to_me); - return ExpandResult::only_err(ExpandError::Other( - "reached recursion limit during macro expansion".into(), - )); - } - - let ExpandResult { value, err } = op(self); - let Some(call_id) = value else { - return ExpandResult { value: None, err }; - }; - - Self::enter_expand_inner(db, call_id, err).map(|value| { - value.and_then(|InFile { file_id, value }| { - let parse = value.cast::()?; - - self.recursion_depth += 1; - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); - let old_file_id = std::mem::replace(&mut self.current_file_id, file_id); - let mark = - Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") }; - Some((mark, parse)) - }) - }) - } -} - -#[derive(Debug)] -pub struct Mark { - file_id: HirFileId, - bomb: DropBomb, -} - /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] pub struct Body { @@ -376,47 +117,49 @@ impl Body { let _p = profile::span("body_with_source_map_query"); let mut params = None; - let (file_id, module, body, is_async_fn) = match def { - DefWithBodyId::FunctionId(f) => { - let data = db.function_data(f); - let f = f.lookup(db); - let src = f.source(db); - params = src.value.param_list().map(|param_list| { - let item_tree = f.id.item_tree(db); - let func = &item_tree[f.id.value]; - let krate = f.container.module(db).krate; - let crate_graph = db.crate_graph(); + let (file_id, module, body, is_async_fn) = { + match def { + DefWithBodyId::FunctionId(f) => { + let data = db.function_data(f); + let f = f.lookup(db); + let src = f.source(db); + params = src.value.param_list().map(|param_list| { + let item_tree = f.id.item_tree(db); + let func = &item_tree[f.id.value]; + let krate = f.container.module(db).krate; + let crate_graph = db.crate_graph(); + ( + param_list, + func.params.clone().map(move |param| { + item_tree + .attrs(db, krate, param.into()) + .is_cfg_enabled(&crate_graph[krate].cfg_options) + }), + ) + }); ( - param_list, - func.params.clone().map(move |param| { - item_tree - .attrs(db, krate, param.into()) - .is_cfg_enabled(&crate_graph[krate].cfg_options) - }), + src.file_id, + f.module(db), + src.value.body().map(ast::Expr::from), + data.has_async_kw(), ) - }); - ( - src.file_id, - f.module(db), - src.value.body().map(ast::Expr::from), - data.has_async_kw(), - ) - } - DefWithBodyId::ConstId(c) => { - let c = c.lookup(db); - let src = c.source(db); - (src.file_id, c.module(db), src.value.body(), false) - } - DefWithBodyId::StaticId(s) => { - let s = s.lookup(db); - let src = s.source(db); - (src.file_id, s.module(db), src.value.body(), false) - } - DefWithBodyId::VariantId(v) => { - let e = v.parent.lookup(db); - let src = v.parent.child_source(db); - let variant = &src.value[v.local_id]; - (src.file_id, e.container, variant.expr(), false) + } + DefWithBodyId::ConstId(c) => { + let c = c.lookup(db); + let src = c.source(db); + (src.file_id, c.module(db), src.value.body(), false) + } + DefWithBodyId::StaticId(s) => { + let s = s.lookup(db); + let src = s.source(db); + (src.file_id, s.module(db), src.value.body(), false) + } + DefWithBodyId::VariantId(v) => { + let e = v.parent.lookup(db); + let src = v.parent.child_source(db); + let variant = &src.value[v.local_id]; + (src.file_id, e.container, variant.expr(), false) + } } }; let expander = Expander::new(db, file_id, module); diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index db619b97db..27dfe766d3 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -24,9 +24,10 @@ use syntax::{ }; use crate::{ - body::{Body, BodyDiagnostic, BodySourceMap, Expander, ExprPtr, LabelPtr, LowerCtx, PatPtr}, + body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr}, data::adt::StructKind, db::DefDatabase, + expander::Expander, hir::{ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr, ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, @@ -34,6 +35,8 @@ use crate::{ }, item_scope::BuiltinShadowMode, lang_item::LangItem, + lower::LowerCtx, + nameres::DefMap, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro, @@ -50,6 +53,7 @@ pub(super) fn lower( ExprCollector { db, krate, + def_map: db.crate_def_map(krate), source_map: BodySourceMap::default(), ast_id_map: db.ast_id_map(expander.current_file_id), body: Body { @@ -75,6 +79,7 @@ pub(super) fn lower( struct ExprCollector<'a> { db: &'a dyn DefDatabase, expander: Expander, + def_map: Arc, ast_id_map: Arc, krate: CrateId, body: Body, @@ -777,7 +782,13 @@ impl ExprCollector<'_> { let outer_file = self.expander.current_file_id; let macro_call_ptr = self.expander.to_source(AstPtr::new(&mcall)); - let res = self.expander.enter_expand(self.db, mcall); + let module = self.expander.module.local_id; + let res = self.expander.enter_expand(self.db, mcall, |path| { + self.def_map + .resolve_path(self.db, module, &path, crate::item_scope::BuiltinShadowMode::Other) + .0 + .take_macros() + }); let res = match res { Ok(res) => res, @@ -944,10 +955,7 @@ impl ExprCollector<'_> { let block_id = if block_has_items { let file_local_id = self.ast_id_map.ast_id(&block); let ast_id = AstId::new(self.expander.current_file_id, file_local_id); - Some(self.db.intern_block(BlockLoc { - ast_id, - module: self.expander.def_map.module_id(self.expander.module), - })) + Some(self.db.intern_block(BlockLoc { ast_id, module: self.expander.module })) } else { None }; @@ -956,11 +964,11 @@ impl ExprCollector<'_> { match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) { Some((def_map, block_id)) => { self.body.block_scopes.push(block_id); - (def_map.root(), def_map) + (def_map.module_id(def_map.root()), def_map) } - None => (self.expander.module, self.expander.def_map.clone()), + None => (self.expander.module, self.def_map.clone()), }; - let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); + let prev_def_map = mem::replace(&mut self.def_map, def_map); let prev_local_module = mem::replace(&mut self.expander.module, module); let mut statements = Vec::new(); @@ -982,7 +990,7 @@ impl ExprCollector<'_> { let expr_id = self .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr); - self.expander.def_map = prev_def_map; + self.def_map = prev_def_map; self.expander.module = prev_local_module; expr_id } @@ -1028,9 +1036,9 @@ impl ExprCollector<'_> { let (binding, pattern) = if is_simple_ident_pat { // This could also be a single-segment path pattern. To // decide that, we need to try resolving the name. - let (resolved, _) = self.expander.def_map.resolve_path( + let (resolved, _) = self.def_map.resolve_path( self.db, - self.expander.module, + self.expander.module.local_id, &name.clone().into(), BuiltinShadowMode::Other, ); diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 3fdd09b004..73d4eebb85 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -11,8 +11,8 @@ use syntax::{ast, Parse}; use crate::{ attr::Attrs, - body::{Expander, Mark}, db::DefDatabase, + expander::{Expander, Mark}, item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId}, nameres::{ attr_resolution::ResolvedAttr, @@ -44,16 +44,16 @@ impl FunctionData { pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc { let loc = func.lookup(db); let krate = loc.container.module(db).krate; - let crate_graph = db.crate_graph(); - let cfg_options = &crate_graph[krate].cfg_options; let item_tree = loc.id.item_tree(db); let func = &item_tree[loc.id.value]; let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { - db.trait_data(trait_id).visibility.clone() + trait_vis(db, trait_id) } else { item_tree[func.visibility].clone() }; + let crate_graph = db.crate_graph(); + let cfg_options = &crate_graph[krate].cfg_options; let enabled_params = func .params .clone() @@ -188,7 +188,7 @@ impl TypeAliasData { let item_tree = loc.id.item_tree(db); let typ = &item_tree[loc.id.value]; let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { - db.trait_data(trait_id).visibility.clone() + trait_vis(db, trait_id) } else { item_tree[typ.visibility].clone() }; @@ -471,7 +471,7 @@ impl ConstData { let item_tree = loc.id.item_tree(db); let konst = &item_tree[loc.id.value]; let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { - db.trait_data(trait_id).visibility.clone() + trait_vis(db, trait_id) } else { item_tree[konst.visibility].clone() }; @@ -647,8 +647,20 @@ impl<'a> AssocItemCollector<'a> { let _cx = stdx::panic_context::enter(format!( "collect_items MacroCall: {macro_call}" )); + let module = self.expander.module.local_id; + if let Ok(res) = - self.expander.enter_expand::(self.db, macro_call) + self.expander.enter_expand::(self.db, macro_call, |path| { + self.def_map + .resolve_path( + self.db, + module, + &path, + crate::item_scope::BuiltinShadowMode::Other, + ) + .0 + .take_macros() + }) { self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike { ast_id: InFile::new(file_id, call.ast_id), @@ -692,3 +704,10 @@ impl<'a> AssocItemCollector<'a> { self.expander.exit(self.db, mark); } } + +fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility { + let ItemLoc { id: tree_id, .. } = trait_id.lookup(db); + let item_tree = tree_id.item_tree(db); + let tr_def = &item_tree[tree_id.value]; + item_tree[tr_def.visibility].clone() +} diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index 0e4b033638..290e98f8bd 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/crates/hir-def/src/data/adt.rs @@ -17,11 +17,12 @@ use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use syntax::ast::{self, HasName, HasVisibility}; use crate::{ - body::{CfgExpander, LowerCtx}, builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, + expander::CfgExpander, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, lang_item::LangItem, + lower::LowerCtx, nameres::diagnostics::DefDiagnostic, src::HasChildSource, src::HasSource, diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 678b0fcb7e..cc87b03eef 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -217,6 +217,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast CrateLimits; + #[salsa::transparent] + fn recursion_limit(&self, crate_id: CrateId) -> u32; fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; } @@ -240,6 +242,10 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits { } } +fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 { + db.crate_limits(crate_id).recursion_limit +} + fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { let file = db.crate_graph()[crate_id].root_file_id; let item_tree = db.file_item_tree(file.into()); diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs new file mode 100644 index 0000000000..34ed1e72f2 --- /dev/null +++ b/crates/hir-def/src/expander.rs @@ -0,0 +1,211 @@ +//! Macro expansion utilities. + +use base_db::CrateId; +use cfg::CfgOptions; +use drop_bomb::DropBomb; +use hir_expand::{ + attrs::RawAttrs, hygiene::Hygiene, mod_path::ModPath, ExpandError, ExpandResult, HirFileId, + InFile, MacroCallId, UnresolvedMacro, +}; +use limit::Limit; +use syntax::{ast, Parse, SyntaxNode}; + +use crate::{ + attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall, + MacroId, ModuleId, +}; + +/// A subset of Expander that only deals with cfg attributes. We only need it to +/// avoid cyclic queries in crate def map during enum processing. +#[derive(Debug)] +pub(crate) struct CfgExpander { + cfg_options: CfgOptions, + hygiene: Hygiene, + krate: CrateId, +} + +#[derive(Debug)] +pub struct Expander { + cfg_expander: CfgExpander, + pub(crate) current_file_id: HirFileId, + pub(crate) module: ModuleId, + /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. + recursion_depth: u32, + recursion_limit: Limit, +} + +impl CfgExpander { + pub(crate) fn new( + db: &dyn DefDatabase, + current_file_id: HirFileId, + krate: CrateId, + ) -> CfgExpander { + let hygiene = Hygiene::new(db.upcast(), current_file_id); + let cfg_options = db.crate_graph()[krate].cfg_options.clone(); + CfgExpander { cfg_options, hygiene, krate } + } + + pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { + Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) + } + + pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool { + let attrs = self.parse_attrs(db, owner); + attrs.is_cfg_enabled(&self.cfg_options) + } + + pub(crate) fn hygiene(&self) -> &Hygiene { + &self.hygiene + } +} + +impl Expander { + pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { + let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); + let recursion_limit = db.recursion_limit(module.krate); + #[cfg(not(test))] + let recursion_limit = Limit::new(recursion_limit as usize); + // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug + #[cfg(test)] + let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize)); + Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit } + } + + pub fn enter_expand( + &mut self, + db: &dyn DefDatabase, + macro_call: ast::MacroCall, + resolver: impl Fn(ModPath) -> Option, + ) -> Result)>>, UnresolvedMacro> { + // FIXME: within_limit should support this, instead of us having to extract the error + let mut unresolved_macro_err = None; + + let result = self.within_limit(db, |this| { + let macro_call = InFile::new(this.current_file_id, ¯o_call); + match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| { + resolver(path).map(|it| macro_id_to_def_id(db, it)) + }) { + Ok(call_id) => call_id, + Err(resolve_err) => { + unresolved_macro_err = Some(resolve_err); + ExpandResult { value: None, err: None } + } + } + }); + + if let Some(err) = unresolved_macro_err { + Err(err) + } else { + Ok(result) + } + } + + pub fn enter_expand_id( + &mut self, + db: &dyn DefDatabase, + call_id: MacroCallId, + ) -> ExpandResult)>> { + self.within_limit(db, |_this| ExpandResult::ok(Some(call_id))) + } + + fn enter_expand_inner( + db: &dyn DefDatabase, + call_id: MacroCallId, + error: Option, + ) -> ExpandResult>>> { + let file_id = call_id.as_file(); + let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id); + + ExpandResult { value: Some(InFile::new(file_id, value)), err: error.or(err) } + } + + pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { + self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); + self.current_file_id = mark.file_id; + if self.recursion_depth == u32::MAX { + // Recursion limit has been reached somewhere in the macro expansion tree. Reset the + // depth only when we get out of the tree. + if !self.current_file_id.is_macro() { + self.recursion_depth = 0; + } + } else { + self.recursion_depth -= 1; + } + mark.bomb.defuse(); + } + + pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> { + LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id) + } + + pub(crate) fn to_source(&self, value: T) -> InFile { + InFile { file_id: self.current_file_id, value } + } + + pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { + self.cfg_expander.parse_attrs(db, owner) + } + + pub(crate) fn cfg_options(&self) -> &CfgOptions { + &self.cfg_expander.cfg_options + } + + pub fn current_file_id(&self) -> HirFileId { + self.current_file_id + } + + pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option { + let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene); + Path::from_src(path, &ctx) + } + + fn within_limit( + &mut self, + db: &dyn DefDatabase, + op: F, + ) -> ExpandResult)>> + where + F: FnOnce(&mut Self) -> ExpandResult>, + { + if self.recursion_depth == u32::MAX { + // Recursion limit has been reached somewhere in the macro expansion tree. We should + // stop expanding other macro calls in this tree, or else this may result in + // exponential number of macro expansions, leading to a hang. + // + // The overflow error should have been reported when it occurred (see the next branch), + // so don't return overflow error here to avoid diagnostics duplication. + cov_mark::hit!(overflow_but_not_me); + return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned); + } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() { + self.recursion_depth = u32::MAX; + cov_mark::hit!(your_stack_belongs_to_me); + return ExpandResult::only_err(ExpandError::Other( + "reached recursion limit during macro expansion".into(), + )); + } + + let ExpandResult { value, err } = op(self); + let Some(call_id) = value else { + return ExpandResult { value: None, err }; + }; + + Self::enter_expand_inner(db, call_id, err).map(|value| { + value.and_then(|InFile { file_id, value }| { + let parse = value.cast::()?; + + self.recursion_depth += 1; + self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); + let old_file_id = std::mem::replace(&mut self.current_file_id, file_id); + let mark = + Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") }; + Some((mark, parse)) + }) + }) + } +} + +#[derive(Debug)] +pub struct Mark { + file_id: HirFileId, + bomb: DropBomb, +} diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index c1e20d657b..c766857758 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -3,6 +3,8 @@ //! generic parameters. See also the `Generics` type and the `generics_of` query //! in rustc. +use std::sync::Arc; + use base_db::FileId; use either::Either; use hir_expand::{ @@ -16,10 +18,12 @@ use stdx::impl_from; use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds}; use crate::{ - body::{Expander, LowerCtx}, child_by_source::ChildBySource, db::DefDatabase, dyn_map::{keys, DynMap}, + expander::Expander, + lower::LowerCtx, + nameres::DefMap, src::{HasChildSource, HasSource}, type_ref::{LifetimeRef, TypeBound, TypeRef}, AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId, @@ -151,7 +155,6 @@ impl GenericParams { def: GenericDefId, ) -> Interned { let _p = profile::span("generic_params_query"); - macro_rules! id_to_generics { ($id:ident) => {{ let id = $id.lookup(db).id; @@ -174,7 +177,9 @@ impl GenericParams { // Don't create an `Expander` nor call `loc.source(db)` if not needed since this // causes a reparse after the `ItemTree` has been created. - let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module)); + let mut expander = Lazy::new(|| { + (module.def_map(db), Expander::new(db, loc.source(db).file_id, module)) + }); for param in &func_data.params { generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); } @@ -327,7 +332,7 @@ impl GenericParams { pub(crate) fn fill_implicit_impl_trait_args( &mut self, db: &dyn DefDatabase, - expander: &mut Expander, + exp: &mut Lazy<(Arc, Expander), impl FnOnce() -> (Arc, Expander)>, type_ref: &TypeRef, ) { type_ref.walk(&mut |type_ref| { @@ -347,14 +352,27 @@ impl GenericParams { } if let TypeRef::Macro(mc) = type_ref { let macro_call = mc.to_node(db.upcast()); - match expander.enter_expand::(db, macro_call) { - Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { - let ctx = expander.ctx(db); - let type_ref = TypeRef::from_ast(&ctx, expanded.tree()); - self.fill_implicit_impl_trait_args(db, expander, &type_ref); - expander.exit(db, mark); - } - _ => {} + let (def_map, expander) = &mut **exp; + + let module = expander.module.local_id; + let resolver = |path| { + def_map + .resolve_path( + db, + module, + &path, + crate::item_scope::BuiltinShadowMode::Other, + ) + .0 + .take_macros() + }; + if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) = + expander.enter_expand(db, macro_call, resolver) + { + let ctx = expander.ctx(db); + let type_ref = TypeRef::from_ast(&ctx, expanded.tree()); + self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref); + exp.1.exit(db, mark); } } }); diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 0e2c0d864d..06e6be66ba 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -11,9 +11,9 @@ use intern::Interned; use syntax::ast::{self, HasName}; use crate::{ - body::LowerCtx, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, hir::Literal, + lower::LowerCtx, path::Path, }; diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs index 07a68d1598..0480e6a51d 100644 --- a/crates/hir-def/src/import_map.rs +++ b/crates/hir-def/src/import_map.rs @@ -476,7 +476,7 @@ mod tests { use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; use expect_test::{expect, Expect}; - use crate::{test_db::TestDB, ItemContainerId, Lookup}; + use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup}; use super::*; diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index c67c8bb440..457a519872 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> { db: &'a dyn DefDatabase, tree: ItemTree, source_ast_id_map: Arc, - body_ctx: crate::body::LowerCtx<'a>, + body_ctx: crate::lower::LowerCtx<'a>, } impl<'a> Ctx<'a> { @@ -29,7 +29,7 @@ impl<'a> Ctx<'a> { db, tree: ItemTree::default(), source_ast_id_map: db.ast_id_map(file), - body_ctx: crate::body::LowerCtx::with_file_id(db, file), + body_ctx: crate::lower::LowerCtx::with_file_id(db, file), } } diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 34d704942a..b7f0e229ee 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -22,6 +22,9 @@ pub mod builtin_type; pub mod per_ns; pub mod item_scope; +pub mod lower; +pub mod expander; + pub mod dyn_map; pub mod item_tree; @@ -65,6 +68,7 @@ use hir_expand::{ builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, + db::ExpandDatabase, eager::expand_eager_macro, hygiene::Hygiene, proc_macro::ProcMacroExpander, @@ -791,7 +795,7 @@ impl AttrDefId { pub trait AsMacroCall { fn as_call_id( &self, - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Option { @@ -800,7 +804,7 @@ pub trait AsMacroCall { fn as_call_id_with_errors( &self, - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Result>, UnresolvedMacro>; @@ -809,15 +813,14 @@ pub trait AsMacroCall { impl AsMacroCall for InFile<&ast::MacroCall> { fn as_call_id_with_errors( &self, - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Result>, UnresolvedMacro> { let expands_to = hir_expand::ExpandTo::from_call_site(self.value); let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); - let h = Hygiene::new(db.upcast(), self.file_id); - let path = - self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h)); + let h = Hygiene::new(db, self.file_id); + let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h)); let Some(path) = path else { return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into()))); @@ -847,7 +850,7 @@ impl AstIdWithPath { } fn macro_call_as_call_id( - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, call: &AstIdWithPath, expand_to: ExpandTo, krate: CrateId, @@ -857,7 +860,7 @@ fn macro_call_as_call_id( } fn macro_call_as_call_id_( - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, call: &AstIdWithPath, expand_to: ExpandTo, krate: CrateId, @@ -867,13 +870,12 @@ fn macro_call_as_call_id_( resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; let res = if let MacroDefKind::BuiltInEager(..) = def.kind { - let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); - - expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver)? + let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db)); + expand_eager_macro(db, krate, macro_call, def, &resolver)? } else { ExpandResult { value: Some(def.as_lazy_macro( - db.upcast(), + db, krate, MacroCallKind::FnLike { ast_id: call.ast_id, expand_to }, )), diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs new file mode 100644 index 0000000000..1991d547f5 --- /dev/null +++ b/crates/hir-def/src/lower.rs @@ -0,0 +1,46 @@ +//! Context for lowering paths. +use std::sync::Arc; + +use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile}; +use once_cell::unsync::OnceCell; +use syntax::ast; + +use crate::{db::DefDatabase, path::Path}; + +pub struct LowerCtx<'a> { + pub db: &'a dyn DefDatabase, + hygiene: Hygiene, + ast_id_map: Option<(HirFileId, OnceCell>)>, +} + +impl<'a> LowerCtx<'a> { + pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self { + LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) } + } + + pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self { + LowerCtx { + db, + hygiene: Hygiene::new(db.upcast(), file_id), + ast_id_map: Some((file_id, OnceCell::new())), + } + } + + pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self { + LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None } + } + + pub(crate) fn hygiene(&self) -> &Hygiene { + &self.hygiene + } + + pub(crate) fn lower_path(&self, ast: ast::Path) -> Option { + Path::from_src(ast, self) + } + + pub(crate) fn ast_id(&self, item: &N) -> Option> { + let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?; + let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id)); + Some(InFile::new(file_id, ast_id_map.ast_id(item))) + } +} diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 461b498fa0..756a8f5049 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -1111,7 +1111,7 @@ impl DefCollector<'_> { match &directive.kind { MacroDirectiveKind::FnLike { ast_id, expand_to } => { let call_id = macro_call_as_call_id( - self.db, + self.db.upcast(), ast_id, *expand_to, self.def_map.krate, @@ -1402,7 +1402,7 @@ impl DefCollector<'_> { MacroDirectiveKind::FnLike { ast_id, expand_to } => { // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error! let macro_call_as_call_id = macro_call_as_call_id( - self.db, + self.db.upcast(), ast_id, *expand_to, self.def_map.krate, @@ -2117,7 +2117,7 @@ impl ModCollector<'_, '_> { // Case 1: try to resolve in legacy scope and expand macro_rules if let Ok(res) = macro_call_as_call_id( - self.def_collector.db, + self.def_collector.db.upcast(), &ast_id, mac.expand_to, self.def_collector.def_map.krate, diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs index d2e3575d5e..34fa15f9e1 100644 --- a/crates/hir-def/src/nameres/tests/incremental.rs +++ b/crates/hir-def/src/nameres/tests/incremental.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use base_db::SourceDatabaseExt; -use crate::{AdtId, ModuleDefId}; +use crate::{db::DefDatabase, AdtId, ModuleDefId}; use super::*; diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs index 8cd287f7f3..b9b8082549 100644 --- a/crates/hir-def/src/path.rs +++ b/crates/hir-def/src/path.rs @@ -7,8 +7,8 @@ use std::{ }; use crate::{ - body::LowerCtx, lang_item::LangItemTarget, + lower::LowerCtx, type_ref::{ConstRefOrPath, LifetimeRef, TypeBound, TypeRef}, }; use hir_expand::name::Name; diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs index c35f915b00..721f9b7680 100644 --- a/crates/hir-def/src/path/lower.rs +++ b/crates/hir-def/src/path/lower.rs @@ -2,17 +2,15 @@ use std::iter; -use crate::type_ref::ConstRefOrPath; +use crate::{lower::LowerCtx, type_ref::ConstRefOrPath}; use either::Either; use hir_expand::name::{name, AsName}; use intern::Interned; use syntax::ast::{self, AstNode, HasTypeBounds}; -use super::AssociatedTypeBinding; use crate::{ - body::LowerCtx, - path::{GenericArg, GenericArgs, ModPath, Path, PathKind}, + path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind}, type_ref::{LifetimeRef, TypeBound, TypeRef}, }; diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 0eef25102e..d445abae92 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -7,7 +7,6 @@ use std::fmt::{self, Debug}; use base_db::CrateId; use chalk_ir::{BoundVar, TyKind}; use hir_def::{ - body, data::adt::VariantData, db::DefDatabase, find_path, @@ -1552,7 +1551,10 @@ impl HirDisplay for TypeRef { } TypeRef::Macro(macro_call) => { let macro_call = macro_call.to_node(f.db.upcast()); - let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic()); + let ctx = hir_def::lower::LowerCtx::with_hygiene( + f.db.upcast(), + &Hygiene::new_unhygienic(), + ); match macro_call.path() { Some(path) => match Path::from_src(path, &ctx) { Some(path) => path.hir_fmt(f)?, diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs index 874f808cda..19bb7f169f 100644 --- a/crates/hir-ty/src/interner.rs +++ b/crates/hir-ty/src/interner.rs @@ -43,7 +43,7 @@ impl_internable!( ); impl chalk_ir::interner::Interner for Interner { - type InternedType = Interned>>; + type InternedType = Interned>>; type InternedLifetime = Interned>>; type InternedConst = Interned>>; type InternedConcreteConst = ConstScalar; diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 33dc5e2d69..7da747b949 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -18,9 +18,9 @@ use chalk_ir::{ use either::Either; use hir_def::{ - body::Expander, builtin_type::BuiltinType, data::adt::StructKind, + expander::Expander, generics::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, @@ -378,7 +378,9 @@ impl<'a> TyLoweringContext<'a> { }; let ty = { let macro_call = macro_call.to_node(self.db.upcast()); - match expander.enter_expand::(self.db.upcast(), macro_call) { + match expander.enter_expand::(self.db.upcast(), macro_call, |path| { + self.resolver.resolve_path_as_macro(self.db.upcast(), &path) + }) { Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { let ctx = expander.ctx(self.db.upcast()); // FIXME: Report syntax errors in expansion here diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index b32efe1cb5..a9f7813150 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -7,8 +7,8 @@ use std::{cell::RefCell, fmt, iter, mem, ops}; use base_db::{FileId, FileRange}; use either::Either; use hir_def::{ - body, hir::Expr, + lower::LowerCtx, macro_id_to_def_id, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, @@ -1065,7 +1065,7 @@ impl<'db> SemanticsImpl<'db> { fn resolve_type(&self, ty: &ast::Type) -> Option { let analyze = self.analyze(ty.syntax())?; - let ctx = body::LowerCtx::with_file_id(self.db.upcast(), analyze.file_id); + let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id); let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver) .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone())); Some(Type::new_with_resolver(self.db, &analyze.resolver, ty)) @@ -1074,7 +1074,7 @@ impl<'db> SemanticsImpl<'db> { fn resolve_trait(&self, path: &ast::Path) -> Option { let analyze = self.analyze(path.syntax())?; let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id); - let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene); + let ctx = LowerCtx::with_hygiene(self.db.upcast(), &hygiene); let hir_path = Path::from_src(path.clone(), &ctx)?; match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? { TypeNs::TraitId(id) => Some(Trait { id }), @@ -1672,7 +1672,7 @@ impl<'a> SemanticsScope<'a> { /// Resolve a path as-if it was written at the given scope. This is /// necessary a heuristic, as it doesn't take hygiene into account. pub fn speculative_resolve(&self, path: &ast::Path) -> Option { - let ctx = body::LowerCtx::with_file_id(self.db.upcast(), self.file_id); + let ctx = LowerCtx::with_file_id(self.db.upcast(), self.file_id); let path = Path::from_src(path.clone(), &ctx)?; resolve_hir_path(self.db, &self.resolver, &path) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index d0bf1c23ac..a6a51e4907 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -13,12 +13,12 @@ use std::{ use either::Either; use hir_def::{ body::{ - self, scope::{ExprScopes, ScopeId}, Body, BodySourceMap, }, hir::{ExprId, Pat, PatId}, lang_item::LangItem, + lower::LowerCtx, macro_id_to_def_id, path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, @@ -463,7 +463,7 @@ impl SourceAnalyzer { db: &dyn HirDatabase, macro_call: InFile<&ast::MacroCall>, ) -> Option { - let ctx = body::LowerCtx::with_file_id(db.upcast(), macro_call.file_id); + let ctx = LowerCtx::with_file_id(db.upcast(), macro_call.file_id); let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into()) } @@ -575,7 +575,7 @@ impl SourceAnalyzer { // This must be a normal source file rather than macro file. let hygiene = Hygiene::new(db.upcast(), self.file_id); - let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene); + let ctx = LowerCtx::with_hygiene(db.upcast(), &hygiene); let hir_path = Path::from_src(path.clone(), &ctx)?; // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are diff --git a/crates/ide-db/src/line_index.rs b/crates/ide-db/src/line_index.rs index 16814a1e63..9fb58ebe8a 100644 --- a/crates/ide-db/src/line_index.rs +++ b/crates/ide-db/src/line_index.rs @@ -109,6 +109,9 @@ impl LineIndex { line_wide_chars.insert(line, wide_chars); } + newlines.shrink_to_fit(); + line_wide_chars.shrink_to_fit(); + LineIndex { newlines, line_wide_chars } } diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index a91ffd1ec4..dcdcd17dc6 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -93,6 +93,7 @@ impl Query { pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast { /// The symbol index for a given module. These modules should only be in source roots that /// are inside local_roots. + // FIXME: We should probably LRU this fn module_symbols(&self, module: Module) -> Arc; /// The symbol index for a given source root within library_roots. diff --git a/crates/limit/src/lib.rs b/crates/limit/src/lib.rs index 6b2534aa46..7fb4b513a7 100644 --- a/crates/limit/src/lib.rs +++ b/crates/limit/src/lib.rs @@ -6,6 +6,7 @@ use std::sync::atomic::AtomicUsize; /// Represents a struct used to enforce a numerical limit. +#[derive(Debug)] pub struct Limit { upper_bound: usize, #[cfg(feature = "tracking")]