From 2b108133ac35b6886c0bd2c7e7bda83e18ba3b79 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Nov 2020 15:31:35 +0100 Subject: Don't stack overflow on circular modules closes #6453 --- crates/hir_def/src/nameres/mod_resolution.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'crates/hir_def/src/nameres/mod_resolution.rs') diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs index e8389b484..c0c789cae 100644 --- a/crates/hir_def/src/nameres/mod_resolution.rs +++ b/crates/hir_def/src/nameres/mod_resolution.rs @@ -2,9 +2,12 @@ use base_db::FileId; use hir_expand::name::Name; use syntax::SmolStr; +use test_utils::mark; use crate::{db::DefDatabase, HirFileId}; +const MOD_DEPTH_LIMIT: u32 = 32; + #[derive(Clone, Debug)] pub(super) struct ModDir { /// `` for `mod.rs`, `lib.rs` @@ -14,18 +17,28 @@ pub(super) struct ModDir { dir_path: DirPath, /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/` root_non_dir_owner: bool, + depth: u32, } impl ModDir { pub(super) fn root() -> ModDir { - ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false } + ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false, depth: 0 } + } + fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option { + let depth = self.depth + 1; + if depth > MOD_DEPTH_LIMIT { + log::error!("MOD_DEPTH_LIMIT exceeded"); + mark::hit!(circular_mods); + return None; + } + Some(ModDir { dir_path, root_non_dir_owner, depth }) } pub(super) fn descend_into_definition( &self, name: &Name, attr_path: Option<&SmolStr>, - ) -> ModDir { + ) -> Option { let path = match attr_path.map(|it| it.as_str()) { None => { let mut path = self.dir_path.clone(); @@ -40,7 +53,7 @@ impl ModDir { DirPath::new(path) } }; - ModDir { dir_path: path, root_non_dir_owner: false } + self.child(path, false) } pub(super) fn resolve_declaration( @@ -72,7 +85,9 @@ impl ModDir { } else { (DirPath::new(format!("{}/", name)), true) }; - return Ok((file_id, is_mod_rs, ModDir { dir_path, root_non_dir_owner })); + if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) { + return Ok((file_id, is_mod_rs, mod_dir)); + } } } Err(candidate_files.remove(0)) -- cgit v1.2.3