Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/body.rs')
-rw-r--r--crates/hir-def/src/body.rs347
1 files changed, 45 insertions, 302 deletions
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<Arc<AstIdMap>>)>,
-}
-
-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> {
- Path::from_src(ast, self)
- }
-
- pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
- 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<DefMap>,
- 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<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- macro_call: ast::MacroCall,
- ) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, 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, &macro_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<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- ) -> ExpandResult<Option<(Mark, Parse<T>)>> {
- self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
- }
-
- fn enter_expand_inner(
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- error: Option<ExpandError>,
- ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
- 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<T>(&self, value: T) -> InFile<T> {
- 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<Path> {
- 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<MacroId> {
- 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<F, T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- op: F,
- ) -> ExpandResult<Option<(Mark, Parse<T>)>>
- where
- F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
- {
- 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::<T>()?;
-
- 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);