6467: Don't stack overflow on circular modules r=matklad a=matklad

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-11-04 14:38:19 +00:00 committed by GitHub
commit 4e8401af41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 18 deletions

View file

@ -1116,17 +1116,20 @@ impl ModCollector<'_, '_> {
&self.item_tree[module.visibility], &self.item_tree[module.visibility],
); );
ModCollector { if let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr)
def_collector: &mut *self.def_collector, {
macro_depth: self.macro_depth, ModCollector {
module_id, def_collector: &mut *self.def_collector,
file_id: self.file_id, macro_depth: self.macro_depth,
item_tree: self.item_tree, module_id,
mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr), file_id: self.file_id,
} item_tree: self.item_tree,
.collect(&*items); mod_dir,
if is_macro_use { }
self.import_all_legacy_macros(module_id); .collect(&*items);
if is_macro_use {
self.import_all_legacy_macros(module_id);
}
} }
} }
// out of line module, resolve, parse and recurse // out of line module, resolve, parse and recurse

View file

@ -2,9 +2,12 @@
use base_db::FileId; use base_db::FileId;
use hir_expand::name::Name; use hir_expand::name::Name;
use syntax::SmolStr; use syntax::SmolStr;
use test_utils::mark;
use crate::{db::DefDatabase, HirFileId}; use crate::{db::DefDatabase, HirFileId};
const MOD_DEPTH_LIMIT: u32 = 32;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(super) struct ModDir { pub(super) struct ModDir {
/// `` for `mod.rs`, `lib.rs` /// `` for `mod.rs`, `lib.rs`
@ -14,18 +17,28 @@ pub(super) struct ModDir {
dir_path: DirPath, dir_path: DirPath,
/// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/` /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
root_non_dir_owner: bool, root_non_dir_owner: bool,
depth: u32,
} }
impl ModDir { impl ModDir {
pub(super) fn root() -> 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<ModDir> {
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( pub(super) fn descend_into_definition(
&self, &self,
name: &Name, name: &Name,
attr_path: Option<&SmolStr>, attr_path: Option<&SmolStr>,
) -> ModDir { ) -> Option<ModDir> {
let path = match attr_path.map(|it| it.as_str()) { let path = match attr_path.map(|it| it.as_str()) {
None => { None => {
let mut path = self.dir_path.clone(); let mut path = self.dir_path.clone();
@ -40,7 +53,7 @@ impl ModDir {
DirPath::new(path) DirPath::new(path)
} }
}; };
ModDir { dir_path: path, root_non_dir_owner: false } self.child(path, false)
} }
pub(super) fn resolve_declaration( pub(super) fn resolve_declaration(
@ -72,7 +85,9 @@ impl ModDir {
} else { } else {
(DirPath::new(format!("{}/", name)), true) (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)) Err(candidate_files.remove(0))

View file

@ -20,9 +20,8 @@ fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
} }
fn check(ra_fixture: &str, expect: Expect) { fn check(ra_fixture: &str, expect: Expect) {
let db = TestDB::with_files(ra_fixture); let def_map = compute_crate_def_map(ra_fixture);
let krate = db.crate_graph().iter().next().unwrap(); let actual = def_map.dump();
let actual = db.crate_def_map(krate).dump();
expect.assert_eq(&actual); expect.assert_eq(&actual);
} }

View file

@ -771,3 +771,30 @@ struct X;
"#]], "#]],
); );
} }
#[test]
fn circular_mods() {
mark::check!(circular_mods);
compute_crate_def_map(
r#"
//- /lib.rs
mod foo;
//- /foo.rs
#[path = "./foo.rs"]
mod foo;
"#,
);
compute_crate_def_map(
r#"
//- /lib.rs
mod foo;
//- /foo.rs
#[path = "./bar.rs"]
mod bar;
//- /bar.rs
#[path = "./foo.rs"]
mod foo;
"#,
);
}