diff options
author | Aleksey Kladov <[email protected]> | 2020-11-04 14:31:35 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-11-04 14:31:35 +0000 |
commit | 2b108133ac35b6886c0bd2c7e7bda83e18ba3b79 (patch) | |
tree | 139e5981758b0ba10270167f30598cb8882d2ec8 | |
parent | bdfffa372be37cc57facc2fbead920fcf3134a91 (diff) |
Don't stack overflow on circular modules
closes #6453
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 25 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/mod_resolution.rs | 23 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests.rs | 5 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/mod_resolution.rs | 27 |
4 files changed, 62 insertions, 18 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 59b6644c3..386287518 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -1116,17 +1116,20 @@ impl ModCollector<'_, '_> { | |||
1116 | &self.item_tree[module.visibility], | 1116 | &self.item_tree[module.visibility], |
1117 | ); | 1117 | ); |
1118 | 1118 | ||
1119 | ModCollector { | 1119 | if let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr) |
1120 | def_collector: &mut *self.def_collector, | 1120 | { |
1121 | macro_depth: self.macro_depth, | 1121 | ModCollector { |
1122 | module_id, | 1122 | def_collector: &mut *self.def_collector, |
1123 | file_id: self.file_id, | 1123 | macro_depth: self.macro_depth, |
1124 | item_tree: self.item_tree, | 1124 | module_id, |
1125 | mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr), | 1125 | file_id: self.file_id, |
1126 | } | 1126 | item_tree: self.item_tree, |
1127 | .collect(&*items); | 1127 | mod_dir, |
1128 | if is_macro_use { | 1128 | } |
1129 | self.import_all_legacy_macros(module_id); | 1129 | .collect(&*items); |
1130 | if is_macro_use { | ||
1131 | self.import_all_legacy_macros(module_id); | ||
1132 | } | ||
1130 | } | 1133 | } |
1131 | } | 1134 | } |
1132 | // out of line module, resolve, parse and recurse | 1135 | // out of line module, resolve, parse and recurse |
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 @@ | |||
2 | use base_db::FileId; | 2 | use base_db::FileId; |
3 | use hir_expand::name::Name; | 3 | use hir_expand::name::Name; |
4 | use syntax::SmolStr; | 4 | use syntax::SmolStr; |
5 | use test_utils::mark; | ||
5 | 6 | ||
6 | use crate::{db::DefDatabase, HirFileId}; | 7 | use crate::{db::DefDatabase, HirFileId}; |
7 | 8 | ||
9 | const MOD_DEPTH_LIMIT: u32 = 32; | ||
10 | |||
8 | #[derive(Clone, Debug)] | 11 | #[derive(Clone, Debug)] |
9 | pub(super) struct ModDir { | 12 | pub(super) struct ModDir { |
10 | /// `` for `mod.rs`, `lib.rs` | 13 | /// `` for `mod.rs`, `lib.rs` |
@@ -14,18 +17,28 @@ pub(super) struct ModDir { | |||
14 | dir_path: DirPath, | 17 | dir_path: DirPath, |
15 | /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/` | 18 | /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/` |
16 | root_non_dir_owner: bool, | 19 | root_non_dir_owner: bool, |
20 | depth: u32, | ||
17 | } | 21 | } |
18 | 22 | ||
19 | impl ModDir { | 23 | impl ModDir { |
20 | pub(super) fn root() -> ModDir { | 24 | 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 }) | ||
22 | } | 35 | } |
23 | 36 | ||
24 | pub(super) fn descend_into_definition( | 37 | pub(super) fn descend_into_definition( |
25 | &self, | 38 | &self, |
26 | name: &Name, | 39 | name: &Name, |
27 | attr_path: Option<&SmolStr>, | 40 | attr_path: Option<&SmolStr>, |
28 | ) -> ModDir { | 41 | ) -> Option<ModDir> { |
29 | let path = match attr_path.map(|it| it.as_str()) { | 42 | let path = match attr_path.map(|it| it.as_str()) { |
30 | None => { | 43 | None => { |
31 | let mut path = self.dir_path.clone(); | 44 | let mut path = self.dir_path.clone(); |
@@ -40,7 +53,7 @@ impl ModDir { | |||
40 | DirPath::new(path) | 53 | DirPath::new(path) |
41 | } | 54 | } |
42 | }; | 55 | }; |
43 | ModDir { dir_path: path, root_non_dir_owner: false } | 56 | self.child(path, false) |
44 | } | 57 | } |
45 | 58 | ||
46 | pub(super) fn resolve_declaration( | 59 | pub(super) fn resolve_declaration( |
@@ -72,7 +85,9 @@ impl ModDir { | |||
72 | } else { | 85 | } else { |
73 | (DirPath::new(format!("{}/", name)), true) | 86 | (DirPath::new(format!("{}/", name)), true) |
74 | }; | 87 | }; |
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 | } | ||
76 | } | 91 | } |
77 | } | 92 | } |
78 | Err(candidate_files.remove(0)) | 93 | Err(candidate_files.remove(0)) |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 9c19bf572..a4d1fb8f3 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -20,9 +20,8 @@ fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> { | |||
20 | } | 20 | } |
21 | 21 | ||
22 | fn check(ra_fixture: &str, expect: Expect) { | 22 | fn check(ra_fixture: &str, expect: Expect) { |
23 | let db = TestDB::with_files(ra_fixture); | 23 | let def_map = compute_crate_def_map(ra_fixture); |
24 | let krate = db.crate_graph().iter().next().unwrap(); | 24 | let actual = def_map.dump(); |
25 | let actual = db.crate_def_map(krate).dump(); | ||
26 | expect.assert_eq(&actual); | 25 | expect.assert_eq(&actual); |
27 | } | 26 | } |
28 | 27 | ||
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index ec9d589a3..ba295fd9e 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -771,3 +771,30 @@ struct X; | |||
771 | "#]], | 771 | "#]], |
772 | ); | 772 | ); |
773 | } | 773 | } |
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 | } | ||