aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock32
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs283
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_db/src/input.rs4
-rw-r--r--crates/ra_hir/src/code_model_api.rs21
-rw-r--r--crates/ra_hir/src/code_model_impl/krate.rs4
-rw-r--r--crates/ra_hir/src/code_model_impl/module.rs104
-rw-r--r--crates/ra_hir/src/db.rs44
-rw-r--r--crates/ra_hir/src/ids.rs64
-rw-r--r--crates/ra_hir/src/impl_block.rs5
-rw-r--r--crates/ra_hir/src/lib.rs7
-rw-r--r--crates/ra_hir/src/macros.rs135
-rw-r--r--crates/ra_hir/src/marks.rs4
-rw-r--r--crates/ra_hir/src/module_tree.rs331
-rw-r--r--crates/ra_hir/src/name.rs3
-rw-r--r--crates/ra_hir/src/nameres.rs743
-rw-r--r--crates/ra_hir/src/nameres/collector.rs564
-rw-r--r--crates/ra_hir/src/nameres/lower.rs222
-rw-r--r--crates/ra_hir/src/nameres/per_ns.rs78
-rw-r--r--crates/ra_hir/src/nameres/raw.rs322
-rw-r--r--crates/ra_hir/src/nameres/tests.rs784
-rw-r--r--crates/ra_hir/src/nameres/tests/globs.rs118
-rw-r--r--crates/ra_hir/src/nameres/tests/incremental.rs123
-rw-r--r--crates/ra_hir/src/nameres/tests/macros.rs94
-rw-r--r--crates/ra_hir/src/resolve.rs36
-rw-r--r--crates/ra_hir/src/source_binder.rs46
-rw-r--r--crates/ra_hir/src/ty.rs173
-rw-r--r--crates/ra_hir/src/ty/display.rs56
-rw-r--r--crates/ra_hir/src/ty/infer.rs130
-rw-r--r--crates/ra_hir/src/ty/lower.rs99
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs10
-rw-r--r--crates/ra_hir/src/ty/primitive.rs64
-rw-r--r--crates/ra_hir/src/ty/tests.rs59
-rw-r--r--crates/ra_ide_api/src/change.rs2
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs11
-rw-r--r--crates/ra_ide_api/src/extend_selection.rs46
-rw-r--r--crates/ra_ide_api/src/hover.rs5
-rw-r--r--crates/ra_ide_api/src/runnables.rs2
-rw-r--r--crates/ra_ide_api/src/symbol_index.rs11
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs36
-rw-r--r--crates/ra_ide_api/tests/test/snapshots/test__unresolved_module_diagnostic.snap2
-rw-r--r--crates/ra_mbe/src/lib.rs20
-rw-r--r--crates/ra_mbe/src/mbe_expander.rs14
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs119
-rw-r--r--crates/ra_parser/src/grammar/expressions/atom.rs30
-rw-r--r--crates/ra_parser/src/grammar/items.rs84
-rw-r--r--crates/ra_syntax/src/ast.rs9
-rw-r--r--crates/ra_syntax/src/ast/generated.rs1
-rw-r--r--crates/ra_syntax/src/grammar.ron2
-rw-r--r--crates/ra_syntax/tests/data/parser/err/0032_match_arms_inner_attrs.txt52
-rw-r--r--crates/ra_syntax/tests/data/parser/err/0033_match_arms_outer_attrs.txt28
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0121_match_arms_outer_attributes.txt156
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0125_struct_literal_field_with_attr.rs3
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0125_struct_literal_field_with_attr.txt45
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0048_compound_assignment.rs17
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0048_compound_assignment.txt200
-rw-r--r--crates/test_utils/src/marks.rs4
57 files changed, 3268 insertions, 2395 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0314735da..6f4c4f767 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -62,7 +62,7 @@ name = "backtrace-sys"
62version = "0.1.28" 62version = "0.1.28"
63source = "registry+https://github.com/rust-lang/crates.io-index" 63source = "registry+https://github.com/rust-lang/crates.io-index"
64dependencies = [ 64dependencies = [
65 "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", 65 "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
66 "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", 66 "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
67] 67]
68 68
@@ -118,7 +118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
118 118
119[[package]] 119[[package]]
120name = "cargo_metadata" 120name = "cargo_metadata"
121version = "0.7.1" 121version = "0.7.3"
122source = "registry+https://github.com/rust-lang/crates.io-index" 122source = "registry+https://github.com/rust-lang/crates.io-index"
123dependencies = [ 123dependencies = [
124 "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 124 "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -130,7 +130,7 @@ dependencies = [
130 130
131[[package]] 131[[package]]
132name = "cc" 132name = "cc"
133version = "1.0.30" 133version = "1.0.31"
134source = "registry+https://github.com/rust-lang/crates.io-index" 134source = "registry+https://github.com/rust-lang/crates.io-index"
135 135
136[[package]] 136[[package]]
@@ -265,7 +265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
265dependencies = [ 265dependencies = [
266 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 266 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
267 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 267 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
268 "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", 268 "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
269] 269]
270 270
271[[package]] 271[[package]]
@@ -338,7 +338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
338dependencies = [ 338dependencies = [
339 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 339 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
340 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 340 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
341 "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", 341 "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
342 "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 342 "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
343] 343]
344 344
@@ -563,7 +563,7 @@ name = "jemalloc-sys"
563version = "0.1.8" 563version = "0.1.8"
564source = "registry+https://github.com/rust-lang/crates.io-index" 564source = "registry+https://github.com/rust-lang/crates.io-index"
565dependencies = [ 565dependencies = [
566 "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", 566 "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
567 "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 567 "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
568 "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", 568 "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
569] 569]
@@ -755,7 +755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
755dependencies = [ 755dependencies = [
756 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 756 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
757 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 757 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
758 "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", 758 "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
759] 759]
760 760
761[[package]] 761[[package]]
@@ -847,7 +847,7 @@ dependencies = [
847 "pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 847 "pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
848 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 848 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
849 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 849 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
850 "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", 850 "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
851] 851]
852 852
853[[package]] 853[[package]]
@@ -1083,7 +1083,7 @@ dependencies = [
1083name = "ra_project_model" 1083name = "ra_project_model"
1084version = "0.1.0" 1084version = "0.1.0"
1085dependencies = [ 1085dependencies = [
1086 "cargo_metadata 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 1086 "cargo_metadata 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
1087 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 1087 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
1088 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1088 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
1089 "ra_arena 0.1.0", 1089 "ra_arena 0.1.0",
@@ -1395,7 +1395,7 @@ dependencies = [
1395 "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 1395 "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
1396 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 1396 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
1397 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 1397 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
1398 "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", 1398 "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
1399] 1399]
1400 1400
1401[[package]] 1401[[package]]
@@ -1440,7 +1440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1440dependencies = [ 1440dependencies = [
1441 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 1441 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
1442 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 1442 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
1443 "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", 1443 "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
1444] 1444]
1445 1445
1446[[package]] 1446[[package]]
@@ -1523,7 +1523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1523 1523
1524[[package]] 1524[[package]]
1525name = "syn" 1525name = "syn"
1526version = "0.15.27" 1526version = "0.15.29"
1527source = "registry+https://github.com/rust-lang/crates.io-index" 1527source = "registry+https://github.com/rust-lang/crates.io-index"
1528dependencies = [ 1528dependencies = [
1529 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 1529 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1538,7 +1538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1538dependencies = [ 1538dependencies = [
1539 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 1539 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
1540 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 1540 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
1541 "syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)", 1541 "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
1542 "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1542 "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1543] 1543]
1544 1544
@@ -1905,8 +1905,8 @@ dependencies = [
1905"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" 1905"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
1906"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" 1906"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
1907"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" 1907"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
1908"checksum cargo_metadata 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "585784cac9b05c93a53b17a0b24a5cdd1dfdda5256f030e089b549d2390cc720" 1908"checksum cargo_metadata 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bc796c7161c220089dfc7159e13324979181532850a237576b8fb907dd087c0d"
1909"checksum cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "d01c69d08ff207f231f07196e30f84c70f1c815b04f980f8b7b01ff01f05eb92" 1909"checksum cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ce8bb087aacff865633f0bd5aeaed910fe2fe55b55f4739527f2e023a2e53d"
1910"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" 1910"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
1911"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" 1911"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
1912"checksum ci_info 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e5e881307a989a3a5e20d52a32cc05950e3c2178cccfcc9428271a6cde09f902" 1912"checksum ci_info 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e5e881307a989a3a5e20d52a32cc05950e3c2178cccfcc9428271a6cde09f902"
@@ -2042,7 +2042,7 @@ dependencies = [
2042"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 2042"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
2043"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 2043"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
2044"checksum superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" 2044"checksum superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
2045"checksum syn 0.15.27 (registry+https://github.com/rust-lang/crates.io-index)" = "525bd55255f03c816e5d7f615587bd13030c7103354fadb104993dcee6a788ec" 2045"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2"
2046"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" 2046"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
2047"checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" 2047"checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a"
2048"checksum tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)" = "4b505279e19d8f7d24b1a9dc58327c9c36174b1a2c7ebdeac70792d017cb64f3" 2048"checksum tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)" = "4b505279e19d8f7d24b1a9dc58327c9c36174b1a2c7ebdeac70792d017cb64f3"
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
new file mode 100644
index 000000000..4435c4b5d
--- /dev/null
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -0,0 +1,283 @@
1use crate::{Assist, AssistId, AssistCtx};
2
3use hir::Resolver;
4use hir::db::HirDatabase;
5use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
6use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
7use ra_db::FilePosition;
8use ra_fmt::{leading_indent, reindent};
9
10use itertools::Itertools;
11
12pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
13 let impl_node = ctx.node_at_offset::<ast::ImplBlock>()?;
14 let impl_item_list = impl_node.item_list()?;
15
16 let trait_def = {
17 let file_id = ctx.frange.file_id;
18 let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
19 let resolver = hir::source_binder::resolver_for_position(ctx.db, position);
20
21 resolve_target_trait_def(ctx.db, &resolver, impl_node)?
22 };
23
24 let missing_fns: Vec<_> = {
25 let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None };
26 let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };
27
28 let trait_items =
29 trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items();
30 let impl_items = impl_item_list.impl_items();
31
32 let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
33 let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
34
35 trait_fns
36 .into_iter()
37 .filter(|t| def_name(t).is_some())
38 .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t)))
39 .collect()
40 };
41 if missing_fns.is_empty() {
42 return None;
43 }
44
45 ctx.add_action(AssistId("add_impl_missing_members"), "add missing impl members", |edit| {
46 let (parent_indent, indent) = {
47 // FIXME: Find a way to get the indent already used in the file.
48 // Now, we copy the indent of first item or indent with 4 spaces relative to impl block
49 const DEFAULT_INDENT: &str = " ";
50 let first_item = impl_item_list.impl_items().next();
51 let first_item_indent =
52 first_item.and_then(|i| leading_indent(i.syntax())).map(ToOwned::to_owned);
53 let impl_block_indent = leading_indent(impl_node.syntax()).unwrap_or_default();
54
55 (
56 impl_block_indent.to_owned(),
57 first_item_indent.unwrap_or_else(|| impl_block_indent.to_owned() + DEFAULT_INDENT),
58 )
59 };
60
61 let changed_range = {
62 let children = impl_item_list.syntax().children();
63 let last_whitespace = children.filter_map(ast::Whitespace::cast).last();
64
65 last_whitespace.map(|w| w.syntax().range()).unwrap_or_else(|| {
66 let in_brackets = impl_item_list.syntax().range().end() - TextUnit::of_str("}");
67 TextRange::from_to(in_brackets, in_brackets)
68 })
69 };
70
71 let func_bodies = format!("\n{}", missing_fns.into_iter().map(build_func_body).join("\n"));
72 let trailing_whitespace = format!("\n{}", parent_indent);
73 let func_bodies = reindent(&func_bodies, &indent) + &trailing_whitespace;
74
75 let replaced_text_range = TextUnit::of_str(&func_bodies);
76
77 edit.replace(changed_range, func_bodies);
78 edit.set_cursor(
79 changed_range.start() + replaced_text_range - TextUnit::of_str(&trailing_whitespace),
80 );
81 });
82
83 ctx.build()
84}
85
86/// Given an `ast::ImplBlock`, resolves the target trait (the one being
87/// implemented) to a `ast::TraitDef`.
88fn resolve_target_trait_def(
89 db: &impl HirDatabase,
90 resolver: &Resolver,
91 impl_block: &ast::ImplBlock,
92) -> Option<TreeArc<ast::TraitDef>> {
93 let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?;
94 let hir_path = ast_path.path().and_then(hir::Path::from_ast)?;
95
96 match resolver.resolve_path(db, &hir_path).take_types() {
97 Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1),
98 _ => None,
99 }
100}
101
102fn build_func_body(def: &ast::FnDef) -> String {
103 let mut buf = String::new();
104
105 for child in def.syntax().children() {
106 if child.kind() == SyntaxKind::SEMI {
107 buf.push_str(" { unimplemented!() }")
108 } else {
109 child.text().push_to(&mut buf);
110 }
111 }
112
113 buf.trim_end().to_string()
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use crate::helpers::{check_assist, check_assist_not_applicable};
120
121 #[test]
122 fn test_add_missing_impl_members() {
123 check_assist(
124 add_missing_impl_members,
125 "
126trait Foo {
127 fn foo(&self);
128 fn bar(&self);
129 fn baz(&self);
130}
131
132struct S;
133
134impl Foo for S {
135 fn bar(&self) {}
136 <|>
137}",
138 "
139trait Foo {
140 fn foo(&self);
141 fn bar(&self);
142 fn baz(&self);
143}
144
145struct S;
146
147impl Foo for S {
148 fn bar(&self) {}
149 fn foo(&self) { unimplemented!() }
150 fn baz(&self) { unimplemented!() }<|>
151}",
152 );
153 }
154
155 #[test]
156 fn test_copied_overriden_members() {
157 check_assist(
158 add_missing_impl_members,
159 "
160trait Foo {
161 fn foo(&self);
162 fn bar(&self) -> bool { true }
163 fn baz(&self) -> u32 { 42 }
164}
165
166struct S;
167
168impl Foo for S {
169 fn bar(&self) {}
170 <|>
171}",
172 "
173trait Foo {
174 fn foo(&self);
175 fn bar(&self) -> bool { true }
176 fn baz(&self) -> u32 { 42 }
177}
178
179struct S;
180
181impl Foo for S {
182 fn bar(&self) {}
183 fn foo(&self) { unimplemented!() }
184 fn baz(&self) -> u32 { 42 }<|>
185}",
186 );
187 }
188
189 #[test]
190 fn test_empty_impl_block() {
191 check_assist(
192 add_missing_impl_members,
193 "
194trait Foo { fn foo(&self); }
195struct S;
196impl Foo for S {<|>}",
197 "
198trait Foo { fn foo(&self); }
199struct S;
200impl Foo for S {
201 fn foo(&self) { unimplemented!() }<|>
202}",
203 );
204 }
205
206 #[test]
207 fn test_cursor_after_empty_impl_block() {
208 check_assist(
209 add_missing_impl_members,
210 "
211trait Foo { fn foo(&self); }
212struct S;
213impl Foo for S {}<|>",
214 "
215trait Foo { fn foo(&self); }
216struct S;
217impl Foo for S {
218 fn foo(&self) { unimplemented!() }<|>
219}",
220 )
221 }
222
223 #[test]
224 fn test_empty_trait() {
225 check_assist_not_applicable(
226 add_missing_impl_members,
227 "
228trait Foo;
229struct S;
230impl Foo for S { <|> }",
231 )
232 }
233
234 #[test]
235 fn test_ignore_unnamed_trait_members() {
236 check_assist(
237 add_missing_impl_members,
238 "
239trait Foo {
240 fn (arg: u32);
241 fn valid(some: u32) -> bool { false }
242}
243struct S;
244impl Foo for S { <|> }",
245 "
246trait Foo {
247 fn (arg: u32);
248 fn valid(some: u32) -> bool { false }
249}
250struct S;
251impl Foo for S {
252 fn valid(some: u32) -> bool { false }<|>
253}",
254 )
255 }
256
257 #[test]
258 fn test_indented_impl_block() {
259 check_assist(
260 add_missing_impl_members,
261 "
262trait Foo {
263 fn valid(some: u32) -> bool { false }
264}
265struct S;
266
267mod my_mod {
268 impl crate::Foo for S { <|> }
269}",
270 "
271trait Foo {
272 fn valid(some: u32) -> bool { false }
273}
274struct S;
275
276mod my_mod {
277 impl crate::Foo for S {
278 fn valid(some: u32) -> bool { false }<|>
279 }
280}",
281 )
282 }
283}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 5efc5025e..871b37f58 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -96,6 +96,7 @@ mod replace_if_let_with_match;
96mod split_import; 96mod split_import;
97mod remove_dbg; 97mod remove_dbg;
98mod auto_import; 98mod auto_import;
99mod add_missing_impl_members;
99 100
100fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 101fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
101 &[ 102 &[
@@ -110,6 +111,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
110 split_import::split_import, 111 split_import::split_import,
111 remove_dbg::remove_dbg, 112 remove_dbg::remove_dbg,
112 auto_import::auto_import, 113 auto_import::auto_import,
114 add_missing_impl_members::add_missing_impl_members,
113 ] 115 ]
114} 116}
115 117
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index e45a510b3..2b1001d48 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -124,6 +124,10 @@ impl CrateGraph {
124 self.arena.is_empty() 124 self.arena.is_empty()
125 } 125 }
126 126
127 pub fn iter<'a>(&'a self) -> impl Iterator<Item = CrateId> + 'a {
128 self.arena.keys().map(|it| *it)
129 }
130
127 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 131 pub fn crate_root(&self, crate_id: CrateId) -> FileId {
128 self.arena[&crate_id].file_id 132 self.arena[&crate_id].file_id
129 } 133 }
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
index 87a7195e6..b00481cd5 100644
--- a/crates/ra_hir/src/code_model_api.rs
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -8,13 +8,12 @@ use crate::{
8 Name, ScopesWithSourceMap, Ty, HirFileId, 8 Name, ScopesWithSourceMap, Ty, HirFileId,
9 HirDatabase, PersistentHirDatabase, 9 HirDatabase, PersistentHirDatabase,
10 type_ref::TypeRef, 10 type_ref::TypeRef,
11 nameres::{ModuleScope, Namespace, lower::ImportId}, 11 nameres::{ModuleScope, Namespace, ImportId, CrateModuleId},
12 expr::{Body, BodySourceMap}, 12 expr::{Body, BodySourceMap},
13 ty::InferenceResult, 13 ty::InferenceResult,
14 adt::{EnumVariantId, StructFieldId, VariantDef}, 14 adt::{EnumVariantId, StructFieldId, VariantDef},
15 generics::GenericParams, 15 generics::GenericParams,
16 docs::{Documentation, Docs, docs_from_ast}, 16 docs::{Documentation, Docs, docs_from_ast},
17 module_tree::ModuleId,
18 ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, 17 ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId},
19 impl_block::ImplBlock, 18 impl_block::ImplBlock,
20 resolve::Resolver, 19 resolve::Resolver,
@@ -65,7 +64,7 @@ impl Crate {
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66pub struct Module { 65pub struct Module {
67 pub(crate) krate: Crate, 66 pub(crate) krate: Crate,
68 pub(crate) module_id: ModuleId, 67 pub(crate) module_id: CrateModuleId,
69} 68}
70 69
71/// The defs which can be visible in the module. 70/// The defs which can be visible in the module.
@@ -173,7 +172,7 @@ impl Module {
173 172
174 /// Returns a `ModuleScope`: a set of items, visible in this module. 173 /// Returns a `ModuleScope`: a set of items, visible in this module.
175 pub fn scope(&self, db: &impl HirDatabase) -> ModuleScope { 174 pub fn scope(&self, db: &impl HirDatabase) -> ModuleScope {
176 db.item_map(self.krate)[self.module_id].clone() 175 db.crate_def_map(self.krate)[self.module_id].scope.clone()
177 } 176 }
178 177
179 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> { 178 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
@@ -181,16 +180,16 @@ impl Module {
181 } 180 }
182 181
183 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 182 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
184 let item_map = db.item_map(self.krate); 183 let def_map = db.crate_def_map(self.krate);
185 Resolver::default().push_module_scope(item_map, *self) 184 Resolver::default().push_module_scope(def_map, self.module_id)
186 } 185 }
187 186
188 pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> { 187 pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> {
189 let lowered_module = db.lower_module(self); 188 let def_map = db.crate_def_map(self.krate);
190 lowered_module 189 def_map[self.module_id]
191 .declarations 190 .scope
192 .values() 191 .entries()
193 .cloned() 192 .filter_map(|(_name, res)| if res.import.is_none() { Some(res.def) } else { None })
194 .flat_map(|per_ns| { 193 .flat_map(|per_ns| {
195 per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter()) 194 per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter())
196 }) 195 })
diff --git a/crates/ra_hir/src/code_model_impl/krate.rs b/crates/ra_hir/src/code_model_impl/krate.rs
index 161ae6e18..cc87c6f14 100644
--- a/crates/ra_hir/src/code_model_impl/krate.rs
+++ b/crates/ra_hir/src/code_model_impl/krate.rs
@@ -18,9 +18,7 @@ impl Crate {
18 .collect() 18 .collect()
19 } 19 }
20 pub(crate) fn root_module_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> { 20 pub(crate) fn root_module_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
21 let module_tree = db.module_tree(*self); 21 let module_id = db.crate_def_map(*self).root();
22 let module_id = module_tree.modules().next()?;
23
24 let module = Module { krate: *self, module_id }; 22 let module = Module { krate: *self, module_id };
25 Some(module) 23 Some(module)
26 } 24 }
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs
index 437f96942..f7d15c55e 100644
--- a/crates/ra_hir/src/code_model_impl/module.rs
+++ b/crates/ra_hir/src/code_model_impl/module.rs
@@ -1,33 +1,61 @@
1use ra_syntax::{ast, SyntaxNode, TreeArc}; 1use ra_db::FileId;
2use ra_syntax::{ast, SyntaxNode, TreeArc, AstNode};
2 3
3use crate::{ 4use crate::{
4 Module, ModuleSource, Problem, 5 Module, ModuleSource, Problem, Name,
5 Name, 6 nameres::{CrateModuleId, ImportId},
6 module_tree::ModuleId,
7 nameres::lower::ImportId,
8 HirDatabase, PersistentHirDatabase, 7 HirDatabase, PersistentHirDatabase,
9 HirFileId 8 HirFileId, SourceItemId,
10}; 9};
11 10
11impl ModuleSource {
12 pub(crate) fn new(
13 db: &impl PersistentHirDatabase,
14 file_id: Option<FileId>,
15 decl_id: Option<SourceItemId>,
16 ) -> ModuleSource {
17 match (file_id, decl_id) {
18 (Some(file_id), _) => {
19 let source_file = db.parse(file_id);
20 ModuleSource::SourceFile(source_file)
21 }
22 (None, Some(item_id)) => {
23 let module = db.file_item(item_id);
24 let module = ast::Module::cast(&*module).unwrap();
25 assert!(module.item_list().is_some(), "expected inline module");
26 ModuleSource::Module(module.to_owned())
27 }
28 (None, None) => panic!(),
29 }
30 }
31}
32
12impl Module { 33impl Module {
13 fn with_module_id(&self, module_id: ModuleId) -> Module { 34 fn with_module_id(&self, module_id: CrateModuleId) -> Module {
14 Module { module_id, krate: self.krate } 35 Module { module_id, krate: self.krate }
15 } 36 }
16 37
17 pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Option<Name> { 38 pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Option<Name> {
18 let module_tree = db.module_tree(self.krate); 39 let def_map = db.crate_def_map(self.krate);
19 let link = self.module_id.parent_link(&module_tree)?; 40 let parent = def_map[self.module_id].parent?;
20 Some(link.name(&module_tree).clone()) 41 def_map[parent].children.iter().find_map(|(name, module_id)| {
42 if *module_id == self.module_id {
43 Some(name.clone())
44 } else {
45 None
46 }
47 })
21 } 48 }
22 49
23 pub(crate) fn definition_source_impl( 50 pub(crate) fn definition_source_impl(
24 &self, 51 &self,
25 db: &impl PersistentHirDatabase, 52 db: &impl PersistentHirDatabase,
26 ) -> (HirFileId, ModuleSource) { 53 ) -> (HirFileId, ModuleSource) {
27 let module_tree = db.module_tree(self.krate); 54 let def_map = db.crate_def_map(self.krate);
28 let file_id = self.module_id.file_id(&module_tree); 55 let decl_id = def_map[self.module_id].declaration;
29 let decl_id = self.module_id.decl_id(&module_tree); 56 let file_id = def_map[self.module_id].definition;
30 let module_source = ModuleSource::new(db, file_id, decl_id); 57 let module_source = ModuleSource::new(db, file_id, decl_id);
58 let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id);
31 (file_id, module_source) 59 (file_id, module_source)
32 } 60 }
33 61
@@ -35,11 +63,11 @@ impl Module {
35 &self, 63 &self,
36 db: &impl HirDatabase, 64 db: &impl HirDatabase,
37 ) -> Option<(HirFileId, TreeArc<ast::Module>)> { 65 ) -> Option<(HirFileId, TreeArc<ast::Module>)> {
38 let module_tree = db.module_tree(self.krate); 66 let def_map = db.crate_def_map(self.krate);
39 let link = self.module_id.parent_link(&module_tree)?; 67 let decl = def_map[self.module_id].declaration?;
40 let file_id = link.owner(&module_tree).file_id(&module_tree); 68 let syntax_node = db.file_item(decl);
41 let src = link.source(&module_tree, db); 69 let ast = ast::Module::cast(&syntax_node).unwrap().to_owned();
42 Some((file_id, src)) 70 Some((decl.file_id, ast))
43 } 71 }
44 72
45 pub(crate) fn import_source_impl( 73 pub(crate) fn import_source_impl(
@@ -47,22 +75,21 @@ impl Module {
47 db: &impl HirDatabase, 75 db: &impl HirDatabase,
48 import: ImportId, 76 import: ImportId,
49 ) -> TreeArc<ast::PathSegment> { 77 ) -> TreeArc<ast::PathSegment> {
50 let (_, source_map) = db.lower_module_with_source_map(*self); 78 let (file_id, source) = self.definition_source(db);
51 let (_, source) = self.definition_source(db); 79 let (_, source_map) = db.raw_items_with_source_map(file_id.original_file(db));
52 source_map.get(&source, import) 80 source_map.get(&source, import)
53 } 81 }
54 82
55 pub(crate) fn crate_root_impl(&self, db: &impl PersistentHirDatabase) -> Module { 83 pub(crate) fn crate_root_impl(&self, db: &impl PersistentHirDatabase) -> Module {
56 let module_tree = db.module_tree(self.krate); 84 let def_map = db.crate_def_map(self.krate);
57 let module_id = self.module_id.crate_root(&module_tree); 85 self.with_module_id(def_map.root())
58 self.with_module_id(module_id)
59 } 86 }
60 87
61 /// Finds a child module with the specified name. 88 /// Finds a child module with the specified name.
62 pub(crate) fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option<Module> { 89 pub(crate) fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option<Module> {
63 let module_tree = db.module_tree(self.krate); 90 let def_map = db.crate_def_map(self.krate);
64 let child_id = self.module_id.child(&module_tree, name)?; 91 let child_id = def_map[self.module_id].children.get(name)?;
65 Some(self.with_module_id(child_id)) 92 Some(self.with_module_id(*child_id))
66 } 93 }
67 94
68 /// Iterates over all child modules. 95 /// Iterates over all child modules.
@@ -70,18 +97,18 @@ impl Module {
70 &self, 97 &self,
71 db: &impl PersistentHirDatabase, 98 db: &impl PersistentHirDatabase,
72 ) -> impl Iterator<Item = Module> { 99 ) -> impl Iterator<Item = Module> {
73 let module_tree = db.module_tree(self.krate); 100 let def_map = db.crate_def_map(self.krate);
74 let children = self 101 let children = def_map[self.module_id]
75 .module_id 102 .children
76 .children(&module_tree) 103 .iter()
77 .map(|(_, module_id)| self.with_module_id(module_id)) 104 .map(|(_, module_id)| self.with_module_id(*module_id))
78 .collect::<Vec<_>>(); 105 .collect::<Vec<_>>();
79 children.into_iter() 106 children.into_iter()
80 } 107 }
81 108
82 pub(crate) fn parent_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> { 109 pub(crate) fn parent_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
83 let module_tree = db.module_tree(self.krate); 110 let def_map = db.crate_def_map(self.krate);
84 let parent_id = self.module_id.parent(&module_tree)?; 111 let parent_id = def_map[self.module_id].parent?;
85 Some(self.with_module_id(parent_id)) 112 Some(self.with_module_id(parent_id))
86 } 113 }
87 114
@@ -89,7 +116,14 @@ impl Module {
89 &self, 116 &self,
90 db: &impl HirDatabase, 117 db: &impl HirDatabase,
91 ) -> Vec<(TreeArc<SyntaxNode>, Problem)> { 118 ) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
92 let module_tree = db.module_tree(self.krate); 119 let def_map = db.crate_def_map(self.krate);
93 self.module_id.problems(&module_tree, db) 120 let (my_file_id, _) = self.definition_source(db);
121 // FIXME: not entirely corret filterint by module
122 def_map
123 .problems()
124 .iter()
125 .filter(|(source_item_id, _problem)| my_file_id == source_item_id.file_id)
126 .map(|(source_item_id, problem)| (db.file_item(*source_item_id), problem.clone()))
127 .collect()
94 } 128 }
95} 129}
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 21d22aa7f..c7bad7e2b 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -1,23 +1,18 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::{SyntaxNode, TreeArc, SourceFile}; 3use ra_syntax::{SyntaxNode, TreeArc, SourceFile};
4use ra_db::{SourceDatabase, salsa}; 4use ra_db::{SourceDatabase, salsa, FileId};
5 5
6use crate::{ 6use crate::{
7 MacroCallId, HirFileId, 7 HirFileId, SourceFileItems, SourceItemId, Crate, Module, HirInterner,
8 SourceFileItems, SourceItemId, Crate, Module, HirInterner,
9 Function, FnSignature, ExprScopes, TypeAlias, 8 Function, FnSignature, ExprScopes, TypeAlias,
10 Struct, Enum, StructField, 9 Struct, Enum, StructField,
11 Const, ConstSignature, Static, 10 Const, ConstSignature, Static,
12 macros::MacroExpansion, 11 nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap},
13 module_tree::ModuleTree, 12 ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig},
14 nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}},
15 ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef},
16 adt::{StructData, EnumData}, 13 adt::{StructData, EnumData},
17 impl_block::{ModuleImplBlocks, ImplSourceMap}, 14 impl_block::{ModuleImplBlocks, ImplSourceMap},
18 generics::{GenericParams, GenericDef}, 15 generics::{GenericParams, GenericDef},
19 ids::SourceFileItemId,
20 nameres::Namespace,
21 type_ref::TypeRef, 16 type_ref::TypeRef,
22}; 17};
23 18
@@ -26,9 +21,6 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> {
26 #[salsa::invoke(HirFileId::hir_parse)] 21 #[salsa::invoke(HirFileId::hir_parse)]
27 fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>; 22 fn hir_parse(&self, file_id: HirFileId) -> TreeArc<SourceFile>;
28 23
29 #[salsa::invoke(crate::macros::expand_macro_invocation)]
30 fn expand_macro_invocation(&self, invoc: MacroCallId) -> Option<Arc<MacroExpansion>>;
31
32 #[salsa::invoke(crate::adt::StructData::struct_data_query)] 24 #[salsa::invoke(crate::adt::StructData::struct_data_query)]
33 fn struct_data(&self, s: Struct) -> Arc<StructData>; 25 fn struct_data(&self, s: Struct) -> Arc<StructData>;
34 26
@@ -41,27 +33,14 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> {
41 #[salsa::invoke(crate::ids::SourceFileItems::file_item_query)] 33 #[salsa::invoke(crate::ids::SourceFileItems::file_item_query)]
42 fn file_item(&self, source_item_id: SourceItemId) -> TreeArc<SyntaxNode>; 34 fn file_item(&self, source_item_id: SourceItemId) -> TreeArc<SyntaxNode>;
43 35
44 #[salsa::invoke(crate::module_tree::Submodule::submodules_query)] 36 #[salsa::invoke(RawItems::raw_items_query)]
45 fn submodules( 37 fn raw_items(&self, file_id: FileId) -> Arc<RawItems>;
46 &self,
47 file_id: HirFileId,
48 delc_id: Option<SourceFileItemId>,
49 ) -> Arc<Vec<crate::module_tree::Submodule>>;
50 38
51 #[salsa::invoke(crate::nameres::lower::LoweredModule::lower_module_with_source_map_query)] 39 #[salsa::invoke(RawItems::raw_items_with_source_map_query)]
52 fn lower_module_with_source_map( 40 fn raw_items_with_source_map(&self, file_id: FileId) -> (Arc<RawItems>, Arc<ImportSourceMap>);
53 &self,
54 module: Module,
55 ) -> (Arc<LoweredModule>, Arc<ImportSourceMap>);
56 41
57 #[salsa::invoke(crate::nameres::lower::LoweredModule::lower_module_query)] 42 #[salsa::invoke(CrateDefMap::crate_def_map_query)]
58 fn lower_module(&self, module: Module) -> Arc<LoweredModule>; 43 fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>;
59
60 #[salsa::invoke(crate::nameres::ItemMap::item_map_query)]
61 fn item_map(&self, krate: Crate) -> Arc<ItemMap>;
62
63 #[salsa::invoke(crate::module_tree::ModuleTree::module_tree_query)]
64 fn module_tree(&self, krate: Crate) -> Arc<ModuleTree>;
65 44
66 #[salsa::invoke(crate::impl_block::impls_in_module)] 45 #[salsa::invoke(crate::impl_block::impls_in_module)]
67 fn impls_in_module(&self, module: Module) -> Arc<ModuleImplBlocks>; 46 fn impls_in_module(&self, module: Module) -> Arc<ModuleImplBlocks>;
@@ -105,6 +84,9 @@ pub trait HirDatabase: PersistentHirDatabase {
105 #[salsa::invoke(crate::ty::type_for_field)] 84 #[salsa::invoke(crate::ty::type_for_field)]
106 fn type_for_field(&self, field: StructField) -> Ty; 85 fn type_for_field(&self, field: StructField) -> Ty;
107 86
87 #[salsa::invoke(crate::ty::callable_item_sig)]
88 fn callable_item_signature(&self, def: CallableDef) -> FnSig;
89
108 #[salsa::invoke(crate::expr::body_with_source_map_query)] 90 #[salsa::invoke(crate::expr::body_with_source_map_query)]
109 fn body_with_source_map( 91 fn body_with_source_map(
110 &self, 92 &self,
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index 5b00330c6..9596488d3 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -83,30 +83,37 @@ impl HirFileId {
83 } 83 }
84 } 84 }
85 85
86 pub(crate) fn as_macro_call_id(self) -> Option<MacroCallId> {
87 match self.0 {
88 HirFileIdRepr::Macro(it) => Some(it),
89 _ => None,
90 }
91 }
92
93 pub(crate) fn hir_parse( 86 pub(crate) fn hir_parse(
94 db: &impl PersistentHirDatabase, 87 db: &impl PersistentHirDatabase,
95 file_id: HirFileId, 88 file_id: HirFileId,
96 ) -> TreeArc<SourceFile> { 89 ) -> TreeArc<SourceFile> {
97 match file_id.0 { 90 match file_id.0 {
98 HirFileIdRepr::File(file_id) => db.parse(file_id), 91 HirFileIdRepr::File(file_id) => db.parse(file_id),
99 HirFileIdRepr::Macro(m) => { 92 HirFileIdRepr::Macro(macro_call_id) => {
100 if let Some(exp) = db.expand_macro_invocation(m) {
101 return exp.file();
102 }
103 // returning an empty string looks fishy... 93 // returning an empty string looks fishy...
104 SourceFile::parse("") 94 parse_macro(db, macro_call_id).unwrap_or_else(|| SourceFile::parse(""))
105 } 95 }
106 } 96 }
107 } 97 }
108} 98}
109 99
100fn parse_macro(
101 db: &impl PersistentHirDatabase,
102 macro_call_id: MacroCallId,
103) -> Option<TreeArc<SourceFile>> {
104 let loc = macro_call_id.loc(db);
105 let syntax = db.file_item(loc.source_item_id);
106 let macro_call = ast::MacroCall::cast(&syntax).unwrap();
107 let (macro_arg, _) = macro_call.token_tree().and_then(mbe::ast_to_token_tree)?;
108
109 let def_map = db.crate_def_map(loc.module.krate);
110 let (krate, macro_id) = def_map.resolve_macro(macro_call_id)?;
111 let def_map = db.crate_def_map(krate);
112 let macro_rules = &def_map[macro_id];
113 let tt = macro_rules.expand(&macro_arg).ok()?;
114 Some(mbe::token_tree_to_ast_item_list(&tt))
115}
116
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111enum HirFileIdRepr { 118enum HirFileIdRepr {
112 File(FileId), 119 File(FileId),
@@ -200,8 +207,14 @@ pub(crate) trait AstItemDef<N: AstNode>: ArenaId + Clone {
200 fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<N>, Self>; 207 fn interner(interner: &HirInterner) -> &LocationIntener<ItemLoc<N>, Self>;
201 fn from_ast(ctx: LocationCtx<&impl PersistentHirDatabase>, ast: &N) -> Self { 208 fn from_ast(ctx: LocationCtx<&impl PersistentHirDatabase>, ast: &N) -> Self {
202 let items = ctx.db.file_items(ctx.file_id); 209 let items = ctx.db.file_items(ctx.file_id);
203 let raw = 210 let item_id = items.id_of(ctx.file_id, ast.syntax());
204 SourceItemId { file_id: ctx.file_id, item_id: items.id_of(ctx.file_id, ast.syntax()) }; 211 Self::from_source_item_id_unchecked(ctx, item_id)
212 }
213 fn from_source_item_id_unchecked(
214 ctx: LocationCtx<&impl PersistentHirDatabase>,
215 item_id: SourceFileItemId,
216 ) -> Self {
217 let raw = SourceItemId { file_id: ctx.file_id, item_id };
205 let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData }; 218 let loc = ItemLoc { module: ctx.module, raw, _ty: PhantomData };
206 219
207 Self::interner(ctx.db.as_ref()).loc2id(&loc) 220 Self::interner(ctx.db.as_ref()).loc2id(&loc)
@@ -290,6 +303,12 @@ impl AstItemDef<ast::TypeAliasDef> for TypeId {
290pub struct SourceFileItemId(RawId); 303pub struct SourceFileItemId(RawId);
291impl_arena_id!(SourceFileItemId); 304impl_arena_id!(SourceFileItemId);
292 305
306impl SourceFileItemId {
307 pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId {
308 SourceItemId { file_id, item_id: self }
309 }
310}
311
293#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 312#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
294pub struct SourceItemId { 313pub struct SourceItemId {
295 pub(crate) file_id: HirFileId, 314 pub(crate) file_id: HirFileId,
@@ -309,9 +328,7 @@ impl SourceFileItems {
309 file_id: HirFileId, 328 file_id: HirFileId,
310 ) -> Arc<SourceFileItems> { 329 ) -> Arc<SourceFileItems> {
311 let source_file = db.hir_parse(file_id); 330 let source_file = db.hir_parse(file_id);
312 let mut res = SourceFileItems { file_id, arena: Arena::default() }; 331 Arc::new(SourceFileItems::from_source_file(&source_file, file_id))
313 res.init(&source_file);
314 Arc::new(res)
315 } 332 }
316 333
317 pub(crate) fn file_item_query( 334 pub(crate) fn file_item_query(
@@ -324,18 +341,23 @@ impl SourceFileItems {
324 .to_owned() 341 .to_owned()
325 } 342 }
326 343
327 fn init(&mut self, source_file: &SourceFile) { 344 pub(crate) fn from_source_file(
345 source_file: &SourceFile,
346 file_id: HirFileId,
347 ) -> SourceFileItems {
348 let mut res = SourceFileItems { file_id, arena: Arena::default() };
328 // By walking the tree in bread-first order we make sure that parents 349 // By walking the tree in bread-first order we make sure that parents
329 // get lower ids then children. That is, adding a new child does not 350 // get lower ids then children. That is, adding a new child does not
330 // change parent's id. This means that, say, adding a new function to a 351 // change parent's id. This means that, say, adding a new function to a
331 // trait does not change ids of top-level items, which helps caching. 352 // trait does not change ids of top-level items, which helps caching.
332 bfs(source_file.syntax(), |it| { 353 bfs(source_file.syntax(), |it| {
333 if let Some(module_item) = ast::ModuleItem::cast(it) { 354 if let Some(module_item) = ast::ModuleItem::cast(it) {
334 self.alloc(module_item.syntax()); 355 res.alloc(module_item.syntax());
335 } else if let Some(macro_call) = ast::MacroCall::cast(it) { 356 } else if let Some(macro_call) = ast::MacroCall::cast(it) {
336 self.alloc(macro_call.syntax()); 357 res.alloc(macro_call.syntax());
337 } 358 }
338 }) 359 });
360 res
339 } 361 }
340 362
341 fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { 363 fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId {
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs
index eb2d4ed8d..8807a4b56 100644
--- a/crates/ra_hir/src/impl_block.rs
+++ b/crates/ra_hir/src/impl_block.rs
@@ -4,7 +4,8 @@ use rustc_hash::FxHashMap;
4use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; 4use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
5use ra_syntax::{ 5use ra_syntax::{
6 AstPtr, SourceFile, TreeArc, 6 AstPtr, SourceFile, TreeArc,
7ast::{self, AstNode}}; 7 ast::{self, AstNode}
8};
8 9
9use crate::{ 10use crate::{
10 Const, TypeAlias, Function, HirFileId, 11 Const, TypeAlias, Function, HirFileId,
@@ -13,7 +14,7 @@ use crate::{
13 type_ref::TypeRef, 14 type_ref::TypeRef,
14 ids::LocationCtx, 15 ids::LocationCtx,
15 resolve::Resolver, 16 resolve::Resolver,
16 ty::Ty, generics::GenericParams 17 ty::Ty, generics::GenericParams,
17}; 18};
18 19
19use crate::code_model_api::{Module, ModuleSource}; 20use crate::code_model_api::{Module, ModuleSource};
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index ac5f33079..75c977d32 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -24,9 +24,7 @@ mod path;
24pub mod source_binder; 24pub mod source_binder;
25 25
26mod ids; 26mod ids;
27mod macros;
28mod name; 27mod name;
29mod module_tree;
30mod nameres; 28mod nameres;
31mod adt; 29mod adt;
32mod type_alias; 30mod type_alias;
@@ -54,9 +52,8 @@ pub use self::{
54 path::{Path, PathKind}, 52 path::{Path, PathKind},
55 name::Name, 53 name::Name,
56 ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner}, 54 ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner},
57 macros::{MacroDef, MacroInput, MacroExpansion}, 55 nameres::{PerNs, Namespace},
58 nameres::{ItemMap, PerNs, Namespace}, 56 ty::{Ty, Substs, display::HirDisplay},
59 ty::{Ty, Substs},
60 impl_block::{ImplBlock, ImplItem}, 57 impl_block::{ImplBlock, ImplItem},
61 docs::{Docs, Documentation}, 58 docs::{Docs, Documentation},
62 adt::AdtDef, 59 adt::AdtDef,
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs
deleted file mode 100644
index 45128c7df..000000000
--- a/crates/ra_hir/src/macros.rs
+++ /dev/null
@@ -1,135 +0,0 @@
1/// Machinery for macro expansion.
2///
3/// One of the more complicated things about macros is managing the source code
4/// that is produced after expansion. See `HirFileId` and `MacroCallId` for how
5/// do we do that.
6///
7/// When the file-management question is resolved, all that is left is a
8/// token-tree-to-token-tree transformation plus hygiene. We don't have either of
9/// those yet, so all macros are string based at the moment!
10use std::sync::Arc;
11
12use ra_syntax::{
13 TextRange, TextUnit, SourceFile, AstNode, SyntaxNode, TreeArc, SyntaxNodePtr,
14 ast,
15};
16
17use crate::{MacroCallId, PersistentHirDatabase};
18
19// Hard-coded defs for now :-(
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum MacroDef {
22 Vec,
23}
24
25impl MacroDef {
26 /// Expands macro call, returning the expansion and offset to be used to
27 /// convert ranges between expansion and original source.
28 pub fn ast_expand(macro_call: &ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> {
29 let (def, input) = MacroDef::from_call(macro_call)?;
30 let exp = def.expand(input)?;
31 let off = macro_call.token_tree()?.syntax().range().start();
32 Some((off, exp))
33 }
34
35 fn from_call(macro_call: &ast::MacroCall) -> Option<(MacroDef, MacroInput)> {
36 let def = {
37 let path = macro_call.path()?;
38 let name_ref = path.segment()?.name_ref()?;
39 if name_ref.text() == "vec" {
40 MacroDef::Vec
41 } else {
42 return None;
43 }
44 };
45
46 let input = {
47 let arg = macro_call.token_tree()?.syntax();
48 MacroInput { text: arg.text().to_string() }
49 };
50 Some((def, input))
51 }
52
53 fn expand(self, input: MacroInput) -> Option<MacroExpansion> {
54 match self {
55 MacroDef::Vec => self.expand_vec(input),
56 }
57 }
58 fn expand_vec(self, input: MacroInput) -> Option<MacroExpansion> {
59 let text = format!(r"fn dummy() {{ {}; }}", input.text);
60 let file = SourceFile::parse(&text);
61 let array_expr = file.syntax().descendants().find_map(ast::ArrayExpr::cast)?;
62 let ptr = SyntaxNodePtr::new(array_expr.syntax());
63 let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text));
64 let ranges_map = vec![(src_range, array_expr.syntax().range())];
65 let res = MacroExpansion { text, ranges_map, ptr };
66 Some(res)
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct MacroInput {
72 // Should be token trees
73 pub text: String,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct MacroExpansion {
78 /// The result of macro expansion. Should be token tree as well.
79 text: String,
80 /// Correspondence between ranges in the original source code and ranges in
81 /// the macro.
82 ranges_map: Vec<(TextRange, TextRange)>,
83 /// Implementation detail: internally, a macro is expanded to the whole file,
84 /// even if it is an expression. This `ptr` selects the actual expansion from
85 /// the expanded file.
86 ptr: SyntaxNodePtr,
87}
88
89impl MacroExpansion {
90 // FIXME: does not really make sense, macro expansion is not necessary a
91 // whole file. See `MacroExpansion::ptr` as well.
92 pub(crate) fn file(&self) -> TreeArc<SourceFile> {
93 SourceFile::parse(&self.text)
94 }
95
96 pub fn syntax(&self) -> TreeArc<SyntaxNode> {
97 self.ptr.to_node(&self.file()).to_owned()
98 }
99 /// Maps range in the source code to the range in the expanded code.
100 pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
101 for (s_range, t_range) in self.ranges_map.iter() {
102 if src_range.is_subrange(&s_range) {
103 let src_at_zero_range = src_range - src_range.start();
104 let src_range_offset = src_range.start() - s_range.start();
105 let src_range = src_at_zero_range + src_range_offset + t_range.start();
106 return Some(src_range);
107 }
108 }
109 None
110 }
111 /// Maps range in the expanded code to the range in the source code.
112 pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
113 for (s_range, t_range) in self.ranges_map.iter() {
114 if tgt_range.is_subrange(&t_range) {
115 let tgt_at_zero_range = tgt_range - tgt_range.start();
116 let tgt_range_offset = tgt_range.start() - t_range.start();
117 let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
118 return Some(src_range);
119 }
120 }
121 None
122 }
123}
124
125pub(crate) fn expand_macro_invocation(
126 db: &impl PersistentHirDatabase,
127 invoc: MacroCallId,
128) -> Option<Arc<MacroExpansion>> {
129 let loc = invoc.loc(db);
130 let syntax = db.file_item(loc.source_item_id);
131 let macro_call = ast::MacroCall::cast(&syntax).unwrap();
132
133 let (def, input) = MacroDef::from_call(macro_call)?;
134 def.expand(input).map(Arc::new)
135}
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index 16852a6a1..5b6400042 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -1,10 +1,12 @@
1test_utils::marks!( 1test_utils::marks!(
2 bogus_paths
2 name_res_works_for_broken_modules 3 name_res_works_for_broken_modules
3 item_map_enum_importing 4 can_import_enum_variant
4 type_var_cycles_resolve_completely 5 type_var_cycles_resolve_completely
5 type_var_cycles_resolve_as_possible 6 type_var_cycles_resolve_as_possible
6 type_var_resolves_to_int_var 7 type_var_resolves_to_int_var
7 glob_enum 8 glob_enum
8 glob_across_crates 9 glob_across_crates
9 std_prelude 10 std_prelude
11 match_ergonomics_ref
10); 12);
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs
index 99c2115e1..e69de29bb 100644
--- a/crates/ra_hir/src/module_tree.rs
+++ b/crates/ra_hir/src/module_tree.rs
@@ -1,331 +0,0 @@
1use std::sync::Arc;
2
3use arrayvec::ArrayVec;
4use relative_path::RelativePathBuf;
5use ra_db::{FileId, SourceRoot};
6use ra_syntax::{
7 SyntaxNode, TreeArc,
8 algo::generate,
9 ast::{self, AstNode, NameOwner},
10};
11use ra_arena::{Arena, RawId, impl_arena_id};
12use test_utils::tested_by;
13
14use crate::{
15 Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource,
16 PersistentHirDatabase,
17 Crate,
18 ids::SourceFileItemId,
19};
20
21impl ModuleSource {
22 pub(crate) fn new(
23 db: &impl PersistentHirDatabase,
24 file_id: HirFileId,
25 decl_id: Option<SourceFileItemId>,
26 ) -> ModuleSource {
27 match decl_id {
28 Some(item_id) => {
29 let module = db.file_item(SourceItemId { file_id, item_id });
30 let module = ast::Module::cast(&*module).unwrap();
31 assert!(module.item_list().is_some(), "expected inline module");
32 ModuleSource::Module(module.to_owned())
33 }
34 None => {
35 let source_file = db.hir_parse(file_id);
36 ModuleSource::SourceFile(source_file)
37 }
38 }
39 }
40}
41
42#[derive(Clone, Hash, PartialEq, Eq, Debug)]
43pub struct Submodule {
44 name: Name,
45 is_declaration: bool,
46 decl_id: SourceFileItemId,
47}
48
49impl Submodule {
50 pub(crate) fn submodules_query(
51 db: &impl PersistentHirDatabase,
52 file_id: HirFileId,
53 decl_id: Option<SourceFileItemId>,
54 ) -> Arc<Vec<Submodule>> {
55 db.check_canceled();
56 let file_items = db.file_items(file_id);
57 let module_source = ModuleSource::new(db, file_id, decl_id);
58 let submodules = match module_source {
59 ModuleSource::SourceFile(source_file) => {
60 collect_submodules(file_id, &file_items, &*source_file)
61 }
62 ModuleSource::Module(module) => {
63 collect_submodules(file_id, &file_items, module.item_list().unwrap())
64 }
65 };
66
67 return Arc::new(submodules);
68
69 fn collect_submodules(
70 file_id: HirFileId,
71 file_items: &SourceFileItems,
72 root: &impl ast::ModuleItemOwner,
73 ) -> Vec<Submodule> {
74 root.items()
75 .filter_map(|item| match item.kind() {
76 ast::ModuleItemKind::Module(m) => Some(m),
77 _ => None,
78 })
79 .filter_map(|module| {
80 let name = module.name()?.as_name();
81 if !module.has_semi() && module.item_list().is_none() {
82 tested_by!(name_res_works_for_broken_modules);
83 return None;
84 }
85 let sub = Submodule {
86 name,
87 is_declaration: module.has_semi(),
88 decl_id: file_items.id_of(file_id, module.syntax()),
89 };
90 Some(sub)
91 })
92 .collect()
93 }
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
98pub struct ModuleId(RawId);
99impl_arena_id!(ModuleId);
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
102pub struct LinkId(RawId);
103impl_arena_id!(LinkId);
104
105/// Physically, rust source is organized as a set of files, but logically it is
106/// organized as a tree of modules. Usually, a single file corresponds to a
107/// single module, but it is not neccessarily always the case.
108///
109/// `ModuleTree` encapsulates the logic of transitioning from the fuzzy world of files
110/// (which can have multiple parents) to the precise world of modules (which
111/// always have one parent).
112#[derive(Default, Debug, PartialEq, Eq)]
113pub struct ModuleTree {
114 mods: Arena<ModuleId, ModuleData>,
115 links: Arena<LinkId, LinkData>,
116}
117
118#[derive(Debug, PartialEq, Eq)]
119pub struct ModuleData {
120 file_id: HirFileId,
121 /// Points to `ast::Module`, `None` for the whole file.
122 decl_id: Option<SourceFileItemId>,
123 parent: Option<LinkId>,
124 children: Vec<LinkId>,
125}
126
127#[derive(Hash, Debug, PartialEq, Eq)]
128struct LinkData {
129 source: SourceItemId,
130 owner: ModuleId,
131 name: Name,
132 points_to: Vec<ModuleId>,
133 problem: Option<Problem>,
134}
135
136impl ModuleTree {
137 pub(crate) fn module_tree_query(
138 db: &impl PersistentHirDatabase,
139 krate: Crate,
140 ) -> Arc<ModuleTree> {
141 db.check_canceled();
142 let mut res = ModuleTree::default();
143 res.init_crate(db, krate);
144 Arc::new(res)
145 }
146
147 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
148 self.mods.iter().map(|(id, _)| id)
149 }
150
151 pub(crate) fn find_module_by_source(
152 &self,
153 file_id: HirFileId,
154 decl_id: Option<SourceFileItemId>,
155 ) -> Option<ModuleId> {
156 let (res, _) =
157 self.mods.iter().find(|(_, m)| (m.file_id, m.decl_id) == (file_id, decl_id))?;
158 Some(res)
159 }
160
161 fn init_crate(&mut self, db: &impl PersistentHirDatabase, krate: Crate) {
162 let crate_graph = db.crate_graph();
163 let file_id = crate_graph.crate_root(krate.crate_id);
164 let source_root_id = db.file_source_root(file_id);
165
166 let source_root = db.source_root(source_root_id);
167 self.init_subtree(db, &source_root, None, file_id.into(), None);
168 }
169
170 fn init_subtree(
171 &mut self,
172 db: &impl PersistentHirDatabase,
173 source_root: &SourceRoot,
174 parent: Option<LinkId>,
175 file_id: HirFileId,
176 decl_id: Option<SourceFileItemId>,
177 ) -> ModuleId {
178 let is_root = parent.is_none();
179 let id = self.alloc_mod(ModuleData { file_id, decl_id, parent, children: Vec::new() });
180 for sub in db.submodules(file_id, decl_id).iter() {
181 let link = self.alloc_link(LinkData {
182 source: SourceItemId { file_id, item_id: sub.decl_id },
183 name: sub.name.clone(),
184 owner: id,
185 points_to: Vec::new(),
186 problem: None,
187 });
188
189 let (points_to, problem) = if sub.is_declaration {
190 let (points_to, problem) = resolve_submodule(db, file_id, &sub.name, is_root);
191 let points_to = points_to
192 .into_iter()
193 .map(|file_id| {
194 self.init_subtree(db, source_root, Some(link), file_id.into(), None)
195 })
196 .collect::<Vec<_>>();
197 (points_to, problem)
198 } else {
199 let points_to =
200 self.init_subtree(db, source_root, Some(link), file_id, Some(sub.decl_id));
201 (vec![points_to], None)
202 };
203
204 self.links[link].points_to = points_to;
205 self.links[link].problem = problem;
206 }
207 id
208 }
209
210 fn alloc_mod(&mut self, data: ModuleData) -> ModuleId {
211 self.mods.alloc(data)
212 }
213
214 fn alloc_link(&mut self, data: LinkData) -> LinkId {
215 let owner = data.owner;
216 let id = self.links.alloc(data);
217 self.mods[owner].children.push(id);
218 id
219 }
220}
221
222impl ModuleId {
223 pub(crate) fn file_id(self, tree: &ModuleTree) -> HirFileId {
224 tree.mods[self].file_id
225 }
226 pub(crate) fn decl_id(self, tree: &ModuleTree) -> Option<SourceFileItemId> {
227 tree.mods[self].decl_id
228 }
229 pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
230 tree.mods[self].parent
231 }
232 pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
233 let link = self.parent_link(tree)?;
234 Some(tree.links[link].owner)
235 }
236 pub(crate) fn crate_root(self, tree: &ModuleTree) -> ModuleId {
237 generate(Some(self), move |it| it.parent(tree)).last().unwrap()
238 }
239 pub(crate) fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
240 let link = tree.mods[self]
241 .children
242 .iter()
243 .map(|&it| &tree.links[it])
244 .find(|it| it.name == *name)?;
245 Some(*link.points_to.first()?)
246 }
247 pub(crate) fn children<'a>(
248 self,
249 tree: &'a ModuleTree,
250 ) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
251 tree.mods[self].children.iter().filter_map(move |&it| {
252 let link = &tree.links[it];
253 let module = *link.points_to.first()?;
254 Some((link.name.clone(), module))
255 })
256 }
257 pub(crate) fn problems(
258 self,
259 tree: &ModuleTree,
260 db: &impl HirDatabase,
261 ) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
262 tree.mods[self]
263 .children
264 .iter()
265 .filter_map(|&link| {
266 let p = tree.links[link].problem.clone()?;
267 let s = link.source(tree, db);
268 let s = s.name().unwrap().syntax().to_owned();
269 Some((s, p))
270 })
271 .collect()
272 }
273}
274
275impl LinkId {
276 pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
277 tree.links[self].owner
278 }
279 pub(crate) fn name(self, tree: &ModuleTree) -> &Name {
280 &tree.links[self].name
281 }
282 pub(crate) fn source(
283 self,
284 tree: &ModuleTree,
285 db: &impl PersistentHirDatabase,
286 ) -> TreeArc<ast::Module> {
287 let syntax_node = db.file_item(tree.links[self].source);
288 ast::Module::cast(&syntax_node).unwrap().to_owned()
289 }
290}
291
292fn resolve_submodule(
293 db: &impl PersistentHirDatabase,
294 file_id: HirFileId,
295 name: &Name,
296 is_root: bool,
297) -> (Vec<FileId>, Option<Problem>) {
298 // FIXME: handle submodules of inline modules properly
299 let file_id = file_id.original_file(db);
300 let source_root_id = db.file_source_root(file_id);
301 let path = db.file_relative_path(file_id);
302 let root = RelativePathBuf::default();
303 let dir_path = path.parent().unwrap_or(&root);
304 let mod_name = path.file_stem().unwrap_or("unknown");
305 let is_dir_owner = is_root || mod_name == "mod";
306
307 let file_mod = dir_path.join(format!("{}.rs", name));
308 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
309 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
310 let mut candidates = ArrayVec::<[_; 2]>::new();
311 if is_dir_owner {
312 candidates.push(file_mod.clone());
313 candidates.push(dir_mod);
314 } else {
315 candidates.push(file_dir_mod.clone());
316 };
317 let sr = db.source_root(source_root_id);
318 let points_to = candidates
319 .into_iter()
320 .filter_map(|path| sr.files.get(&path))
321 .map(|&it| it)
322 .collect::<Vec<_>>();
323 let problem = if points_to.is_empty() {
324 Some(Problem::UnresolvedModule {
325 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
326 })
327 } else {
328 None
329 };
330 (points_to, problem)
331}
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index 8d786d2ac..06bafa6f0 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -64,6 +64,7 @@ impl Name {
64 "str" => KnownName::Str, 64 "str" => KnownName::Str,
65 "Self" => KnownName::SelfType, 65 "Self" => KnownName::SelfType,
66 "self" => KnownName::SelfParam, 66 "self" => KnownName::SelfParam,
67 "macro_rules" => KnownName::MacroRules,
67 _ => return None, 68 _ => return None,
68 }; 69 };
69 Some(name) 70 Some(name)
@@ -122,4 +123,6 @@ pub(crate) enum KnownName {
122 123
123 SelfType, 124 SelfType,
124 SelfParam, 125 SelfParam,
126
127 MacroRules,
125} 128}
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index 73919ee37..edd2f25f7 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -1,60 +1,148 @@
1//! Name resolution algorithm. The end result of the algorithm is an `ItemMap`: 1/// This module implements import-resolution/macro expansion algorithm.
2//! a map which maps each module to its scope: the set of items visible in the 2///
3//! module. That is, we only resolve imports here, name resolution of item 3/// The result of this module is `CrateDefMap`: a datastructure which contains:
4//! bodies will be done in a separate step. 4///
5//! 5/// * a tree of modules for the crate
6//! Like Rustc, we use an interactive per-crate algorithm: we start with scopes 6/// * for each module, a set of items visible in the module (directly declared
7//! containing only directly defined items, and then iteratively resolve 7/// or imported)
8//! imports. 8///
9//! 9/// Note that `CrateDefMap` contains fully macro expanded code.
10//! To make this work nicely in the IDE scenario, we place `InputModuleItems` 10///
11//! in between raw syntax and name resolution. `InputModuleItems` are computed 11/// Computing `CrateDefMap` can be partitioned into several logically
12//! using only the module's syntax, and it is all directly defined items plus 12/// independent "phases". The phases are mutually recursive though, there's no
13//! imports. The plan is to make `InputModuleItems` independent of local 13/// strict ordering.
14//! modifications (that is, typing inside a function should not change IMIs), 14///
15//! so that the results of name resolution can be preserved unless the module 15/// ## Collecting RawItems
16//! structure itself is modified. 16///
17pub(crate) mod lower; 17/// This happens in the `raw` module, which parses a single source file into a
18 18/// set of top-level items. Nested imports are desugared to flat imports in
19use std::{time, sync::Arc}; 19/// this phase. Macro calls are represented as a triple of (Path, Option<Name>,
20 20/// TokenTree).
21use rustc_hash::{FxHashMap, FxHashSet}; 21///
22 22/// ## Collecting Modules
23use ra_arena::map::ArenaMap; 23///
24use ra_db::Edition; 24/// This happens in the `collector` module. In this phase, we recursively walk
25/// tree of modules, collect raw items from submodules, populate module scopes
26/// with defined items (so, we assign item ids in this phase) and record the set
27/// of unresolved imports and macros.
28///
29/// While we walk tree of modules, we also record macro_rules definitions and
30/// expand calls to macro_rules defined macros.
31///
32/// ## Resolving Imports
33///
34/// We maintain a list of currently unresolved imports. On every iteration, we
35/// try to resolve some imports from this list. If the import is resolved, we
36/// record it, by adding an item to current module scope and, if necessary, by
37/// recursively populating glob imports.
38///
39/// ## Resolving Macros
40///
41/// macro_rules from the same crate use a global mutable namespace. We expand
42/// them immediately, when we collect modules.
43///
44/// Macros from other crates (including proc-macros) can be used with
45/// `foo::bar!` syntax. We handle them similarly to imports. There's a list of
46/// unexpanded macros. On every iteration, we try to resolve each macro call
47/// path and, upon success, we run macro expansion and "collect module" phase
48/// on the result
49
50mod per_ns;
51mod raw;
52mod collector;
53#[cfg(test)]
54mod tests;
55
56use std::sync::Arc;
57
58use rustc_hash::FxHashMap;
59use ra_arena::{Arena, RawId, impl_arena_id};
60use ra_db::{FileId, Edition};
25use test_utils::tested_by; 61use test_utils::tested_by;
26 62
27use crate::{ 63use crate::{
28 Module, ModuleDef, 64 ModuleDef, Name, Crate, Module, Problem,
29 Path, PathKind, PersistentHirDatabase, 65 PersistentHirDatabase, Path, PathKind, HirFileId,
30 Crate, Name, 66 ids::{SourceItemId, SourceFileItemId, MacroCallId},
31 module_tree::{ModuleId, ModuleTree},
32 nameres::lower::{ImportId, LoweredModule, ImportData},
33}; 67};
34 68
35/// `ItemMap` is the result of module name resolution. It contains, for each 69pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap};
36/// module, the set of visible items. 70
71pub use self::per_ns::{PerNs, Namespace};
72
73/// Contans all top-level defs from a macro-expanded crate
37#[derive(Debug, PartialEq, Eq)] 74#[derive(Debug, PartialEq, Eq)]
38pub struct ItemMap { 75pub struct CrateDefMap {
76 krate: Crate,
39 edition: Edition, 77 edition: Edition,
40 /// The prelude module for this crate. This either comes from an import 78 /// The prelude module for this crate. This either comes from an import
41 /// marked with the `prelude_import` attribute, or (in the normal case) from 79 /// marked with the `prelude_import` attribute, or (in the normal case) from
42 /// a dependency (`std` or `core`). 80 /// a dependency (`std` or `core`).
43 pub(crate) prelude: Option<Module>, 81 prelude: Option<Module>,
44 pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>, 82 extern_prelude: FxHashMap<Name, ModuleDef>,
45 per_module: ArenaMap<ModuleId, ModuleScope>, 83 root: CrateModuleId,
84 modules: Arena<CrateModuleId, ModuleData>,
85 macros: Arena<CrateMacroId, mbe::MacroRules>,
86 public_macros: FxHashMap<Name, CrateMacroId>,
87 macro_resolutions: FxHashMap<MacroCallId, (Crate, CrateMacroId)>,
88 problems: CrateDefMapProblems,
89}
90
91impl std::ops::Index<CrateModuleId> for CrateDefMap {
92 type Output = ModuleData;
93 fn index(&self, id: CrateModuleId) -> &ModuleData {
94 &self.modules[id]
95 }
96}
97
98impl std::ops::Index<CrateMacroId> for CrateDefMap {
99 type Output = mbe::MacroRules;
100 fn index(&self, id: CrateMacroId) -> &mbe::MacroRules {
101 &self.macros[id]
102 }
103}
104
105/// An ID of a macro, **local** to a specific crate
106#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
107pub(crate) struct CrateMacroId(RawId);
108impl_arena_id!(CrateMacroId);
109
110/// An ID of a module, **local** to a specific crate
111#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
112pub(crate) struct CrateModuleId(RawId);
113impl_arena_id!(CrateModuleId);
114
115#[derive(Default, Debug, PartialEq, Eq)]
116pub(crate) struct ModuleData {
117 pub(crate) parent: Option<CrateModuleId>,
118 pub(crate) children: FxHashMap<Name, CrateModuleId>,
119 pub(crate) scope: ModuleScope,
120 /// None for root
121 pub(crate) declaration: Option<SourceItemId>,
122 /// None for inline modules.
123 ///
124 /// Note that non-inline modules, by definition, live inside non-macro file.
125 pub(crate) definition: Option<FileId>,
126}
127
128#[derive(Default, Debug, PartialEq, Eq)]
129pub(crate) struct CrateDefMapProblems {
130 problems: Vec<(SourceItemId, Problem)>,
46} 131}
47 132
48impl std::ops::Index<ModuleId> for ItemMap { 133impl CrateDefMapProblems {
49 type Output = ModuleScope; 134 fn add(&mut self, source_item_id: SourceItemId, problem: Problem) {
50 fn index(&self, id: ModuleId) -> &ModuleScope { 135 self.problems.push((source_item_id, problem))
51 &self.per_module[id] 136 }
137
138 pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a {
139 self.problems.iter().map(|(s, p)| (s, p))
52 } 140 }
53} 141}
54 142
55#[derive(Debug, Default, PartialEq, Eq, Clone)] 143#[derive(Debug, Default, PartialEq, Eq, Clone)]
56pub struct ModuleScope { 144pub struct ModuleScope {
57 pub(crate) items: FxHashMap<Name, Resolution>, 145 items: FxHashMap<Name, Resolution>,
58} 146}
59 147
60impl ModuleScope { 148impl ModuleScope {
@@ -66,8 +154,6 @@ impl ModuleScope {
66 } 154 }
67} 155}
68 156
69/// `Resolution` is basically `DefId` atm, but it should account for stuff like
70/// multiple namespaces, ambiguity and errors.
71#[derive(Debug, Clone, PartialEq, Eq, Default)] 157#[derive(Debug, Clone, PartialEq, Eq, Default)]
72pub struct Resolution { 158pub struct Resolution {
73 /// None for unresolved 159 /// None for unresolved
@@ -76,372 +162,6 @@ pub struct Resolution {
76 pub import: Option<ImportId>, 162 pub import: Option<ImportId>,
77} 163}
78 164
79#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub enum Namespace {
81 Types,
82 Values,
83}
84
85#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
86pub struct PerNs<T> {
87 pub types: Option<T>,
88 pub values: Option<T>,
89}
90
91impl<T> Default for PerNs<T> {
92 fn default() -> Self {
93 PerNs { types: None, values: None }
94 }
95}
96
97impl<T> PerNs<T> {
98 pub fn none() -> PerNs<T> {
99 PerNs { types: None, values: None }
100 }
101
102 pub fn values(t: T) -> PerNs<T> {
103 PerNs { types: None, values: Some(t) }
104 }
105
106 pub fn types(t: T) -> PerNs<T> {
107 PerNs { types: Some(t), values: None }
108 }
109
110 pub fn both(types: T, values: T) -> PerNs<T> {
111 PerNs { types: Some(types), values: Some(values) }
112 }
113
114 pub fn is_none(&self) -> bool {
115 self.types.is_none() && self.values.is_none()
116 }
117
118 pub fn is_both(&self) -> bool {
119 self.types.is_some() && self.values.is_some()
120 }
121
122 pub fn take(self, namespace: Namespace) -> Option<T> {
123 match namespace {
124 Namespace::Types => self.types,
125 Namespace::Values => self.values,
126 }
127 }
128
129 pub fn take_types(self) -> Option<T> {
130 self.take(Namespace::Types)
131 }
132
133 pub fn take_values(self) -> Option<T> {
134 self.take(Namespace::Values)
135 }
136
137 pub fn get(&self, namespace: Namespace) -> Option<&T> {
138 self.as_ref().take(namespace)
139 }
140
141 pub fn as_ref(&self) -> PerNs<&T> {
142 PerNs { types: self.types.as_ref(), values: self.values.as_ref() }
143 }
144
145 pub fn or(self, other: PerNs<T>) -> PerNs<T> {
146 PerNs { types: self.types.or(other.types), values: self.values.or(other.values) }
147 }
148
149 pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
150 PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) }
151 }
152
153 pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
154 PerNs { types: self.types.map(&f), values: self.values.map(&f) }
155 }
156}
157
158struct Resolver<'a, DB> {
159 db: &'a DB,
160 input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
161 krate: Crate,
162 module_tree: Arc<ModuleTree>,
163 processed_imports: FxHashSet<(ModuleId, ImportId)>,
164 /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import)
165 glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>,
166 result: ItemMap,
167}
168
169impl<'a, DB> Resolver<'a, DB>
170where
171 DB: PersistentHirDatabase,
172{
173 fn new(
174 db: &'a DB,
175 input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
176 krate: Crate,
177 ) -> Resolver<'a, DB> {
178 let module_tree = db.module_tree(krate);
179 Resolver {
180 db,
181 input,
182 krate,
183 module_tree,
184 processed_imports: FxHashSet::default(),
185 glob_imports: FxHashMap::default(),
186 result: ItemMap {
187 edition: krate.edition(db),
188 prelude: None,
189 extern_prelude: FxHashMap::default(),
190 per_module: ArenaMap::default(),
191 },
192 }
193 }
194
195 pub(crate) fn resolve(mut self) -> ItemMap {
196 self.populate_extern_prelude();
197 for (&module_id, items) in self.input.iter() {
198 self.populate_module(module_id, Arc::clone(items));
199 }
200
201 let mut iter = 0;
202 loop {
203 iter += 1;
204 if iter > 1000 {
205 panic!("failed to reach fixedpoint after 1000 iters")
206 }
207 let processed_imports_count = self.processed_imports.len();
208 for &module_id in self.input.keys() {
209 self.db.check_canceled();
210 self.resolve_imports(module_id);
211 }
212 if processed_imports_count == self.processed_imports.len() {
213 // no new imports resolved
214 break;
215 }
216 }
217 self.result
218 }
219
220 fn populate_extern_prelude(&mut self) {
221 for dep in self.krate.dependencies(self.db) {
222 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
223 if let Some(module) = dep.krate.root_module(self.db) {
224 self.result.extern_prelude.insert(dep.name.clone(), module.into());
225 }
226 // look for the prelude
227 if self.result.prelude.is_none() {
228 let item_map = self.db.item_map(dep.krate);
229 if item_map.prelude.is_some() {
230 self.result.prelude = item_map.prelude;
231 }
232 }
233 }
234 }
235
236 fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) {
237 let mut module_items = ModuleScope::default();
238 for (import_id, import_data) in input.imports.iter() {
239 if let Some(last_segment) = import_data.path.segments.iter().last() {
240 if !import_data.is_glob {
241 let name =
242 import_data.alias.clone().unwrap_or_else(|| last_segment.name.clone());
243 module_items
244 .items
245 .insert(name, Resolution { def: PerNs::none(), import: Some(import_id) });
246 }
247 }
248 }
249 // Populate explicitly declared items, except modules
250 for (name, &def) in input.declarations.iter() {
251 let resolution = Resolution { def, import: None };
252 module_items.items.insert(name.clone(), resolution);
253 }
254
255 // Populate modules
256 for (name, module_id) in module_id.children(&self.module_tree) {
257 let module = Module { module_id, krate: self.krate };
258 self.add_module_item(&mut module_items, name, PerNs::types(module.into()));
259 }
260
261 self.result.per_module.insert(module_id, module_items);
262 }
263
264 fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) {
265 let resolution = Resolution { def, import: None };
266 module_items.items.insert(name, resolution);
267 }
268
269 fn resolve_imports(&mut self, module_id: ModuleId) {
270 for (import_id, import_data) in self.input[&module_id].imports.iter() {
271 if self.processed_imports.contains(&(module_id, import_id)) {
272 // already done
273 continue;
274 }
275 if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes {
276 log::debug!("import {:?} resolved (or definite error)", import_id);
277 self.processed_imports.insert((module_id, import_id));
278 }
279 }
280 }
281
282 fn resolve_import(
283 &mut self,
284 module_id: ModuleId,
285 import_id: ImportId,
286 import: &ImportData,
287 ) -> ReachedFixedPoint {
288 log::debug!("resolving import: {:?} ({:?})", import, self.result.edition);
289 let original_module = Module { krate: self.krate, module_id };
290
291 let (def, reached_fixedpoint) = if import.is_extern_crate {
292 let res = self.result.resolve_name_in_extern_prelude(
293 &import
294 .path
295 .as_ident()
296 .expect("extern crate should have been desugared to one-element path"),
297 );
298 (res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes })
299 } else {
300 let res = self.result.resolve_path_fp(
301 self.db,
302 ResolveMode::Import,
303 original_module,
304 &import.path,
305 );
306
307 (res.resolved_def, res.reached_fixedpoint)
308 };
309
310 if reached_fixedpoint != ReachedFixedPoint::Yes {
311 return reached_fixedpoint;
312 }
313
314 if import.is_glob {
315 log::debug!("glob import: {:?}", import);
316 match def.take_types() {
317 Some(ModuleDef::Module(m)) => {
318 if import.is_prelude {
319 tested_by!(std_prelude);
320 self.result.prelude = Some(m);
321 } else if m.krate != self.krate {
322 tested_by!(glob_across_crates);
323 // glob import from other crate => we can just import everything once
324 let item_map = self.db.item_map(m.krate);
325 let scope = &item_map[m.module_id];
326 let items = scope
327 .items
328 .iter()
329 .map(|(name, res)| (name.clone(), res.clone()))
330 .collect::<Vec<_>>();
331 self.update(module_id, Some(import_id), &items);
332 } else {
333 // glob import from same crate => we do an initial
334 // import, and then need to propagate any further
335 // additions
336 let scope = &self.result[m.module_id];
337 let items = scope
338 .items
339 .iter()
340 .map(|(name, res)| (name.clone(), res.clone()))
341 .collect::<Vec<_>>();
342 self.update(module_id, Some(import_id), &items);
343 // record the glob import in case we add further items
344 self.glob_imports
345 .entry(m.module_id)
346 .or_default()
347 .push((module_id, import_id));
348 }
349 }
350 Some(ModuleDef::Enum(e)) => {
351 tested_by!(glob_enum);
352 // glob import from enum => just import all the variants
353 let variants = e.variants(self.db);
354 let resolutions = variants
355 .into_iter()
356 .filter_map(|variant| {
357 let res = Resolution {
358 def: PerNs::both(variant.into(), variant.into()),
359 import: Some(import_id),
360 };
361 let name = variant.name(self.db)?;
362 Some((name, res))
363 })
364 .collect::<Vec<_>>();
365 self.update(module_id, Some(import_id), &resolutions);
366 }
367 Some(d) => {
368 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
369 }
370 None => {
371 log::debug!("glob import {:?} didn't resolve as type", import);
372 }
373 }
374 } else {
375 let last_segment = import.path.segments.last().unwrap();
376 let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
377 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
378
379 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
380 if let Some(root_module) = self.krate.root_module(self.db) {
381 if import.is_extern_crate && module_id == root_module.module_id {
382 if let Some(def) = def.take_types() {
383 self.result.extern_prelude.insert(name.clone(), def);
384 }
385 }
386 }
387 let resolution = Resolution { def, import: Some(import_id) };
388 self.update(module_id, None, &[(name, resolution)]);
389 }
390 reached_fixedpoint
391 }
392
393 fn update(
394 &mut self,
395 module_id: ModuleId,
396 import: Option<ImportId>,
397 resolutions: &[(Name, Resolution)],
398 ) {
399 self.update_recursive(module_id, import, resolutions, 0)
400 }
401
402 fn update_recursive(
403 &mut self,
404 module_id: ModuleId,
405 import: Option<ImportId>,
406 resolutions: &[(Name, Resolution)],
407 depth: usize,
408 ) {
409 if depth > 100 {
410 // prevent stack overflows (but this shouldn't be possible)
411 panic!("infinite recursion in glob imports!");
412 }
413 let module_items = self.result.per_module.get_mut(module_id).unwrap();
414 let mut changed = false;
415 for (name, res) in resolutions {
416 let existing = module_items.items.entry(name.clone()).or_default();
417 if existing.def.types.is_none() && res.def.types.is_some() {
418 existing.def.types = res.def.types;
419 existing.import = import.or(res.import);
420 changed = true;
421 }
422 if existing.def.values.is_none() && res.def.values.is_some() {
423 existing.def.values = res.def.values;
424 existing.import = import.or(res.import);
425 changed = true;
426 }
427 }
428 if !changed {
429 return;
430 }
431 let glob_imports = self
432 .glob_imports
433 .get(&module_id)
434 .into_iter()
435 .flat_map(|v| v.iter())
436 .cloned()
437 .collect::<Vec<_>>();
438 for (glob_importing_module, glob_import) in glob_imports {
439 // We pass the glob import so that the tracked import in those modules is that glob import
440 self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
441 }
442 }
443}
444
445#[derive(Debug, Clone)] 165#[derive(Debug, Clone)]
446struct ResolvePathResult { 166struct ResolvePathResult {
447 resolved_def: PerNs<ModuleDef>, 167 resolved_def: PerNs<ModuleDef>,
@@ -475,84 +195,85 @@ enum ReachedFixedPoint {
475 No, 195 No,
476} 196}
477 197
478impl ItemMap { 198impl CrateDefMap {
479 pub(crate) fn item_map_query(db: &impl PersistentHirDatabase, krate: Crate) -> Arc<ItemMap> { 199 pub(crate) fn crate_def_map_query(
480 let start = time::Instant::now(); 200 db: &impl PersistentHirDatabase,
481 let module_tree = db.module_tree(krate); 201 krate: Crate,
482 let input = module_tree 202 ) -> Arc<CrateDefMap> {
483 .modules() 203 let start = std::time::Instant::now();
484 .map(|module_id| (module_id, db.lower_module(Module { krate, module_id }))) 204 let def_map = {
485 .collect::<FxHashMap<_, _>>(); 205 let edition = krate.edition(db);
486 206 let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
487 let resolver = Resolver::new(db, &input, krate); 207 let root = modules.alloc(ModuleData::default());
488 let res = resolver.resolve(); 208 CrateDefMap {
489 let elapsed = start.elapsed(); 209 krate,
490 log::info!("item_map: {:?}", elapsed); 210 edition,
491 Arc::new(res) 211 extern_prelude: FxHashMap::default(),
212 prelude: None,
213 root,
214 modules,
215 macros: Arena::default(),
216 public_macros: FxHashMap::default(),
217 macro_resolutions: FxHashMap::default(),
218 problems: CrateDefMapProblems::default(),
219 }
220 };
221 let def_map = collector::collect_defs(db, def_map);
222 log::info!("crate_def_map_query: {:?}", start.elapsed());
223 Arc::new(def_map)
492 } 224 }
493 225
494 pub(crate) fn resolve_path( 226 pub(crate) fn root(&self) -> CrateModuleId {
495 &self, 227 self.root
496 db: &impl PersistentHirDatabase,
497 original_module: Module,
498 path: &Path,
499 ) -> (PerNs<ModuleDef>, Option<usize>) {
500 let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path);
501 (res.resolved_def, res.segment_index)
502 } 228 }
503 229
504 fn resolve_in_prelude( 230 pub(crate) fn problems(&self) -> &CrateDefMapProblems {
505 &self, 231 &self.problems
506 db: &impl PersistentHirDatabase,
507 original_module: Module,
508 name: &Name,
509 ) -> PerNs<ModuleDef> {
510 if let Some(prelude) = self.prelude {
511 let resolution = if prelude.krate == original_module.krate {
512 self[prelude.module_id].items.get(name).cloned()
513 } else {
514 db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned()
515 };
516 resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
517 } else {
518 PerNs::none()
519 }
520 } 232 }
521 233
522 pub(crate) fn resolve_name_in_module( 234 pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module {
523 &self, 235 Module { krate: self.krate, module_id }
524 db: &impl PersistentHirDatabase, 236 }
525 module: Module,
526 name: &Name,
527 ) -> PerNs<ModuleDef> {
528 // Resolve in:
529 // - current module / scope
530 // - extern prelude
531 // - std prelude
532 let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
533 let from_extern_prelude =
534 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
535 let from_prelude = self.resolve_in_prelude(db, module, name);
536 237
537 from_scope.or(from_extern_prelude).or(from_prelude) 238 pub(crate) fn prelude(&self) -> Option<Module> {
239 self.prelude
538 } 240 }
539 241
540 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> { 242 pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> {
541 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) 243 &self.extern_prelude
542 } 244 }
543 245
544 fn resolve_name_in_crate_root_or_extern_prelude( 246 pub(crate) fn resolve_macro(
545 &self, 247 &self,
546 db: &impl PersistentHirDatabase, 248 macro_call_id: MacroCallId,
547 module: Module, 249 ) -> Option<(Crate, CrateMacroId)> {
548 name: &Name, 250 self.macro_resolutions.get(&macro_call_id).map(|&it| it)
549 ) -> PerNs<ModuleDef> { 251 }
550 let crate_root = module.crate_root(db);
551 let from_crate_root =
552 self[crate_root.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
553 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
554 252
555 from_crate_root.or(from_extern_prelude) 253 pub(crate) fn find_module_by_source(
254 &self,
255 file_id: HirFileId,
256 decl_id: Option<SourceFileItemId>,
257 ) -> Option<CrateModuleId> {
258 let decl_id = decl_id.map(|it| it.with_file_id(file_id));
259 let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| {
260 if decl_id.is_some() {
261 module_data.declaration == decl_id
262 } else {
263 module_data.definition.map(|it| it.into()) == Some(file_id)
264 }
265 })?;
266 Some(module_id)
267 }
268
269 pub(crate) fn resolve_path(
270 &self,
271 db: &impl PersistentHirDatabase,
272 original_module: CrateModuleId,
273 path: &Path,
274 ) -> (PerNs<ModuleDef>, Option<usize>) {
275 let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path);
276 (res.resolved_def, res.segment_index)
556 } 277 }
557 278
558 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change 279 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
@@ -561,13 +282,17 @@ impl ItemMap {
561 &self, 282 &self,
562 db: &impl PersistentHirDatabase, 283 db: &impl PersistentHirDatabase,
563 mode: ResolveMode, 284 mode: ResolveMode,
564 original_module: Module, 285 original_module: CrateModuleId,
565 path: &Path, 286 path: &Path,
566 ) -> ResolvePathResult { 287 ) -> ResolvePathResult {
567 let mut segments = path.segments.iter().enumerate(); 288 let mut segments = path.segments.iter().enumerate();
568 let mut curr_per_ns: PerNs<ModuleDef> = match path.kind { 289 let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
569 PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), 290 PathKind::Crate => {
570 PathKind::Self_ => PerNs::types(original_module.into()), 291 PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
292 }
293 PathKind::Self_ => {
294 PerNs::types(Module { krate: self.krate, module_id: original_module }.into())
295 }
571 // plain import or absolute path in 2015: crate-relative with 296 // plain import or absolute path in 2015: crate-relative with
572 // fallback to extern prelude (with the simplification in 297 // fallback to extern prelude (with the simplification in
573 // rust-lang/rust#57745) 298 // rust-lang/rust#57745)
@@ -581,11 +306,7 @@ impl ItemMap {
581 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 306 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
582 }; 307 };
583 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); 308 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
584 self.resolve_name_in_crate_root_or_extern_prelude( 309 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
585 db,
586 original_module,
587 &segment.name,
588 )
589 } 310 }
590 PathKind::Plain => { 311 PathKind::Plain => {
591 let segment = match segments.next() { 312 let segment = match segments.next() {
@@ -596,8 +317,8 @@ impl ItemMap {
596 self.resolve_name_in_module(db, original_module, &segment.name) 317 self.resolve_name_in_module(db, original_module, &segment.name)
597 } 318 }
598 PathKind::Super => { 319 PathKind::Super => {
599 if let Some(p) = original_module.parent(db) { 320 if let Some(p) = self.modules[original_module].parent {
600 PerNs::types(p.into()) 321 PerNs::types(Module { krate: self.krate, module_id: p }.into())
601 } else { 322 } else {
602 log::debug!("super path in root module"); 323 log::debug!("super path in root module");
603 return ResolvePathResult::empty(ReachedFixedPoint::Yes); 324 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
@@ -634,14 +355,14 @@ impl ItemMap {
634 355
635 curr_per_ns = match curr { 356 curr_per_ns = match curr {
636 ModuleDef::Module(module) => { 357 ModuleDef::Module(module) => {
637 if module.krate != original_module.krate { 358 if module.krate != self.krate {
638 let path = Path { 359 let path = Path {
639 segments: path.segments[i..].iter().cloned().collect(), 360 segments: path.segments[i..].iter().cloned().collect(),
640 kind: PathKind::Self_, 361 kind: PathKind::Self_,
641 }; 362 };
642 log::debug!("resolving {:?} in other crate", path); 363 log::debug!("resolving {:?} in other crate", path);
643 let item_map = db.item_map(module.krate); 364 let defp_map = db.crate_def_map(module.krate);
644 let (def, s) = item_map.resolve_path(db, *module, &path); 365 let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
645 return ResolvePathResult::with( 366 return ResolvePathResult::with(
646 def, 367 def,
647 ReachedFixedPoint::Yes, 368 ReachedFixedPoint::Yes,
@@ -649,7 +370,7 @@ impl ItemMap {
649 ); 370 );
650 } 371 }
651 372
652 match self[module.module_id].items.get(&segment.name) { 373 match self[module.module_id].scope.items.get(&segment.name) {
653 Some(res) if !res.def.is_none() => res.def, 374 Some(res) if !res.def.is_none() => res.def,
654 _ => { 375 _ => {
655 log::debug!("path segment {:?} not found", segment.name); 376 log::debug!("path segment {:?} not found", segment.name);
@@ -659,7 +380,7 @@ impl ItemMap {
659 } 380 }
660 ModuleDef::Enum(e) => { 381 ModuleDef::Enum(e) => {
661 // enum variant 382 // enum variant
662 tested_by!(item_map_enum_importing); 383 tested_by!(can_import_enum_variant);
663 match e.variant(db, &segment.name) { 384 match e.variant(db, &segment.name) {
664 Some(variant) => PerNs::both(variant.into(), variant.into()), 385 Some(variant) => PerNs::both(variant.into(), variant.into()),
665 None => { 386 None => {
@@ -690,7 +411,47 @@ impl ItemMap {
690 } 411 }
691 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) 412 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
692 } 413 }
693}
694 414
695#[cfg(test)] 415 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
696mod tests; 416 let from_crate_root =
417 self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
418 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
419
420 from_crate_root.or(from_extern_prelude)
421 }
422
423 pub(crate) fn resolve_name_in_module(
424 &self,
425 db: &impl PersistentHirDatabase,
426 module: CrateModuleId,
427 name: &Name,
428 ) -> PerNs<ModuleDef> {
429 // Resolve in:
430 // - current module / scope
431 // - extern prelude
432 // - std prelude
433 let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
434 let from_extern_prelude =
435 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
436 let from_prelude = self.resolve_in_prelude(db, name);
437
438 from_scope.or(from_extern_prelude).or(from_prelude)
439 }
440
441 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
442 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
443 }
444
445 fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> {
446 if let Some(prelude) = self.prelude {
447 let resolution = if prelude.krate == self.krate {
448 self[prelude.module_id].scope.items.get(name).cloned()
449 } else {
450 db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned()
451 };
452 resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
453 } else {
454 PerNs::none()
455 }
456 }
457}
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
new file mode 100644
index 000000000..12ed49a0a
--- /dev/null
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -0,0 +1,564 @@
1use arrayvec::ArrayVec;
2use rustc_hash::FxHashMap;
3use relative_path::RelativePathBuf;
4use test_utils::tested_by;
5use ra_db::FileId;
6
7use crate::{
8 Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
9 PersistentHirDatabase, HirFileId, Name, Path, Problem, Crate,
10 KnownName,
11 nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw},
12 ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId},
13};
14
15use super::{CrateDefMap, CrateModuleId, ModuleData, CrateMacroId};
16
17pub(super) fn collect_defs(
18 db: &impl PersistentHirDatabase,
19 mut def_map: CrateDefMap,
20) -> CrateDefMap {
21 // populate external prelude
22 for dep in def_map.krate.dependencies(db) {
23 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
24 if let Some(module) = dep.krate.root_module(db) {
25 def_map.extern_prelude.insert(dep.name.clone(), module.into());
26 }
27 // look for the prelude
28 if def_map.prelude.is_none() {
29 let map = db.crate_def_map(dep.krate);
30 if map.prelude.is_some() {
31 def_map.prelude = map.prelude;
32 }
33 }
34 }
35
36 let mut collector = DefCollector {
37 db,
38 def_map,
39 glob_imports: FxHashMap::default(),
40 unresolved_imports: Vec::new(),
41 unexpanded_macros: Vec::new(),
42 global_macro_scope: FxHashMap::default(),
43 };
44 collector.collect();
45 collector.finish()
46}
47
48/// Walks the tree of module recursively
49struct DefCollector<DB> {
50 db: DB,
51 def_map: CrateDefMap,
52 glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
53 unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
54 unexpanded_macros: Vec<(CrateModuleId, MacroCallId, Path, tt::Subtree)>,
55 global_macro_scope: FxHashMap<Name, CrateMacroId>,
56}
57
58impl<'a, DB> DefCollector<&'a DB>
59where
60 DB: PersistentHirDatabase,
61{
62 fn collect(&mut self) {
63 let crate_graph = self.db.crate_graph();
64 let file_id = crate_graph.crate_root(self.def_map.krate.crate_id());
65 let raw_items = self.db.raw_items(file_id);
66 let module_id = self.def_map.root;
67 self.def_map.modules[module_id].definition = Some(file_id);
68 ModCollector {
69 def_collector: &mut *self,
70 module_id,
71 file_id: file_id.into(),
72 raw_items: &raw_items,
73 }
74 .collect(raw_items.items());
75
76 // main name resolution fixed-point loop.
77 let mut i = 0;
78 loop {
79 match (self.resolve_imports(), self.resolve_macros()) {
80 (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break,
81 _ => i += 1,
82 }
83 if i == 1000 {
84 log::error!("diverging name resolution");
85 break;
86 }
87 }
88
89 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
90 // show unresolved imports in completion, etc
91 for (module_id, import, import_data) in unresolved_imports {
92 self.record_resolved_import(module_id, PerNs::none(), import, &import_data)
93 }
94 }
95
96 fn define_macro(&mut self, name: Name, tt: &tt::Subtree, export: bool) {
97 if let Ok(rules) = mbe::MacroRules::parse(tt) {
98 let macro_id = self.def_map.macros.alloc(rules);
99 if export {
100 self.def_map.public_macros.insert(name.clone(), macro_id);
101 }
102 self.global_macro_scope.insert(name, macro_id);
103 }
104 }
105
106 fn resolve_imports(&mut self) -> ReachedFixedPoint {
107 let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
108 let mut resolved = Vec::new();
109 imports.retain(|(module_id, import, import_data)| {
110 let (def, fp) = self.resolve_import(*module_id, import_data);
111 if fp == ReachedFixedPoint::Yes {
112 resolved.push((*module_id, def, *import, import_data.clone()))
113 }
114 fp == ReachedFixedPoint::No
115 });
116 self.unresolved_imports = imports;
117 // Resolves imports, filling-in module scopes
118 let result =
119 if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No };
120 for (module_id, def, import, import_data) in resolved {
121 self.record_resolved_import(module_id, def, import, &import_data)
122 }
123 result
124 }
125
126 fn resolve_import(
127 &mut self,
128 module_id: CrateModuleId,
129 import: &raw::ImportData,
130 ) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
131 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
132 if import.is_extern_crate {
133 let res = self.def_map.resolve_name_in_extern_prelude(
134 &import
135 .path
136 .as_ident()
137 .expect("extern crate should have been desugared to one-element path"),
138 );
139 (res, ReachedFixedPoint::Yes)
140 } else {
141 let res =
142 self.def_map.resolve_path_fp(self.db, ResolveMode::Import, module_id, &import.path);
143
144 (res.resolved_def, res.reached_fixedpoint)
145 }
146 }
147
148 fn record_resolved_import(
149 &mut self,
150 module_id: CrateModuleId,
151 def: PerNs<ModuleDef>,
152 import_id: raw::ImportId,
153 import: &raw::ImportData,
154 ) {
155 if import.is_glob {
156 log::debug!("glob import: {:?}", import);
157 match def.take_types() {
158 Some(ModuleDef::Module(m)) => {
159 if import.is_prelude {
160 tested_by!(std_prelude);
161 self.def_map.prelude = Some(m);
162 } else if m.krate != self.def_map.krate {
163 tested_by!(glob_across_crates);
164 // glob import from other crate => we can just import everything once
165 let item_map = self.db.crate_def_map(m.krate);
166 let scope = &item_map[m.module_id].scope;
167 let items = scope
168 .items
169 .iter()
170 .map(|(name, res)| (name.clone(), res.clone()))
171 .collect::<Vec<_>>();
172 self.update(module_id, Some(import_id), &items);
173 } else {
174 // glob import from same crate => we do an initial
175 // import, and then need to propagate any further
176 // additions
177 let scope = &self.def_map[m.module_id].scope;
178 let items = scope
179 .items
180 .iter()
181 .map(|(name, res)| (name.clone(), res.clone()))
182 .collect::<Vec<_>>();
183 self.update(module_id, Some(import_id), &items);
184 // record the glob import in case we add further items
185 self.glob_imports
186 .entry(m.module_id)
187 .or_default()
188 .push((module_id, import_id));
189 }
190 }
191 Some(ModuleDef::Enum(e)) => {
192 tested_by!(glob_enum);
193 // glob import from enum => just import all the variants
194 let variants = e.variants(self.db);
195 let resolutions = variants
196 .into_iter()
197 .filter_map(|variant| {
198 let res = Resolution {
199 def: PerNs::both(variant.into(), variant.into()),
200 import: Some(import_id),
201 };
202 let name = variant.name(self.db)?;
203 Some((name, res))
204 })
205 .collect::<Vec<_>>();
206 self.update(module_id, Some(import_id), &resolutions);
207 }
208 Some(d) => {
209 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
210 }
211 None => {
212 log::debug!("glob import {:?} didn't resolve as type", import);
213 }
214 }
215 } else {
216 match import.path.segments.last() {
217 Some(last_segment) => {
218 let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
219 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
220
221 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
222 if import.is_extern_crate && module_id == self.def_map.root {
223 if let Some(def) = def.take_types() {
224 self.def_map.extern_prelude.insert(name.clone(), def);
225 }
226 }
227 let resolution = Resolution { def, import: Some(import_id) };
228 self.update(module_id, Some(import_id), &[(name, resolution)]);
229 }
230 None => tested_by!(bogus_paths),
231 }
232 }
233 }
234
235 fn update(
236 &mut self,
237 module_id: CrateModuleId,
238 import: Option<raw::ImportId>,
239 resolutions: &[(Name, Resolution)],
240 ) {
241 self.update_recursive(module_id, import, resolutions, 0)
242 }
243
244 fn update_recursive(
245 &mut self,
246 module_id: CrateModuleId,
247 import: Option<raw::ImportId>,
248 resolutions: &[(Name, Resolution)],
249 depth: usize,
250 ) {
251 if depth > 100 {
252 // prevent stack overflows (but this shouldn't be possible)
253 panic!("infinite recursion in glob imports!");
254 }
255 let module_items = &mut self.def_map.modules[module_id].scope;
256 let mut changed = false;
257 for (name, res) in resolutions {
258 let existing = module_items.items.entry(name.clone()).or_default();
259 if existing.def.types.is_none() && res.def.types.is_some() {
260 existing.def.types = res.def.types;
261 existing.import = import.or(res.import);
262 changed = true;
263 }
264 if existing.def.values.is_none() && res.def.values.is_some() {
265 existing.def.values = res.def.values;
266 existing.import = import.or(res.import);
267 changed = true;
268 }
269 if existing.def.is_none()
270 && res.def.is_none()
271 && existing.import.is_none()
272 && res.import.is_some()
273 {
274 existing.import = res.import;
275 }
276 }
277 if !changed {
278 return;
279 }
280 let glob_imports = self
281 .glob_imports
282 .get(&module_id)
283 .into_iter()
284 .flat_map(|v| v.iter())
285 .cloned()
286 .collect::<Vec<_>>();
287 for (glob_importing_module, glob_import) in glob_imports {
288 // We pass the glob import so that the tracked import in those modules is that glob import
289 self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1);
290 }
291 }
292
293 // XXX: this is just a pile of hacks now, because `PerNs` does not handle
294 // macro namespace.
295 fn resolve_macros(&mut self) -> ReachedFixedPoint {
296 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
297 let mut resolved = Vec::new();
298 let mut res = ReachedFixedPoint::Yes;
299 macros.retain(|(module_id, call_id, path, tt)| {
300 if path.segments.len() != 2 {
301 return true;
302 }
303 let crate_name = &path.segments[0].name;
304 let krate = match self.def_map.resolve_name_in_extern_prelude(crate_name).take_types() {
305 Some(ModuleDef::Module(m)) => m.krate(self.db),
306 _ => return true,
307 };
308 let krate = match krate {
309 Some(it) => it,
310 _ => return true,
311 };
312 res = ReachedFixedPoint::No;
313 let def_map = self.db.crate_def_map(krate);
314 if let Some(macro_id) = def_map.public_macros.get(&path.segments[1].name).cloned() {
315 resolved.push((*module_id, *call_id, (krate, macro_id), tt.clone()));
316 }
317 false
318 });
319
320 for (module_id, macro_call_id, macro_def_id, arg) in resolved {
321 self.collect_macro_expansion(module_id, macro_call_id, macro_def_id, arg);
322 }
323 res
324 }
325
326 fn collect_macro_expansion(
327 &mut self,
328 module_id: CrateModuleId,
329 macro_call_id: MacroCallId,
330 macro_def_id: (Crate, CrateMacroId),
331 macro_arg: tt::Subtree,
332 ) {
333 let (macro_krate, macro_id) = macro_def_id;
334 let dm;
335 let rules = if macro_krate == self.def_map.krate {
336 &self.def_map[macro_id]
337 } else {
338 dm = self.db.crate_def_map(macro_krate);
339 &dm[macro_id]
340 };
341 if let Ok(expansion) = rules.expand(&macro_arg) {
342 self.def_map.macro_resolutions.insert(macro_call_id, macro_def_id);
343 // XXX: this **does not** go through a database, because we can't
344 // identify macro_call without adding the whole state of name resolution
345 // as a parameter to the query.
346 //
347 // So, we run the queries "manually" and we must ensure that
348 // `db.hir_parse(macro_call_id)` returns the same source_file.
349 let file_id: HirFileId = macro_call_id.into();
350 let source_file = mbe::token_tree_to_ast_item_list(&expansion);
351
352 let raw_items = raw::RawItems::from_source_file(&source_file, file_id);
353 ModCollector { def_collector: &mut *self, file_id, module_id, raw_items: &raw_items }
354 .collect(raw_items.items())
355 }
356 }
357
358 fn finish(self) -> CrateDefMap {
359 self.def_map
360 }
361}
362
363/// Walks a single module, populating defs, imports and macros
364struct ModCollector<'a, D> {
365 def_collector: D,
366 module_id: CrateModuleId,
367 file_id: HirFileId,
368 raw_items: &'a raw::RawItems,
369}
370
371impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
372where
373 DB: PersistentHirDatabase,
374{
375 fn collect(&mut self, items: &[raw::RawItem]) {
376 for item in items {
377 match *item {
378 raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]),
379 raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push((
380 self.module_id,
381 import,
382 self.raw_items[import].clone(),
383 )),
384 raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]),
385 raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
386 }
387 }
388 }
389
390 fn collect_module(&mut self, module: &raw::ModuleData) {
391 match module {
392 // inline module, just recurse
393 raw::ModuleData::Definition { name, items, source_item_id } => {
394 let module_id = self.push_child_module(
395 name.clone(),
396 source_item_id.with_file_id(self.file_id),
397 None,
398 );
399 ModCollector {
400 def_collector: &mut *self.def_collector,
401 module_id,
402 file_id: self.file_id,
403 raw_items: self.raw_items,
404 }
405 .collect(&*items);
406 }
407 // out of line module, resovle, parse and recurse
408 raw::ModuleData::Declaration { name, source_item_id } => {
409 let source_item_id = source_item_id.with_file_id(self.file_id);
410 let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none();
411 let (file_ids, problem) =
412 resolve_submodule(self.def_collector.db, self.file_id, name, is_root);
413
414 if let Some(problem) = problem {
415 self.def_collector.def_map.problems.add(source_item_id, problem)
416 }
417
418 if let Some(&file_id) = file_ids.first() {
419 let module_id =
420 self.push_child_module(name.clone(), source_item_id, Some(file_id));
421 let raw_items = self.def_collector.db.raw_items(file_id);
422 ModCollector {
423 def_collector: &mut *self.def_collector,
424 module_id,
425 file_id: file_id.into(),
426 raw_items: &raw_items,
427 }
428 .collect(raw_items.items())
429 }
430 }
431 }
432 }
433
434 fn push_child_module(
435 &mut self,
436 name: Name,
437 declaration: SourceItemId,
438 definition: Option<FileId>,
439 ) -> CrateModuleId {
440 let modules = &mut self.def_collector.def_map.modules;
441 let res = modules.alloc(ModuleData::default());
442 modules[res].parent = Some(self.module_id);
443 modules[res].declaration = Some(declaration);
444 modules[res].definition = definition;
445 modules[self.module_id].children.insert(name.clone(), res);
446 let resolution = Resolution {
447 def: PerNs::types(
448 Module { krate: self.def_collector.def_map.krate, module_id: res }.into(),
449 ),
450 import: None,
451 };
452 self.def_collector.update(self.module_id, None, &[(name, resolution)]);
453 res
454 }
455
456 fn define_def(&mut self, def: &raw::DefData) {
457 let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id };
458 let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into());
459 macro_rules! id {
460 () => {
461 AstItemDef::from_source_item_id_unchecked(ctx, def.source_item_id)
462 };
463 }
464 let name = def.name.clone();
465 let def: PerNs<ModuleDef> = match def.kind {
466 raw::DefKind::Function => PerNs::values(Function { id: id!() }.into()),
467 raw::DefKind::Struct => {
468 let s = Struct { id: id!() }.into();
469 PerNs::both(s, s)
470 }
471 raw::DefKind::Enum => PerNs::types(Enum { id: id!() }.into()),
472 raw::DefKind::Const => PerNs::values(Const { id: id!() }.into()),
473 raw::DefKind::Static => PerNs::values(Static { id: id!() }.into()),
474 raw::DefKind::Trait => PerNs::types(Trait { id: id!() }.into()),
475 raw::DefKind::TypeAlias => PerNs::types(TypeAlias { id: id!() }.into()),
476 };
477 let resolution = Resolution { def, import: None };
478 self.def_collector.update(self.module_id, None, &[(name, resolution)])
479 }
480
481 fn collect_macro(&mut self, mac: &raw::MacroData) {
482 // Case 1: macro rules, define a macro in crate-global mutable scope
483 if is_macro_rules(&mac.path) {
484 if let Some(name) = &mac.name {
485 self.def_collector.define_macro(name.clone(), &mac.arg, mac.export)
486 }
487 return;
488 }
489
490 let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id };
491 let macro_call_id = MacroCallLoc {
492 module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id },
493 source_item_id,
494 }
495 .id(self.def_collector.db);
496
497 // Case 2: try to expand macro_rules from this crate, triggering
498 // recursive item collection.
499 if let Some(&macro_id) =
500 mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(name))
501 {
502 self.def_collector.collect_macro_expansion(
503 self.module_id,
504 macro_call_id,
505 (self.def_collector.def_map.krate, macro_id),
506 mac.arg.clone(),
507 );
508 return;
509 }
510
511 // Case 3: path to a macro from another crate, expand during name resolution
512 self.def_collector.unexpanded_macros.push((
513 self.module_id,
514 macro_call_id,
515 mac.path.clone(),
516 mac.arg.clone(),
517 ))
518 }
519}
520
521fn is_macro_rules(path: &Path) -> bool {
522 path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules)
523}
524
525fn resolve_submodule(
526 db: &impl PersistentHirDatabase,
527 file_id: HirFileId,
528 name: &Name,
529 is_root: bool,
530) -> (Vec<FileId>, Option<Problem>) {
531 // FIXME: handle submodules of inline modules properly
532 let file_id = file_id.original_file(db);
533 let source_root_id = db.file_source_root(file_id);
534 let path = db.file_relative_path(file_id);
535 let root = RelativePathBuf::default();
536 let dir_path = path.parent().unwrap_or(&root);
537 let mod_name = path.file_stem().unwrap_or("unknown");
538 let is_dir_owner = is_root || mod_name == "mod";
539
540 let file_mod = dir_path.join(format!("{}.rs", name));
541 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
542 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
543 let mut candidates = ArrayVec::<[_; 2]>::new();
544 if is_dir_owner {
545 candidates.push(file_mod.clone());
546 candidates.push(dir_mod);
547 } else {
548 candidates.push(file_dir_mod.clone());
549 };
550 let sr = db.source_root(source_root_id);
551 let points_to = candidates
552 .into_iter()
553 .filter_map(|path| sr.files.get(&path))
554 .map(|&it| it)
555 .collect::<Vec<_>>();
556 let problem = if points_to.is_empty() {
557 Some(Problem::UnresolvedModule {
558 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
559 })
560 } else {
561 None
562 };
563 (points_to, problem)
564}
diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs
deleted file mode 100644
index 56262ad6d..000000000
--- a/crates/ra_hir/src/nameres/lower.rs
+++ /dev/null
@@ -1,222 +0,0 @@
1use std::sync::Arc;
2
3use ra_syntax::{
4 AstNode, SourceFile, TreeArc, AstPtr,
5 ast::{self, ModuleItemOwner, NameOwner, AttrsOwner},
6};
7use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
8use rustc_hash::FxHashMap;
9
10use crate::{
11 SourceItemId, Path, ModuleSource, Name,
12 HirFileId, MacroCallLoc, AsName, PerNs, Function,
13 ModuleDef, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
14 ids::LocationCtx, PersistentHirDatabase,
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct ImportId(RawId);
19impl_arena_id!(ImportId);
20
21#[derive(Debug, PartialEq, Eq)]
22pub(super) struct ImportData {
23 pub(super) path: Path,
24 pub(super) alias: Option<Name>,
25 pub(super) is_glob: bool,
26 pub(super) is_prelude: bool,
27 pub(super) is_extern_crate: bool,
28}
29
30/// A set of items and imports declared inside a module, without relation to
31/// other modules.
32///
33/// This sits in-between raw syntax and name resolution and allows us to avoid
34/// recomputing name res: if two instance of `InputModuleItems` are the same, we
35/// can avoid redoing name resolution.
36#[derive(Debug, Default, PartialEq, Eq)]
37pub struct LoweredModule {
38 pub(crate) declarations: FxHashMap<Name, PerNs<ModuleDef>>,
39 pub(super) imports: Arena<ImportId, ImportData>,
40}
41
42#[derive(Debug, Default, PartialEq, Eq)]
43pub struct ImportSourceMap {
44 map: ArenaMap<ImportId, AstPtr<ast::PathSegment>>,
45}
46
47impl ImportSourceMap {
48 fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) {
49 self.map.insert(import, AstPtr::new(segment))
50 }
51
52 pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc<ast::PathSegment> {
53 let file = match source {
54 ModuleSource::SourceFile(file) => &*file,
55 ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
56 };
57
58 self.map[import].to_node(file).to_owned()
59 }
60}
61
62impl LoweredModule {
63 pub(crate) fn lower_module_query(
64 db: &impl PersistentHirDatabase,
65 module: Module,
66 ) -> Arc<LoweredModule> {
67 db.lower_module_with_source_map(module).0
68 }
69
70 pub(crate) fn lower_module_with_source_map_query(
71 db: &impl PersistentHirDatabase,
72 module: Module,
73 ) -> (Arc<LoweredModule>, Arc<ImportSourceMap>) {
74 let (file_id, source) = module.definition_source(db);
75 let file_id: HirFileId = file_id.into();
76 let mut source_map = ImportSourceMap::default();
77 let mut res = LoweredModule::default();
78 match source {
79 ModuleSource::SourceFile(it) => {
80 res.fill(&mut source_map, db, module, file_id, &mut it.items_with_macros())
81 }
82 ModuleSource::Module(it) => {
83 if let Some(item_list) = it.item_list() {
84 res.fill(
85 &mut source_map,
86 db,
87 module,
88 file_id,
89 &mut item_list.items_with_macros(),
90 )
91 }
92 }
93 };
94 (Arc::new(res), Arc::new(source_map))
95 }
96
97 fn fill(
98 &mut self,
99 source_map: &mut ImportSourceMap,
100 db: &impl PersistentHirDatabase,
101 module: Module,
102 file_id: HirFileId,
103 items: &mut Iterator<Item = ast::ItemOrMacro>,
104 ) {
105 let file_items = db.file_items(file_id);
106
107 for item in items {
108 match item {
109 ast::ItemOrMacro::Item(it) => {
110 self.add_def_id(source_map, db, module, file_id, it);
111 }
112 ast::ItemOrMacro::Macro(macro_call) => {
113 let item_id = file_items.id_of_unchecked(macro_call.syntax());
114 let loc =
115 MacroCallLoc { module, source_item_id: SourceItemId { file_id, item_id } };
116 let id = loc.id(db);
117 let file_id = HirFileId::from(id);
118 //FIXME: expand recursively
119 for item in db.hir_parse(file_id).items() {
120 self.add_def_id(source_map, db, module, file_id, item);
121 }
122 }
123 }
124 }
125 }
126
127 fn add_def_id(
128 &mut self,
129 source_map: &mut ImportSourceMap,
130 db: &impl PersistentHirDatabase,
131 module: Module,
132 file_id: HirFileId,
133 item: &ast::ModuleItem,
134 ) {
135 let ctx = LocationCtx::new(db, module, file_id);
136 match item.kind() {
137 ast::ModuleItemKind::StructDef(it) => {
138 if let Some(name) = it.name() {
139 let s = Struct { id: ctx.to_def(it) };
140 let s: ModuleDef = s.into();
141 self.declarations.insert(name.as_name(), PerNs::both(s, s));
142 }
143 }
144 ast::ModuleItemKind::EnumDef(it) => {
145 if let Some(name) = it.name() {
146 let e = Enum { id: ctx.to_def(it) };
147 let e: ModuleDef = e.into();
148 self.declarations.insert(name.as_name(), PerNs::types(e));
149 }
150 }
151 ast::ModuleItemKind::FnDef(it) => {
152 if let Some(name) = it.name() {
153 let func = Function { id: ctx.to_def(it) };
154 self.declarations.insert(name.as_name(), PerNs::values(func.into()));
155 }
156 }
157 ast::ModuleItemKind::TraitDef(it) => {
158 if let Some(name) = it.name() {
159 let t = Trait { id: ctx.to_def(it) };
160 self.declarations.insert(name.as_name(), PerNs::types(t.into()));
161 }
162 }
163 ast::ModuleItemKind::TypeAliasDef(it) => {
164 if let Some(name) = it.name() {
165 let t = TypeAlias { id: ctx.to_def(it) };
166 self.declarations.insert(name.as_name(), PerNs::types(t.into()));
167 }
168 }
169 ast::ModuleItemKind::ImplBlock(_) => {
170 // impls don't define items
171 }
172 ast::ModuleItemKind::UseItem(it) => {
173 self.add_use_item(source_map, it);
174 }
175 ast::ModuleItemKind::ExternCrateItem(it) => {
176 if let Some(name_ref) = it.name_ref() {
177 let path = Path::from_name_ref(name_ref);
178 let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name);
179 self.imports.alloc(ImportData {
180 path,
181 alias,
182 is_glob: false,
183 is_prelude: false,
184 is_extern_crate: true,
185 });
186 }
187 }
188 ast::ModuleItemKind::ConstDef(it) => {
189 if let Some(name) = it.name() {
190 let c = Const { id: ctx.to_def(it) };
191 self.declarations.insert(name.as_name(), PerNs::values(c.into()));
192 }
193 }
194 ast::ModuleItemKind::StaticDef(it) => {
195 if let Some(name) = it.name() {
196 let s = Static { id: ctx.to_def(it) };
197 self.declarations.insert(name.as_name(), PerNs::values(s.into()));
198 }
199 }
200 ast::ModuleItemKind::Module(_) => {
201 // modules are handled separately directly by name res
202 }
203 };
204 }
205
206 fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) {
207 let is_prelude =
208 item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false));
209 Path::expand_use_item(item, |path, segment, alias| {
210 let import = self.imports.alloc(ImportData {
211 path,
212 alias,
213 is_glob: segment.is_none(),
214 is_prelude,
215 is_extern_crate: false,
216 });
217 if let Some(segment) = segment {
218 source_map.insert(import, segment)
219 }
220 })
221 }
222}
diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir/src/nameres/per_ns.rs
new file mode 100644
index 000000000..c40a3ff9d
--- /dev/null
+++ b/crates/ra_hir/src/nameres/per_ns.rs
@@ -0,0 +1,78 @@
1#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2pub enum Namespace {
3 Types,
4 Values,
5}
6
7#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
8pub struct PerNs<T> {
9 pub types: Option<T>,
10 pub values: Option<T>,
11}
12
13impl<T> Default for PerNs<T> {
14 fn default() -> Self {
15 PerNs { types: None, values: None }
16 }
17}
18
19impl<T> PerNs<T> {
20 pub fn none() -> PerNs<T> {
21 PerNs { types: None, values: None }
22 }
23
24 pub fn values(t: T) -> PerNs<T> {