]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir_def/src/body.rs
parameters.split_last()
[rust.git] / crates / hir_def / src / body.rs
index 44ae136437fc3d86028be87e9a3e4ca99fe72f3b..8488b4f0d03ff5fe1c4cadf978fbbf920ab96a2d 100644 (file)
@@ -1,7 +1,6 @@
 //! Defines `Body`: a lowered representation of bodies of functions, statics and
 //! consts.
 mod lower;
-mod diagnostics;
 #[cfg(test)]
 mod tests;
 pub mod scope;
@@ -9,19 +8,18 @@
 use std::{mem, ops::Index, sync::Arc};
 
 use base_db::CrateId;
-use cfg::CfgOptions;
+use cfg::{CfgExpr, CfgOptions};
 use drop_bomb::DropBomb;
 use either::Either;
 use hir_expand::{
-    ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult,
-    HirFileId, InFile, MacroDefId,
+    ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandError, ExpandResult, HirFileId, InFile,
+    MacroCallId, MacroDefId,
 };
 use la_arena::{Arena, ArenaMap};
+use limit::Limit;
 use profile::Count;
 use rustc_hash::FxHashMap;
-use syntax::{ast, AstNode, AstPtr, SyntaxNode};
-
-pub use lower::LowerCtx;
+use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
 
 use crate::{
     attr::{Attrs, RawAttrs},
     UnresolvedMacro,
 };
 
+pub use lower::LowerCtx;
+
 /// 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,
 }
 
-pub(crate) struct Expander {
+#[derive(Debug)]
+pub struct Expander {
     cfg_expander: CfgExpander,
     def_map: Arc<DefMap>,
     current_file_id: HirFileId,
@@ -52,12 +54,6 @@ pub(crate) struct Expander {
     recursion_limit: usize,
 }
 
-#[cfg(test)]
-const EXPANSION_RECURSION_LIMIT: usize = 32;
-
-#[cfg(not(test))]
-const EXPANSION_RECURSION_LIMIT: usize = 128;
-
 impl CfgExpander {
     pub(crate) fn new(
         db: &dyn DefDatabase,
@@ -69,22 +65,18 @@ pub(crate) fn new(
         CfgExpander { cfg_options, hygiene, krate }
     }
 
-    pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> Attrs {
-        RawAttrs::new(owner, &self.hygiene).filter(db, self.krate)
+    pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+        RawAttrs::new(db, owner, &self.hygiene).filter(db, self.krate)
     }
 
-    pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::AttrsOwner) -> bool {
+    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)
     }
 }
 
 impl Expander {
-    pub(crate) fn new(
-        db: &dyn DefDatabase,
-        current_file_id: HirFileId,
-        module: ModuleId,
-    ) -> 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);
         let ast_id_map = db.ast_id_map(current_file_id);
@@ -98,15 +90,12 @@ pub(crate) fn new(
         }
     }
 
-    fn enter_expand_intern(
+    pub fn enter_expand<T: ast::AstNode>(
         &mut self,
         db: &dyn DefDatabase,
         macro_call: ast::MacroCall,
-    ) -> Result<
-        ExpandResult<Option<(SyntaxNode, impl FnMut(&dyn DefDatabase) -> Mark + '_)>>,
-        UnresolvedMacro,
-    > {
-        if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
+    ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
+        if self.recursion_limit(db).check(self.recursion_limit + 1).is_err() {
             cov_mark::hit!(your_stack_belongs_to_me);
             return Ok(ExpandResult::str_err(
                 "reached recursion limit during macro expansion".into(),
@@ -130,6 +119,23 @@ fn enter_expand_intern(
             }
         };
 
+        Ok(self.enter_expand_inner(db, call_id, err))
+    }
+
+    pub fn enter_expand_id<T: ast::AstNode>(
+        &mut self,
+        db: &dyn DefDatabase,
+        call_id: MacroCallId,
+    ) -> ExpandResult<Option<(Mark, T)>> {
+        self.enter_expand_inner(db, call_id, None)
+    }
+
+    fn enter_expand_inner<T: ast::AstNode>(
+        &mut self,
+        db: &dyn DefDatabase,
+        call_id: MacroCallId,
+        mut err: Option<ExpandError>,
+    ) -> ExpandResult<Option<(Mark, T)>> {
         if err.is_none() {
             err = db.macro_expand_error(call_id);
         }
@@ -141,80 +147,39 @@ fn enter_expand_intern(
             None => {
                 // Only `None` if the macro expansion produced no usable AST.
                 if err.is_none() {
-                    log::warn!("no error despite `parse_or_expand` failing");
+                    tracing::warn!("no error despite `parse_or_expand` failing");
                 }
 
-                return Ok(ExpandResult::only_err(err.unwrap_or_else(|| {
+                return ExpandResult::only_err(err.unwrap_or_else(|| {
                     mbe::ExpandError::Other("failed to parse macro invocation".into())
-                })));
-            }
-        };
-
-        let this = self;
-
-        let advance_state = move |db: &dyn DefDatabase| {
-            this.recursion_limit += 1;
-            let mark = Mark {
-                file_id: this.current_file_id,
-                ast_id_map: mem::take(&mut this.ast_id_map),
-                bomb: DropBomb::new("expansion mark dropped"),
-            };
-            this.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
-            this.current_file_id = file_id;
-            this.ast_id_map = db.ast_id_map(file_id);
-            mark
-        };
-
-        Ok(ExpandResult { value: Some((raw_node, advance_state)), err })
-    }
-
-    pub(crate) fn enter_expand_raw(
-        &mut self,
-        db: &dyn DefDatabase,
-        macro_call: ast::MacroCall,
-    ) -> Result<ExpandResult<Option<(Mark, SyntaxNode)>>, UnresolvedMacro> {
-        let (raw_node, mut advance_state, err) = match self.enter_expand_intern(db, macro_call)? {
-            ExpandResult { value: Some((raw_node, advance_state)), err } => {
-                (raw_node, advance_state, err)
+                }));
             }
-            ExpandResult { value: None, err } => return Ok(ExpandResult { value: None, err }),
-        };
-
-        log::debug!("macro expansion {:#?}", raw_node);
-
-        let mark = advance_state(db);
-
-        Ok(ExpandResult { value: Some((mark, raw_node)), err })
-    }
-
-    pub(crate) fn enter_expand<T: ast::AstNode>(
-        &mut self,
-        db: &dyn DefDatabase,
-        macro_call: ast::MacroCall,
-    ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
-        let (raw_node, mut advance_state, err) = match self.enter_expand_intern(db, macro_call)? {
-            ExpandResult { value: Some((raw_node, advance_state)), err } => {
-                (raw_node, advance_state, err)
-            }
-            ExpandResult { value: None, err } => return Ok(ExpandResult { value: None, err }),
         };
 
         let node = match T::cast(raw_node) {
             Some(it) => it,
             None => {
                 // This can happen without being an error, so only forward previous errors.
-                return Ok(ExpandResult { value: None, err });
+                return ExpandResult { value: None, err };
             }
         };
 
-        log::debug!("macro expansion {:#?}", node.syntax());
+        tracing::debug!("macro expansion {:#?}", node.syntax());
 
-        let mark = advance_state(db);
+        self.recursion_limit += 1;
+        let mark = Mark {
+            file_id: self.current_file_id,
+            ast_id_map: mem::take(&mut self.ast_id_map),
+            bomb: DropBomb::new("expansion mark dropped"),
+        };
+        self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
+        self.current_file_id = file_id;
+        self.ast_id_map = db.ast_id_map(file_id);
 
-        Ok(ExpandResult { value: Some((mark, node)), err })
+        ExpandResult { value: Some((mark, node)), err }
     }
 
-    pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
+    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;
         self.ast_id_map = mem::take(&mut mark.ast_id_map);
@@ -226,7 +191,7 @@ 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::AttrsOwner) -> Attrs {
+    pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
         self.cfg_expander.parse_attrs(db, owner)
     }
 
@@ -234,8 +199,12 @@ pub(crate) fn cfg_options(&self) -> &CfgOptions {
         &self.cfg_expander.cfg_options
     }
 
-    fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
-        let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene);
+    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)
     }
 
@@ -247,10 +216,21 @@ fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
         let file_local_id = self.ast_id_map.ast_id(item);
         AstId::new(self.current_file_id, file_local_id)
     }
+
+    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));
+    }
 }
 
 #[derive(Debug)]
-pub(crate) struct Mark {
+pub struct Mark {
     file_id: HirFileId,
     ast_id_map: Arc<AstIdMap>,
     bomb: DropBomb,
@@ -315,12 +295,20 @@ pub struct BodySourceMap {
 
     /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
     /// the source map (since they're just as volatile).
-    diagnostics: Vec<diagnostics::BodyDiagnostic>,
+    diagnostics: Vec<BodyDiagnostic>,
 }
 
 #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
 pub struct SyntheticSyntax;
 
+#[derive(Debug, Eq, PartialEq)]
+pub enum BodyDiagnostic {
+    InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
+    MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
+    UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>> },
+    UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+}
+
 impl Body {
     pub(crate) fn body_with_source_map_query(
         db: &dyn DefDatabase,
@@ -458,9 +446,8 @@ pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId>
         self.field_map.get(&src).cloned()
     }
 
-    pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) {
-        for diag in &self.diagnostics {
-            diag.add_to(sink);
-        }
+    /// Get a reference to the body source map's diagnostics.
+    pub fn diagnostics(&self) -> &[BodyDiagnostic] {
+        &self.diagnostics
     }
 }