mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Merge #6467
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:
commit
4e8401af41
4 changed files with 62 additions and 18 deletions
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue