Skip to content

Commit 4e8401a

Browse files
bors[bot]matklad
andauthored
Merge #6467
6467: Don't stack overflow on circular modules r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents bd6eeff + 2b10813 commit 4e8401a

File tree

4 files changed

+62
-18
lines changed

4 files changed

+62
-18
lines changed

crates/hir_def/src/nameres/collector.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,17 +1116,20 @@ impl ModCollector<'_, '_> {
11161116
&self.item_tree[module.visibility],
11171117
);
11181118

1119-
ModCollector {
1120-
def_collector: &mut *self.def_collector,
1121-
macro_depth: self.macro_depth,
1122-
module_id,
1123-
file_id: self.file_id,
1124-
item_tree: self.item_tree,
1125-
mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
1126-
}
1127-
.collect(&*items);
1128-
if is_macro_use {
1129-
self.import_all_legacy_macros(module_id);
1119+
if let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr)
1120+
{
1121+
ModCollector {
1122+
def_collector: &mut *self.def_collector,
1123+
macro_depth: self.macro_depth,
1124+
module_id,
1125+
file_id: self.file_id,
1126+
item_tree: self.item_tree,
1127+
mod_dir,
1128+
}
1129+
.collect(&*items);
1130+
if is_macro_use {
1131+
self.import_all_legacy_macros(module_id);
1132+
}
11301133
}
11311134
}
11321135
// out of line module, resolve, parse and recurse

crates/hir_def/src/nameres/mod_resolution.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
use base_db::FileId;
33
use hir_expand::name::Name;
44
use syntax::SmolStr;
5+
use test_utils::mark;
56

67
use crate::{db::DefDatabase, HirFileId};
78

9+
const MOD_DEPTH_LIMIT: u32 = 32;
10+
811
#[derive(Clone, Debug)]
912
pub(super) struct ModDir {
1013
/// `` for `mod.rs`, `lib.rs`
@@ -14,18 +17,28 @@ pub(super) struct ModDir {
1417
dir_path: DirPath,
1518
/// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
1619
root_non_dir_owner: bool,
20+
depth: u32,
1721
}
1822

1923
impl ModDir {
2024
pub(super) fn root() -> ModDir {
21-
ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false }
25+
ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false, depth: 0 }
26+
}
27+
fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option<ModDir> {
28+
let depth = self.depth + 1;
29+
if depth > MOD_DEPTH_LIMIT {
30+
log::error!("MOD_DEPTH_LIMIT exceeded");
31+
mark::hit!(circular_mods);
32+
return None;
33+
}
34+
Some(ModDir { dir_path, root_non_dir_owner, depth })
2235
}
2336

2437
pub(super) fn descend_into_definition(
2538
&self,
2639
name: &Name,
2740
attr_path: Option<&SmolStr>,
28-
) -> ModDir {
41+
) -> Option<ModDir> {
2942
let path = match attr_path.map(|it| it.as_str()) {
3043
None => {
3144
let mut path = self.dir_path.clone();
@@ -40,7 +53,7 @@ impl ModDir {
4053
DirPath::new(path)
4154
}
4255
};
43-
ModDir { dir_path: path, root_non_dir_owner: false }
56+
self.child(path, false)
4457
}
4558

4659
pub(super) fn resolve_declaration(
@@ -72,7 +85,9 @@ impl ModDir {
7285
} else {
7386
(DirPath::new(format!("{}/", name)), true)
7487
};
75-
return Ok((file_id, is_mod_rs, ModDir { dir_path, root_non_dir_owner }));
88+
if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
89+
return Ok((file_id, is_mod_rs, mod_dir));
90+
}
7691
}
7792
}
7893
Err(candidate_files.remove(0))

crates/hir_def/src/nameres/tests.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
2020
}
2121

2222
fn check(ra_fixture: &str, expect: Expect) {
23-
let db = TestDB::with_files(ra_fixture);
24-
let krate = db.crate_graph().iter().next().unwrap();
25-
let actual = db.crate_def_map(krate).dump();
23+
let def_map = compute_crate_def_map(ra_fixture);
24+
let actual = def_map.dump();
2625
expect.assert_eq(&actual);
2726
}
2827

crates/hir_def/src/nameres/tests/mod_resolution.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,3 +771,30 @@ struct X;
771771
"#]],
772772
);
773773
}
774+
775+
#[test]
776+
fn circular_mods() {
777+
mark::check!(circular_mods);
778+
compute_crate_def_map(
779+
r#"
780+
//- /lib.rs
781+
mod foo;
782+
//- /foo.rs
783+
#[path = "./foo.rs"]
784+
mod foo;
785+
"#,
786+
);
787+
788+
compute_crate_def_map(
789+
r#"
790+
//- /lib.rs
791+
mod foo;
792+
//- /foo.rs
793+
#[path = "./bar.rs"]
794+
mod bar;
795+
//- /bar.rs
796+
#[path = "./foo.rs"]
797+
mod foo;
798+
"#,
799+
);
800+
}

0 commit comments

Comments
 (0)