aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock29
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs103
-rw-r--r--crates/ra_db/src/input.rs8
-rw-r--r--crates/ra_db/src/lib.rs20
-rw-r--r--crates/ra_hir/src/code_model.rs18
-rw-r--r--crates/ra_hir/src/semantics.rs3
-rw-r--r--crates/ra_hir/src/source_analyzer.rs3
-rw-r--r--crates/ra_hir_def/Cargo.toml3
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/data.rs2
-rw-r--r--crates/ra_hir_def/src/import_map.rs355
-rw-r--r--crates/ra_hir_def/src/lib.rs11
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs39
-rw-r--r--crates/ra_hir_def/src/path.rs20
-rw-r--r--crates/ra_hir_def/src/test_db.rs3
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs31
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs18
-rw-r--r--crates/ra_hir_expand/src/eager.rs32
-rw-r--r--crates/ra_hir_expand/src/lib.rs11
-rw-r--r--crates/ra_hir_expand/src/test_db.rs3
-rw-r--r--crates/ra_hir_ty/src/expr.rs10
-rw-r--r--crates/ra_hir_ty/src/infer.rs16
-rw-r--r--crates/ra_hir_ty/src/test_db.rs3
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs30
-rw-r--r--crates/ra_ide/src/diagnostics.rs56
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html4
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs26
-rw-r--r--crates/ra_ide/src/typing.rs38
-rw-r--r--crates/ra_ide_db/src/change.rs9
-rw-r--r--crates/ra_ide_db/src/defs.rs9
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs46
-rw-r--r--crates/ra_ide_db/src/lib.rs20
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs53
-rw-r--r--crates/ra_project_model/src/lib.rs20
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs42
-rw-r--r--crates/rust-analyzer/src/global_state.rs36
-rw-r--r--docs/dev/README.md11
39 files changed, 842 insertions, 306 deletions
diff --git a/Cargo.lock b/Cargo.lock
index df79334c9..308e36836 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -247,12 +247,13 @@ dependencies = [
247 247
248[[package]] 248[[package]]
249name = "crossbeam-queue" 249name = "crossbeam-queue"
250version = "0.2.2" 250version = "0.2.3"
251source = "registry+https://github.com/rust-lang/crates.io-index" 251source = "registry+https://github.com/rust-lang/crates.io-index"
252checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2" 252checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
253dependencies = [ 253dependencies = [
254 "cfg-if", 254 "cfg-if",
255 "crossbeam-utils", 255 "crossbeam-utils",
256 "maybe-uninit",
256] 257]
257 258
258[[package]] 259[[package]]
@@ -565,9 +566,9 @@ dependencies = [
565 566
566[[package]] 567[[package]]
567name = "jod-thread" 568name = "jod-thread"
568version = "0.1.1" 569version = "0.1.2"
569source = "registry+https://github.com/rust-lang/crates.io-index" 570source = "registry+https://github.com/rust-lang/crates.io-index"
570checksum = "4022656272c3e564a7cdebcaaba6518d844b0d0c1836597196efb5bfeb98bb49" 571checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
571 572
572[[package]] 573[[package]]
573name = "kernel32-sys" 574name = "kernel32-sys"
@@ -981,7 +982,10 @@ dependencies = [
981 "anymap", 982 "anymap",
982 "drop_bomb", 983 "drop_bomb",
983 "either", 984 "either",
985 "fst",
986 "indexmap",
984 "insta", 987 "insta",
988 "itertools",
985 "log", 989 "log",
986 "once_cell", 990 "once_cell",
987 "ra_arena", 991 "ra_arena",
@@ -1010,6 +1014,7 @@ dependencies = [
1010 "ra_prof", 1014 "ra_prof",
1011 "ra_syntax", 1015 "ra_syntax",
1012 "ra_tt", 1016 "ra_tt",
1017 "rustc-hash",
1013 "test_utils", 1018 "test_utils",
1014] 1019]
1015 1020
@@ -1516,9 +1521,9 @@ dependencies = [
1516 1521
1517[[package]] 1522[[package]]
1518name = "serde_json" 1523name = "serde_json"
1519version = "1.0.53" 1524version = "1.0.55"
1520source = "registry+https://github.com/rust-lang/crates.io-index" 1525source = "registry+https://github.com/rust-lang/crates.io-index"
1521checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" 1526checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
1522dependencies = [ 1527dependencies = [
1523 "itoa", 1528 "itoa",
1524 "ryu", 1529 "ryu",
@@ -1538,9 +1543,9 @@ dependencies = [
1538 1543
1539[[package]] 1544[[package]]
1540name = "serde_yaml" 1545name = "serde_yaml"
1541version = "0.8.12" 1546version = "0.8.13"
1542source = "registry+https://github.com/rust-lang/crates.io-index" 1547source = "registry+https://github.com/rust-lang/crates.io-index"
1543checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" 1548checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
1544dependencies = [ 1549dependencies = [
1545 "dtoa", 1550 "dtoa",
1546 "linked-hash-map", 1551 "linked-hash-map",
@@ -1581,9 +1586,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1581 1586
1582[[package]] 1587[[package]]
1583name = "syn" 1588name = "syn"
1584version = "1.0.30" 1589version = "1.0.31"
1585source = "registry+https://github.com/rust-lang/crates.io-index" 1590source = "registry+https://github.com/rust-lang/crates.io-index"
1586checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" 1591checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
1587dependencies = [ 1592dependencies = [
1588 "proc-macro2", 1593 "proc-macro2",
1589 "quote", 1594 "quote",
@@ -1592,9 +1597,9 @@ dependencies = [
1592 1597
1593[[package]] 1598[[package]]
1594name = "synstructure" 1599name = "synstructure"
1595version = "0.12.3" 1600version = "0.12.4"
1596source = "registry+https://github.com/rust-lang/crates.io-index" 1601source = "registry+https://github.com/rust-lang/crates.io-index"
1597checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" 1602checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
1598dependencies = [ 1603dependencies = [
1599 "proc-macro2", 1604 "proc-macro2",
1600 "quote", 1605 "quote",
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index edf96d50e..5092bf336 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -130,7 +130,7 @@ impl AutoImportAssets {
130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
131 let _p = profile("auto_import::search_for_imports"); 131 let _p = profile("auto_import::search_for_imports");
132 let current_crate = self.module_with_name_to_import.krate(); 132 let current_crate = self.module_with_name_to_import.krate();
133 ImportsLocator::new(db) 133 ImportsLocator::new(db, current_crate)
134 .find_imports(&self.get_search_query()) 134 .find_imports(&self.get_search_query())
135 .into_iter() 135 .into_iter()
136 .filter_map(|candidate| match &self.import_candidate { 136 .filter_map(|candidate| match &self.import_candidate {
@@ -841,4 +841,105 @@ fn main() {
841 ", 841 ",
842 ) 842 )
843 } 843 }
844
845 #[test]
846 fn dep_import() {
847 check_assist(
848 auto_import,
849 r"
850 //- /lib.rs crate:dep
851 pub struct Struct;
852
853 //- /main.rs crate:main deps:dep
854 fn main() {
855 Struct<|>
856 }",
857 r"use dep::Struct;
858
859fn main() {
860 Struct
861}
862",
863 );
864 }
865
866 #[test]
867 fn whole_segment() {
868 // Tests that only imports whose last segment matches the identifier get suggested.
869 check_assist(
870 auto_import,
871 r"
872 //- /lib.rs crate:dep
873 pub mod fmt {
874 pub trait Display {}
875 }
876
877 pub fn panic_fmt() {}
878
879 //- /main.rs crate:main deps:dep
880 struct S;
881
882 impl f<|>mt::Display for S {}",
883 r"use dep::fmt;
884
885struct S;
886impl fmt::Display for S {}
887",
888 );
889 }
890
891 #[test]
892 fn macro_generated() {
893 // Tests that macro-generated items are suggested from external crates.
894 check_assist(
895 auto_import,
896 r"
897 //- /lib.rs crate:dep
898
899 macro_rules! mac {
900 () => {
901 pub struct Cheese;
902 };
903 }
904
905 mac!();
906
907 //- /main.rs crate:main deps:dep
908
909 fn main() {
910 Cheese<|>;
911 }",
912 r"use dep::Cheese;
913
914fn main() {
915 Cheese;
916}
917",
918 );
919 }
920
921 #[test]
922 fn casing() {
923 // Tests that differently cased names don't interfere and we only suggest the matching one.
924 check_assist(
925 auto_import,
926 r"
927 //- /lib.rs crate:dep
928
929 pub struct FMT;
930 pub struct fmt;
931
932 //- /main.rs crate:main deps:dep
933
934 fn main() {
935 FMT<|>;
936 }",
937 r"use dep::FMT;
938
939fn main() {
940 FMT;
941}
942",
943 );
944 }
844} 945}
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index a8d6466ea..bf26048f2 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -15,12 +15,10 @@ use std::{
15 15
16use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
17use ra_syntax::SmolStr; 17use ra_syntax::SmolStr;
18use rustc_hash::FxHashMap; 18use ra_tt::TokenExpander;
19use rustc_hash::FxHashSet; 19use rustc_hash::{FxHashMap, FxHashSet};
20 20
21use crate::{RelativePath, RelativePathBuf}; 21use crate::{RelativePath, RelativePathBuf};
22use fmt::Display;
23use ra_tt::TokenExpander;
24 22
25/// `FileId` is an integer which uniquely identifies a file. File paths are 23/// `FileId` is an integer which uniquely identifies a file. File paths are
26/// messy and system-dependent, so most of the code should work directly with 24/// messy and system-dependent, so most of the code should work directly with
@@ -111,7 +109,7 @@ impl CrateName {
111 } 109 }
112} 110}
113 111
114impl Display for CrateName { 112impl fmt::Display for CrateName {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 write!(f, "{}", self.0) 114 write!(f, "{}", self.0)
117 } 115 }
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index 2ab314884..80ddb6058 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -7,6 +7,7 @@ use std::{panic, sync::Arc};
7 7
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; 9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10use rustc_hash::FxHashSet;
10 11
11pub use crate::{ 12pub use crate::{
12 cancellation::Canceled, 13 cancellation::Canceled,
@@ -95,7 +96,7 @@ pub trait FileLoader {
95 /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we 96 /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we
96 /// get by with a `&str` for the time being. 97 /// get by with a `&str` for the time being.
97 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; 98 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>;
98 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; 99 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
99} 100}
100 101
101/// Database which stores all significant input facts: source code and project 102/// Database which stores all significant input facts: source code and project
@@ -133,16 +134,21 @@ pub trait SourceDatabaseExt: SourceDatabase {
133 #[salsa::input] 134 #[salsa::input]
134 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; 135 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
135 136
136 fn source_root_crates(&self, id: SourceRootId) -> Arc<Vec<CrateId>>; 137 fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
137} 138}
138 139
139fn source_root_crates( 140fn source_root_crates(
140 db: &(impl SourceDatabaseExt + SourceDatabase), 141 db: &(impl SourceDatabaseExt + SourceDatabase),
141 id: SourceRootId, 142 id: SourceRootId,
142) -> Arc<Vec<CrateId>> { 143) -> Arc<FxHashSet<CrateId>> {
143 let root = db.source_root(id);
144 let graph = db.crate_graph(); 144 let graph = db.crate_graph();
145 let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::<Vec<_>>(); 145 let res = graph
146 .iter()
147 .filter(|&krate| {
148 let root_file = graph[krate].root_file_id;
149 db.file_source_root(root_file) == id
150 })
151 .collect::<FxHashSet<_>>();
146 Arc::new(res) 152 Arc::new(res)
147} 153}
148 154
@@ -156,7 +162,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
156 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 162 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
157 // FIXME: this *somehow* should be platform agnostic... 163 // FIXME: this *somehow* should be platform agnostic...
158 if std::path::Path::new(path).is_absolute() { 164 if std::path::Path::new(path).is_absolute() {
159 let krate = *self.relevant_crates(anchor).get(0)?; 165 let krate = *self.relevant_crates(anchor).iter().next()?;
160 let (extern_source_id, relative_file) = 166 let (extern_source_id, relative_file) =
161 self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?; 167 self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?;
162 168
@@ -175,7 +181,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
175 } 181 }
176 } 182 }
177 183
178 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 184 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
179 let source_root = self.0.file_source_root(file_id); 185 let source_root = self.0.file_source_root(file_id);
180 self.0.source_root_crates(source_root) 186 self.0.source_root_crates(source_root)
181 } 187 }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 4a06f3bcd..1a9f6cc76 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -9,6 +9,7 @@ use hir_def::{
9 builtin_type::BuiltinType, 9 builtin_type::BuiltinType,
10 docs::Documentation, 10 docs::Documentation,
11 expr::{BindingAnnotation, Pat, PatId}, 11 expr::{BindingAnnotation, Pat, PatId},
12 import_map,
12 per_ns::PerNs, 13 per_ns::PerNs,
13 resolver::{HasResolver, Resolver}, 14 resolver::{HasResolver, Resolver},
14 type_ref::{Mutability, TypeRef}, 15 type_ref::{Mutability, TypeRef},
@@ -98,6 +99,23 @@ impl Crate {
98 db.crate_graph()[self.id].display_name.as_ref().cloned() 99 db.crate_graph()[self.id].display_name.as_ref().cloned()
99 } 100 }
100 101
102 pub fn query_external_importables(
103 self,
104 db: &dyn DefDatabase,
105 query: &str,
106 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
107 import_map::search_dependencies(
108 db,
109 self.into(),
110 import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
111 )
112 .into_iter()
113 .map(|item| match item {
114 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
115 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
116 })
117 }
118
101 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { 119 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
102 db.crate_graph().iter().map(|id| Crate { id }).collect() 120 db.crate_graph().iter().map(|id| Crate { id }).collect()
103 } 121 }
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 7c1f79f27..a232a5856 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -122,8 +122,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
122 let macro_call = 122 let macro_call =
123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
125 let krate = sa.resolver.krate()?;
125 let macro_call_id = macro_call 126 let macro_call_id = macro_call
126 .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; 127 .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?;
127 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) 128 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map)
128 } 129 }
129 130
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 4b509f07c..7c6bbea13 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -307,7 +307,8 @@ impl SourceAnalyzer {
307 db: &dyn HirDatabase, 307 db: &dyn HirDatabase,
308 macro_call: InFile<&ast::MacroCall>, 308 macro_call: InFile<&ast::MacroCall>,
309 ) -> Option<HirFileId> { 309 ) -> Option<HirFileId> {
310 let macro_call_id = macro_call.as_call_id(db.upcast(), |path| { 310 let krate = self.resolver.krate()?;
311 let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
311 self.resolver.resolve_path_as_macro(db.upcast(), &path) 312 self.resolver.resolve_path_as_macro(db.upcast(), &path)
312 })?; 313 })?;
313 Some(macro_call_id.as_file()) 314 Some(macro_call_id.as_file())
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index b85358308..ef1f65ee0 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -14,6 +14,9 @@ rustc-hash = "1.1.0"
14either = "1.5.3" 14either = "1.5.3"
15anymap = "0.12.1" 15anymap = "0.12.1"
16drop_bomb = "0.1.4" 16drop_bomb = "0.1.4"
17fst = { version = "0.4", default-features = false }
18itertools = "0.9.0"
19indexmap = "1.4.0"
17 20
18stdx = { path = "../stdx" } 21stdx = { path = "../stdx" }
19 22
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 273036cee..4f2350915 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -97,7 +97,7 @@ impl Expander {
97 97
98 let macro_call = InFile::new(self.current_file_id, &macro_call); 98 let macro_call = InFile::new(self.current_file_id, &macro_call);
99 99
100 if let Some(call_id) = macro_call.as_call_id(db, |path| { 100 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
101 if let Some(local_scope) = local_scope { 101 if let Some(local_scope) = local_scope {
102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { 102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
103 return Some(def); 103 return Some(def);
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 807195d25..53599e74a 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -99,7 +99,7 @@ impl FunctionData {
99} 99}
100 100
101fn desugar_future_path(orig: TypeRef) -> Path { 101fn desugar_future_path(orig: TypeRef) -> Path {
102 let path = path![std::future::Future]; 102 let path = path![core::future::Future];
103 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); 103 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
104 let mut last = GenericArgs::empty(); 104 let mut last = GenericArgs::empty();
105 last.bindings.push(AssociatedTypeBinding { 105 last.bindings.push(AssociatedTypeBinding {
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
index 4284a0a91..36b4fdd81 100644
--- a/crates/ra_hir_def/src/import_map.rs
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -1,9 +1,11 @@
1//! A map of all publicly exported items in a crate. 1//! A map of all publicly exported items in a crate.
2 2
3use std::{collections::hash_map::Entry, fmt, sync::Arc}; 3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4 4
5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap};
5use ra_db::CrateId; 7use ra_db::CrateId;
6use rustc_hash::FxHashMap; 8use rustc_hash::FxHasher;
7 9
8use crate::{ 10use crate::{
9 db::DefDatabase, 11 db::DefDatabase,
@@ -13,6 +15,8 @@ use crate::{
13 ModuleDefId, ModuleId, 15 ModuleDefId, ModuleId,
14}; 16};
15 17
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
19
16/// A map from publicly exported items to the path needed to import/name them from a downstream 20/// A map from publicly exported items to the path needed to import/name them from a downstream
17/// crate. 21/// crate.
18/// 22///
@@ -21,16 +25,24 @@ use crate::{
21/// 25///
22/// Note that all paths are relative to the containing crate's root, so the crate name still needs 26/// Note that all paths are relative to the containing crate's root, so the crate name still needs
23/// to be prepended to the `ModPath` before the path is valid. 27/// to be prepended to the `ModPath` before the path is valid.
24#[derive(Eq, PartialEq)]
25pub struct ImportMap { 28pub struct ImportMap {
26 map: FxHashMap<ItemInNs, ModPath>, 29 map: FxIndexMap<ItemInNs, ModPath>,
30
31 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
32 /// values returned by running `fst`.
33 ///
34 /// Since a path can refer to multiple items due to namespacing, we store all items with the
35 /// same path right after each other. This allows us to find all items after the FST gives us
36 /// the index of the first one.
37 importables: Vec<ItemInNs>,
38 fst: fst::Map<Vec<u8>>,
27} 39}
28 40
29impl ImportMap { 41impl ImportMap {
30 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { 42 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
31 let _p = ra_prof::profile("import_map_query"); 43 let _p = ra_prof::profile("import_map_query");
32 let def_map = db.crate_def_map(krate); 44 let def_map = db.crate_def_map(krate);
33 let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); 45 let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default());
34 46
35 // We look only into modules that are public(ly reexported), starting with the crate root. 47 // We look only into modules that are public(ly reexported), starting with the crate root.
36 let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; 48 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
@@ -88,7 +100,34 @@ impl ImportMap {
88 } 100 }
89 } 101 }
90 102
91 Arc::new(Self { map: import_map }) 103 let mut importables = import_map.iter().collect::<Vec<_>>();
104
105 importables.sort_by(cmp);
106
107 // Build the FST, taking care not to insert duplicate values.
108
109 let mut builder = fst::MapBuilder::memory();
110 let mut last_batch_start = 0;
111
112 for idx in 0..importables.len() {
113 if let Some(next_item) = importables.get(idx + 1) {
114 if cmp(&importables[last_batch_start], next_item) == Ordering::Equal {
115 continue;
116 }
117 }
118
119 let start = last_batch_start;
120 last_batch_start = idx + 1;
121
122 let key = fst_path(&importables[start].1);
123
124 builder.insert(key, start as u64).unwrap();
125 }
126
127 let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
128 let importables = importables.iter().map(|(item, _)| **item).collect();
129
130 Arc::new(Self { map: import_map, fst, importables })
92 } 131 }
93 132
94 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. 133 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
@@ -97,6 +136,15 @@ impl ImportMap {
97 } 136 }
98} 137}
99 138
139impl PartialEq for ImportMap {
140 fn eq(&self, other: &Self) -> bool {
141 // `fst` and `importables` are built from `map`, so we don't need to compare them.
142 self.map == other.map
143 }
144}
145
146impl Eq for ImportMap {}
147
100impl fmt::Debug for ImportMap { 148impl fmt::Debug for ImportMap {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 let mut importable_paths: Vec<_> = self 150 let mut importable_paths: Vec<_> = self
@@ -117,19 +165,135 @@ impl fmt::Debug for ImportMap {
117 } 165 }
118} 166}
119 167
168fn fst_path(path: &ModPath) -> String {
169 let mut s = path.to_string();
170 s.make_ascii_lowercase();
171 s
172}
173
174fn cmp((_, lhs): &(&ItemInNs, &ModPath), (_, rhs): &(&ItemInNs, &ModPath)) -> Ordering {
175 let lhs_str = fst_path(lhs);
176 let rhs_str = fst_path(rhs);
177 lhs_str.cmp(&rhs_str)
178}
179
180#[derive(Debug)]
181pub struct Query {
182 query: String,
183 lowercased: String,
184 anchor_end: bool,
185 case_sensitive: bool,
186 limit: usize,
187}
188
189impl Query {
190 pub fn new(query: &str) -> Self {
191 Self {
192 lowercased: query.to_lowercase(),
193 query: query.to_string(),
194 anchor_end: false,
195 case_sensitive: false,
196 limit: usize::max_value(),
197 }
198 }
199
200 /// Only returns items whose paths end with the (case-insensitive) query string as their last
201 /// segment.
202 pub fn anchor_end(self) -> Self {
203 Self { anchor_end: true, ..self }
204 }
205
206 /// Limits the returned number of items to `limit`.
207 pub fn limit(self, limit: usize) -> Self {
208 Self { limit, ..self }
209 }
210
211 /// Respect casing of the query string when matching.
212 pub fn case_sensitive(self) -> Self {
213 Self { case_sensitive: true, ..self }
214 }
215}
216
217/// Searches dependencies of `krate` for an importable path matching `query`.
218///
219/// This returns a list of items that could be imported from dependencies of `krate`.
220pub fn search_dependencies<'a>(
221 db: &'a dyn DefDatabase,
222 krate: CrateId,
223 query: Query,
224) -> Vec<ItemInNs> {
225 let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query));
226
227 let graph = db.crate_graph();
228 let import_maps: Vec<_> =
229 graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
230
231 let automaton = fst::automaton::Subsequence::new(&query.lowercased);
232
233 let mut op = fst::map::OpBuilder::new();
234 for map in &import_maps {
235 op = op.add(map.fst.search(&automaton));
236 }
237
238 let mut stream = op.union();
239 let mut res = Vec::new();
240 while let Some((_, indexed_values)) = stream.next() {
241 for indexed_value in indexed_values {
242 let import_map = &import_maps[indexed_value.index];
243 let importables = &import_map.importables[indexed_value.value as usize..];
244
245 // Path shared by the importable items in this group.
246 let path = &import_map.map[&importables[0]];
247
248 if query.anchor_end {
249 // Last segment must match query.
250 let last = path.segments.last().unwrap().to_string();
251 if last.to_lowercase() != query.lowercased {
252 continue;
253 }
254 }
255
256 // Add the items from this `ModPath` group. Those are all subsequent items in
257 // `importables` whose paths match `path`.
258 let iter = importables.iter().copied().take_while(|item| {
259 let item_path = &import_map.map[item];
260 fst_path(item_path) == fst_path(path)
261 });
262
263 if query.case_sensitive {
264 // FIXME: This does not do a subsequence match.
265 res.extend(iter.filter(|item| {
266 let item_path = &import_map.map[item];
267 item_path.to_string().contains(&query.query)
268 }));
269 } else {
270 res.extend(iter);
271 }
272
273 if res.len() >= query.limit {
274 res.truncate(query.limit);
275 return res;
276 }
277 }
278 }
279
280 res
281}
282
120#[cfg(test)] 283#[cfg(test)]
121mod tests { 284mod tests {
122 use super::*; 285 use super::*;
123 use crate::test_db::TestDB; 286 use crate::test_db::TestDB;
124 use insta::assert_snapshot; 287 use insta::assert_snapshot;
288 use itertools::Itertools;
125 use ra_db::fixture::WithFixture; 289 use ra_db::fixture::WithFixture;
126 use ra_db::SourceDatabase; 290 use ra_db::{SourceDatabase, Upcast};
127 291
128 fn import_map(ra_fixture: &str) -> String { 292 fn import_map(ra_fixture: &str) -> String {
129 let db = TestDB::with_files(ra_fixture); 293 let db = TestDB::with_files(ra_fixture);
130 let crate_graph = db.crate_graph(); 294 let crate_graph = db.crate_graph();
131 295
132 let import_maps: Vec<_> = crate_graph 296 let s = crate_graph
133 .iter() 297 .iter()
134 .filter_map(|krate| { 298 .filter_map(|krate| {
135 let cdata = &crate_graph[krate]; 299 let cdata = &crate_graph[krate];
@@ -139,9 +303,41 @@ mod tests {
139 303
140 Some(format!("{}:\n{:?}", name, map)) 304 Some(format!("{}:\n{:?}", name, map))
141 }) 305 })
142 .collect(); 306 .join("\n");
307 s
308 }
143 309
144 import_maps.join("\n") 310 fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String {
311 let db = TestDB::with_files(ra_fixture);
312 let crate_graph = db.crate_graph();
313 let krate = crate_graph
314 .iter()
315 .find(|krate| {
316 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
317 == Some(krate_name.to_string())
318 })
319 .unwrap();
320
321 search_dependencies(db.upcast(), krate, query)
322 .into_iter()
323 .filter_map(|item| {
324 let mark = match item {
325 ItemInNs::Types(_) => "t",
326 ItemInNs::Values(_) => "v",
327 ItemInNs::Macros(_) => "m",
328 };
329 item.krate(db.upcast()).map(|krate| {
330 let map = db.import_map(krate);
331 let path = map.path_of(item).unwrap();
332 format!(
333 "{}::{} ({})",
334 crate_graph[krate].display_name.as_ref().unwrap(),
335 path,
336 mark
337 )
338 })
339 })
340 .join("\n")
145 } 341 }
146 342
147 #[test] 343 #[test]
@@ -328,4 +524,143 @@ mod tests {
328 lib: 524 lib:
329 "###); 525 "###);
330 } 526 }
527
528 #[test]
529 fn namespacing() {
530 let map = import_map(
531 r"
532 //- /lib.rs crate:lib
533 pub struct Thing; // t + v
534 #[macro_export]
535 macro_rules! Thing { // m
536 () => {};
537 }
538 ",
539 );
540
541 assert_snapshot!(map, @r###"
542 lib:
543 - Thing (m)
544 - Thing (t)
545 - Thing (v)
546 "###);
547
548 let map = import_map(
549 r"
550 //- /lib.rs crate:lib
551 pub mod Thing {} // t
552 #[macro_export]
553 macro_rules! Thing { // m
554 () => {};
555 }
556 ",
557 );
558
559 assert_snapshot!(map, @r###"
560 lib:
561 - Thing (m)
562 - Thing (t)
563 "###);
564 }
565
566 #[test]
567 fn search() {
568 let ra_fixture = r#"
569 //- /main.rs crate:main deps:dep
570 //- /dep.rs crate:dep deps:tdep
571 use tdep::fmt as fmt_dep;
572 pub mod fmt {
573 pub trait Display {
574 fn fmt();
575 }
576 }
577 #[macro_export]
578 macro_rules! Fmt {
579 () => {};
580 }
581 pub struct Fmt;
582
583 pub fn format() {}
584 pub fn no() {}
585
586 //- /tdep.rs crate:tdep
587 pub mod fmt {
588 pub struct NotImportableFromMain;
589 }
590 "#;
591
592 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt"));
593 assert_snapshot!(res, @r###"
594 dep::fmt (t)
595 dep::Fmt (t)
596 dep::Fmt (v)
597 dep::Fmt (m)
598 dep::fmt::Display (t)
599 dep::format (v)
600 "###);
601
602 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end());
603 assert_snapshot!(res, @r###"
604 dep::fmt (t)
605 dep::Fmt (t)
606 dep::Fmt (v)
607 dep::Fmt (m)
608 "###);
609 }
610
611 #[test]
612 fn search_casing() {
613 let ra_fixture = r#"
614 //- /main.rs crate:main deps:dep
615 //- /dep.rs crate:dep
616
617 pub struct fmt;
618 pub struct FMT;
619 "#;
620
621 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT"));
622
623 assert_snapshot!(res, @r###"
624 dep::fmt (t)
625 dep::fmt (v)
626 dep::FMT (t)
627 dep::FMT (v)
628 "###);
629
630 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive());
631
632 assert_snapshot!(res, @r###"
633 dep::FMT (t)
634 dep::FMT (v)
635 "###);
636 }
637
638 #[test]
639 fn search_limit() {
640 let res = search_dependencies_of(
641 r#"
642 //- /main.rs crate:main deps:dep
643 //- /dep.rs crate:dep
644 pub mod fmt {
645 pub trait Display {
646 fn fmt();
647 }
648 }
649 #[macro_export]
650 macro_rules! Fmt {
651 () => {};
652 }
653 pub struct Fmt;
654
655 pub fn format() {}
656 pub fn no() {}
657 "#,
658 "main",
659 Query::new("").limit(2),
660 );
661 assert_snapshot!(res, @r###"
662 dep::fmt (t)
663 dep::Fmt (t)
664 "###);
665 }
331} 666}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index de490fcc5..edc59e5a8 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -417,6 +417,7 @@ pub trait AsMacroCall {
417 fn as_call_id( 417 fn as_call_id(
418 &self, 418 &self,
419 db: &dyn db::DefDatabase, 419 db: &dyn db::DefDatabase,
420 krate: CrateId,
420 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 421 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
421 ) -> Option<MacroCallId>; 422 ) -> Option<MacroCallId>;
422} 423}
@@ -425,13 +426,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
425 fn as_call_id( 426 fn as_call_id(
426 &self, 427 &self,
427 db: &dyn db::DefDatabase, 428 db: &dyn db::DefDatabase,
429 krate: CrateId,
428 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 430 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
429 ) -> Option<MacroCallId> { 431 ) -> Option<MacroCallId> {
430 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 432 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
431 let h = Hygiene::new(db.upcast(), self.file_id); 433 let h = Hygiene::new(db.upcast(), self.file_id);
432 let path = path::ModPath::from_src(self.value.path()?, &h)?; 434 let path = path::ModPath::from_src(self.value.path()?, &h)?;
433 435
434 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) 436 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
435 } 437 }
436} 438}
437 439
@@ -452,6 +454,7 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
452 fn as_call_id( 454 fn as_call_id(
453 &self, 455 &self,
454 db: &dyn db::DefDatabase, 456 db: &dyn db::DefDatabase,
457 krate: CrateId,
455 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 458 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
456 ) -> Option<MacroCallId> { 459 ) -> Option<MacroCallId> {
457 let def: MacroDefId = resolver(self.path.clone())?; 460 let def: MacroDefId = resolver(self.path.clone())?;
@@ -461,13 +464,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
461 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); 464 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
462 465
463 Some( 466 Some(
464 expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { 467 expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
465 resolver(path::ModPath::from_src(path, &hygiene)?) 468 resolver(path::ModPath::from_src(path, &hygiene)?)
466 })? 469 })?
467 .into(), 470 .into(),
468 ) 471 )
469 } else { 472 } else {
470 Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) 473 Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into())
471 } 474 }
472 } 475 }
473} 476}
@@ -476,12 +479,14 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
476 fn as_call_id( 479 fn as_call_id(
477 &self, 480 &self,
478 db: &dyn db::DefDatabase, 481 db: &dyn db::DefDatabase,
482 krate: CrateId,
479 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 483 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
480 ) -> Option<MacroCallId> { 484 ) -> Option<MacroCallId> {
481 let def = resolver(self.path.clone())?; 485 let def = resolver(self.path.clone())?;
482 Some( 486 Some(
483 def.as_lazy_macro( 487 def.as_lazy_macro(
484 db.upcast(), 488 db.upcast(),
489 krate,
485 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), 490 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()),
486 ) 491 )
487 .into(), 492 .into(),
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 353a31ad4..976e5e585 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -571,16 +571,18 @@ impl DefCollector<'_> {
571 return false; 571 return false;
572 } 572 }
573 573
574 if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { 574 if let Some(call_id) =
575 let resolved_res = self.def_map.resolve_path_fp_with_macro( 575 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
576 self.db, 576 let resolved_res = self.def_map.resolve_path_fp_with_macro(
577 ResolveMode::Other, 577 self.db,
578 directive.module_id, 578 ResolveMode::Other,
579 &path, 579 directive.module_id,
580 BuiltinShadowMode::Module, 580 &path,
581 ); 581 BuiltinShadowMode::Module,
582 resolved_res.resolved_def.take_macros() 582 );
583 }) { 583 resolved_res.resolved_def.take_macros()
584 })
585 {
584 resolved.push((directive.module_id, call_id, directive.depth)); 586 resolved.push((directive.module_id, call_id, directive.depth));
585 res = ReachedFixedPoint::No; 587 res = ReachedFixedPoint::No;
586 return false; 588 return false;
@@ -589,9 +591,10 @@ impl DefCollector<'_> {
589 true 591 true
590 }); 592 });
591 attribute_macros.retain(|directive| { 593 attribute_macros.retain(|directive| {
592 if let Some(call_id) = directive 594 if let Some(call_id) =
593 .ast_id 595 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
594 .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) 596 self.resolve_attribute_macro(&directive, &path)
597 })
595 { 598 {
596 resolved.push((directive.module_id, call_id, 0)); 599 resolved.push((directive.module_id, call_id, 0));
597 res = ReachedFixedPoint::No; 600 res = ReachedFixedPoint::No;
@@ -957,11 +960,13 @@ impl ModCollector<'_, '_> {
957 } 960 }
958 961
959 // Case 2: try to resolve in legacy scope and expand macro_rules 962 // Case 2: try to resolve in legacy scope and expand macro_rules
960 if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { 963 if let Some(macro_call_id) =
961 path.as_ident().and_then(|name| { 964 ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| {
962 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) 965 path.as_ident().and_then(|name| {
966 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
967 })
963 }) 968 })
964 }) { 969 {
965 self.def_collector.unexpanded_macros.push(MacroDirective { 970 self.def_collector.unexpanded_macros.push(MacroDirective {
966 module_id: self.module_id, 971 module_id: self.module_id,
967 ast_id, 972 ast_id,
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index bfa921de2..ba16442bd 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -323,16 +323,16 @@ pub use hir_expand::name as __name;
323 323
324#[macro_export] 324#[macro_export]
325macro_rules! __known_path { 325macro_rules! __known_path {
326 (std::iter::IntoIterator) => {}; 326 (core::iter::IntoIterator) => {};
327 (std::result::Result) => {}; 327 (core::result::Result) => {};
328 (std::ops::Range) => {}; 328 (core::ops::Range) => {};
329 (std::ops::RangeFrom) => {}; 329 (core::ops::RangeFrom) => {};
330 (std::ops::RangeFull) => {}; 330 (core::ops::RangeFull) => {};
331 (std::ops::RangeTo) => {}; 331 (core::ops::RangeTo) => {};
332 (std::ops::RangeToInclusive) => {}; 332 (core::ops::RangeToInclusive) => {};
333 (std::ops::RangeInclusive) => {}; 333 (core::ops::RangeInclusive) => {};
334 (std::future::Future) => {}; 334 (core::future::Future) => {};
335 (std::ops::Try) => {}; 335 (core::ops::Try) => {};
336 ($path:path) => { 336 ($path:path) => {
337 compile_error!("Please register your known path in the path module") 337 compile_error!("Please register your known path in the path module")
338 }; 338 };
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index bcfa66ac9..4581d8745 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -7,6 +7,7 @@ use std::{
7 7
8use hir_expand::db::AstDatabase; 8use hir_expand::db::AstDatabase;
9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
10use rustc_hash::FxHashSet;
10 11
11use crate::db::DefDatabase; 12use crate::db::DefDatabase;
12 13
@@ -59,7 +60,7 @@ impl FileLoader for TestDB {
59 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
60 FileLoaderDelegate(self).resolve_path(anchor, path) 61 FileLoaderDelegate(self).resolve_path(anchor, path)
61 } 62 }
62 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 63 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
63 FileLoaderDelegate(self).relevant_crates(file_id) 64 FileLoaderDelegate(self).relevant_crates(file_id)
64 } 65 }
65} 66}
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index 2cd522766..e5c9f3e99 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -10,6 +10,7 @@ doctest = false
10[dependencies] 10[dependencies]
11log = "0.4.8" 11log = "0.4.8"
12either = "1.5.3" 12either = "1.5.3"
13rustc-hash = "1.0.0"
13 14
14ra_arena = { path = "../ra_arena" } 15ra_arena = { path = "../ra_arena" }
15ra_db = { path = "../ra_db" } 16ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index 1dc9cac66..26b667b55 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -8,8 +8,7 @@ use ra_syntax::{
8 match_ast, 8 match_ast,
9}; 9};
10 10
11use crate::db::AstDatabase; 11use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind};
12use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => { 14 ( $($trait:ident => $expand:ident),* ) => {
@@ -156,23 +155,13 @@ fn expand_simple_derive(
156fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { 155fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
157 // FIXME: make hygiene works for builtin derive macro 156 // FIXME: make hygiene works for builtin derive macro
158 // such that $crate can be used here. 157 // such that $crate can be used here.
159
160 let m: MacroCallId = id.into();
161 let file_id = m.as_file().original_file(db);
162 let cg = db.crate_graph(); 158 let cg = db.crate_graph();
163 let krates = db.relevant_crates(file_id); 159 let krate = db.lookup_intern_macro(id).krate;
164 let krate = match krates.get(0) {
165 Some(krate) => krate,
166 None => {
167 let tt = quote! { core };
168 return tt.token_trees[0].clone();
169 }
170 };
171 160
172 // XXX 161 // XXX
173 // All crates except core itself should have a dependency on core, 162 // All crates except core itself should have a dependency on core,
174 // We detect `core` by seeing whether it doesn't have such a dependency. 163 // We detect `core` by seeing whether it doesn't have such a dependency.
175 let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") { 164 let tt = if cg[krate].dependencies.iter().any(|dep| dep.name == "core") {
176 quote! { core } 165 quote! { core }
177 } else { 166 } else {
178 quote! { crate } 167 quote! { crate }
@@ -264,10 +253,12 @@ fn partial_ord_expand(
264 253
265#[cfg(test)] 254#[cfg(test)]
266mod tests { 255mod tests {
267 use super::*;
268 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
269 use name::{known, Name}; 256 use name::{known, Name};
270 use ra_db::{fixture::WithFixture, SourceDatabase}; 257 use ra_db::{fixture::WithFixture, CrateId, SourceDatabase};
258
259 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
260
261 use super::*;
271 262
272 fn expand_builtin_derive(s: &str, name: Name) -> String { 263 fn expand_builtin_derive(s: &str, name: Name) -> String {
273 let def = find_builtin_derive(&name).unwrap(); 264 let def = find_builtin_derive(&name).unwrap();
@@ -291,7 +282,11 @@ mod tests {
291 282
292 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); 283 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
293 284
294 let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) }; 285 let loc = MacroCallLoc {
286 def,
287 krate: CrateId(0),
288 kind: MacroCallKind::Attr(attr_id, name.to_string()),
289 };
295 290
296 let id: MacroCallId = db.intern_macro(loc).into(); 291 let id: MacroCallId = db.intern_macro(loc).into();
297 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 292 let parsed = db.parse_or_expand(id.as_file()).unwrap();
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 7579546d2..b50eb347c 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -1,15 +1,14 @@
1//! Builtin macro 1//! Builtin macro
2use crate::db::AstDatabase;
3use crate::{ 2use crate::{
4 ast::{self, AstToken, HasStringValue}, 3 db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId,
5 name, AstId, CrateId, MacroDefId, MacroDefKind, TextSize, 4 MacroDefId, MacroDefKind, TextSize,
6}; 5};
7 6
8use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId};
9use either::Either; 7use either::Either;
10use mbe::parse_to_token_tree; 8use mbe::parse_to_token_tree;
11use ra_db::FileId; 9use ra_db::FileId;
12use ra_parser::FragmentKind; 10use ra_parser::FragmentKind;
11use ra_syntax::ast::{self, AstToken, HasStringValue};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { 14 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
@@ -333,10 +332,7 @@ fn include_expand(
333} 332}
334 333
335fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { 334fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
336 let call_id: MacroCallId = arg_id.into(); 335 let krate = db.lookup_intern_eager_expansion(arg_id).krate;
337 let original_file = call_id.as_file().original_file(db);
338
339 let krate = *db.relevant_crates(original_file).get(0)?;
340 db.crate_graph()[krate].env.get(key) 336 db.crate_graph()[krate].env.get(key)
341} 337}
342 338
@@ -395,6 +391,7 @@ mod tests {
395 391
396 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 392 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
397 393
394 let krate = CrateId(0);
398 let file_id = match expander { 395 let file_id = match expander {
399 Either::Left(expander) => { 396 Either::Left(expander) => {
400 // the first one should be a macro_rules 397 // the first one should be a macro_rules
@@ -407,6 +404,7 @@ mod tests {
407 404
408 let loc = MacroCallLoc { 405 let loc = MacroCallLoc {
409 def, 406 def,
407 krate,
410 kind: MacroCallKind::FnLike(AstId::new( 408 kind: MacroCallKind::FnLike(AstId::new(
411 file_id.into(), 409 file_id.into(),
412 ast_id_map.ast_id(&macro_calls[1]), 410 ast_id_map.ast_id(&macro_calls[1]),
@@ -419,7 +417,7 @@ mod tests {
419 Either::Right(expander) => { 417 Either::Right(expander) => {
420 // the first one should be a macro_rules 418 // the first one should be a macro_rules
421 let def = MacroDefId { 419 let def = MacroDefId {
422 krate: Some(CrateId(0)), 420 krate: Some(krate),
423 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))), 421 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
424 kind: MacroDefKind::BuiltInEager(expander), 422 kind: MacroDefKind::BuiltInEager(expander),
425 local_inner: false, 423 local_inner: false,
@@ -433,6 +431,7 @@ mod tests {
433 def, 431 def,
434 fragment: FragmentKind::Expr, 432 fragment: FragmentKind::Expr,
435 subtree: Arc::new(parsed_args.clone()), 433 subtree: Arc::new(parsed_args.clone()),
434 krate,
436 file_id: file_id.into(), 435 file_id: file_id.into(),
437 } 436 }
438 }); 437 });
@@ -442,6 +441,7 @@ mod tests {
442 def, 441 def,
443 fragment, 442 fragment,
444 subtree: Arc::new(subtree), 443 subtree: Arc::new(subtree),
444 krate,
445 file_id: file_id.into(), 445 file_id: file_id.into(),
446 }; 446 };
447 447
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
index 932f47c30..302d2b3e0 100644
--- a/crates/ra_hir_expand/src/eager.rs
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -25,12 +25,14 @@ use crate::{
25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
26}; 26};
27 27
28use ra_db::CrateId;
28use ra_parser::FragmentKind; 29use ra_parser::FragmentKind;
29use ra_syntax::{algo::SyntaxRewriter, SyntaxNode}; 30use ra_syntax::{algo::SyntaxRewriter, SyntaxNode};
30use std::sync::Arc; 31use std::sync::Arc;
31 32
32pub fn expand_eager_macro( 33pub fn expand_eager_macro(
33 db: &dyn AstDatabase, 34 db: &dyn AstDatabase,
35 krate: CrateId,
34 macro_call: InFile<ast::MacroCall>, 36 macro_call: InFile<ast::MacroCall>,
35 def: MacroDefId, 37 def: MacroDefId,
36 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
@@ -47,6 +49,7 @@ pub fn expand_eager_macro(
47 def, 49 def,
48 fragment: FragmentKind::Expr, 50 fragment: FragmentKind::Expr,
49 subtree: Arc::new(parsed_args.clone()), 51 subtree: Arc::new(parsed_args.clone()),
52 krate,
50 file_id: macro_call.file_id, 53 file_id: macro_call.file_id,
51 } 54 }
52 }); 55 });
@@ -56,14 +59,20 @@ pub fn expand_eager_macro(
56 let result = eager_macro_recur( 59 let result = eager_macro_recur(
57 db, 60 db,
58 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), 61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate,
59 resolver, 63 resolver,
60 )?; 64 )?;
61 let subtree = to_subtree(&result)?; 65 let subtree = to_subtree(&result)?;
62 66
63 if let MacroDefKind::BuiltInEager(eager) = def.kind { 67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
64 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; 68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
65 let eager = 69 let eager = EagerCallLoc {
66 EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; 70 def,
71 fragment,
72 subtree: Arc::new(subtree),
73 krate,
74 file_id: macro_call.file_id,
75 };
67 76
68 Some(db.intern_eager_expansion(eager)) 77 Some(db.intern_eager_expansion(eager))
69 } else { 78 } else {
@@ -81,11 +90,12 @@ fn lazy_expand(
81 db: &dyn AstDatabase, 90 db: &dyn AstDatabase,
82 def: &MacroDefId, 91 def: &MacroDefId,
83 macro_call: InFile<ast::MacroCall>, 92 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId,
84) -> Option<InFile<SyntaxNode>> { 94) -> Option<InFile<SyntaxNode>> {
85 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
86 96
87 let id: MacroCallId = 97 let id: MacroCallId =
88 def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
89 99
90 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) 100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
91} 101}
@@ -93,6 +103,7 @@ fn lazy_expand(
93fn eager_macro_recur( 103fn eager_macro_recur(
94 db: &dyn AstDatabase, 104 db: &dyn AstDatabase,
95 curr: InFile<SyntaxNode>, 105 curr: InFile<SyntaxNode>,
106 krate: CrateId,
96 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
97) -> Option<SyntaxNode> { 108) -> Option<SyntaxNode> {
98 let original = curr.value.clone(); 109 let original = curr.value.clone();
@@ -105,18 +116,23 @@ fn eager_macro_recur(
105 let def: MacroDefId = macro_resolver(child.path()?)?; 116 let def: MacroDefId = macro_resolver(child.path()?)?;
106 let insert = match def.kind { 117 let insert = match def.kind {
107 MacroDefKind::BuiltInEager(_) => { 118 MacroDefKind::BuiltInEager(_) => {
108 let id: MacroCallId = 119 let id: MacroCallId = expand_eager_macro(
109 expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? 120 db,
110 .into(); 121 krate,
122 curr.with_value(child.clone()),
123 def,
124 macro_resolver,
125 )?
126 .into();
111 db.parse_or_expand(id.as_file())? 127 db.parse_or_expand(id.as_file())?
112 } 128 }
113 MacroDefKind::Declarative 129 MacroDefKind::Declarative
114 | MacroDefKind::BuiltIn(_) 130 | MacroDefKind::BuiltIn(_)
115 | MacroDefKind::BuiltInDerive(_) 131 | MacroDefKind::BuiltInDerive(_)
116 | MacroDefKind::CustomDerive(_) => { 132 | MacroDefKind::CustomDerive(_) => {
117 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; 133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
118 // replace macro inside 134 // replace macro inside
119 eager_macro_recur(db, expanded, macro_resolver)? 135 eager_macro_recur(db, expanded, krate, macro_resolver)?
120 } 136 }
121 }; 137 };
122 138
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index f440c073b..5eac2605b 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -209,8 +209,13 @@ pub struct MacroDefId {
209} 209}
210 210
211impl MacroDefId { 211impl MacroDefId {
212 pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId { 212 pub fn as_lazy_macro(
213 db.intern_macro(MacroCallLoc { def: self, kind }) 213 self,
214 db: &dyn db::AstDatabase,
215 krate: CrateId,
216 kind: MacroCallKind,
217 ) -> LazyMacroId {
218 db.intern_macro(MacroCallLoc { def: self, krate, kind })
214 } 219 }
215} 220}
216 221
@@ -227,6 +232,7 @@ pub enum MacroDefKind {
227#[derive(Debug, Clone, PartialEq, Eq, Hash)] 232#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct MacroCallLoc { 233pub struct MacroCallLoc {
229 pub(crate) def: MacroDefId, 234 pub(crate) def: MacroDefId,
235 pub(crate) krate: CrateId,
230 pub(crate) kind: MacroCallKind, 236 pub(crate) kind: MacroCallKind,
231} 237}
232 238
@@ -274,6 +280,7 @@ pub struct EagerCallLoc {
274 pub(crate) def: MacroDefId, 280 pub(crate) def: MacroDefId,
275 pub(crate) fragment: FragmentKind, 281 pub(crate) fragment: FragmentKind,
276 pub(crate) subtree: Arc<tt::Subtree>, 282 pub(crate) subtree: Arc<tt::Subtree>,
283 pub(crate) krate: CrateId,
277 pub(crate) file_id: HirFileId, 284 pub(crate) file_id: HirFileId,
278} 285}
279 286
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index fdf225f55..09fc18c36 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -6,6 +6,7 @@ use std::{
6}; 6};
7 7
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; 8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
9use rustc_hash::FxHashSet;
9 10
10#[salsa::database( 11#[salsa::database(
11 ra_db::SourceDatabaseExtStorage, 12 ra_db::SourceDatabaseExtStorage,
@@ -44,7 +45,7 @@ impl FileLoader for TestDB {
44 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 45 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
45 FileLoaderDelegate(self).resolve_path(anchor, path) 46 FileLoaderDelegate(self).resolve_path(anchor, path)
46 } 47 }
47 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 48 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
48 FileLoaderDelegate(self).relevant_crates(file_id) 49 FileLoaderDelegate(self).relevant_crates(file_id)
49 } 50 }
50} 51}
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index f04968e14..7db928dde 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -226,17 +226,19 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
226 None => return, 226 None => return,
227 }; 227 };
228 228
229 let std_result_path = path![std::result::Result]; 229 let core_result_path = path![core::result::Result];
230 230
231 let resolver = self.func.resolver(db.upcast()); 231 let resolver = self.func.resolver(db.upcast());
232 let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) { 232 let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
233 Some(it) => it, 233 Some(it) => it,
234 _ => return, 234 _ => return,
235 }; 235 };
236 236
237 let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum)); 237 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
238 let params = match &mismatch.expected { 238 let params = match &mismatch.expected {
239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters, 239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
240 parameters
241 }
240 _ => return, 242 _ => return,
241 }; 243 };
242 244
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index f965eb2b5..3719f76a6 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -555,13 +555,13 @@ impl<'a> InferenceContext<'a> {
555 } 555 }
556 556
557 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> { 557 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
558 let path = path![std::iter::IntoIterator]; 558 let path = path![core::iter::IntoIterator];
559 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 559 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
560 self.db.trait_data(trait_).associated_type_by_name(&name![Item]) 560 self.db.trait_data(trait_).associated_type_by_name(&name![Item])
561 } 561 }
562 562
563 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { 563 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
564 let path = path![std::ops::Try]; 564 let path = path![core::ops::Try];
565 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 565 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
566 self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) 566 self.db.trait_data(trait_).associated_type_by_name(&name![Ok])
567 } 567 }
@@ -587,37 +587,37 @@ impl<'a> InferenceContext<'a> {
587 } 587 }
588 588
589 fn resolve_range_full(&self) -> Option<AdtId> { 589 fn resolve_range_full(&self) -> Option<AdtId> {
590 let path = path![std::ops::RangeFull]; 590 let path = path![core::ops::RangeFull];
591 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 591 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
592 Some(struct_.into()) 592 Some(struct_.into())
593 } 593 }
594 594
595 fn resolve_range(&self) -> Option<AdtId> { 595 fn resolve_range(&self) -> Option<AdtId> {
596 let path = path![std::ops::Range]; 596 let path = path![core::ops::Range];
597 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 597 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
598 Some(struct_.into()) 598 Some(struct_.into())
599 } 599 }
600 600
601 fn resolve_range_inclusive(&self) -> Option<AdtId> { 601 fn resolve_range_inclusive(&self) -> Option<AdtId> {
602 let path = path![std::ops::RangeInclusive]; 602 let path = path![core::ops::RangeInclusive];
603 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 603 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
604 Some(struct_.into()) 604 Some(struct_.into())
605 } 605 }
606 606
607 fn resolve_range_from(&self) -> Option<AdtId> { 607 fn resolve_range_from(&self) -> Option<AdtId> {
608 let path = path![std::ops::RangeFrom]; 608 let path = path![core::ops::RangeFrom];
609 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 609 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
610 Some(struct_.into()) 610 Some(struct_.into())
611 } 611 }
612 612
613 fn resolve_range_to(&self) -> Option<AdtId> { 613 fn resolve_range_to(&self) -> Option<AdtId> {
614 let path = path![std::ops::RangeTo]; 614 let path = path![core::ops::RangeTo];
615 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 615 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
616 Some(struct_.into()) 616 Some(struct_.into())
617 } 617 }
618 618
619 fn resolve_range_to_inclusive(&self) -> Option<AdtId> { 619 fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
620 let path = path![std::ops::RangeToInclusive]; 620 let path = path![core::ops::RangeToInclusive];
621 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 621 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
622 Some(struct_.into()) 622 Some(struct_.into())
623 } 623 }
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index e484968a0..ad04e3e0f 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -8,6 +8,7 @@ use std::{
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; 10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11use rustc_hash::FxHashSet;
11use stdx::format_to; 12use stdx::format_to;
12 13
13use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; 14use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
@@ -73,7 +74,7 @@ impl FileLoader for TestDB {
73 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 74 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
74 FileLoaderDelegate(self).resolve_path(anchor, path) 75 FileLoaderDelegate(self).resolve_path(anchor, path)
75 } 76 }
76 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 77 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
77 FileLoaderDelegate(self).relevant_crates(file_id) 78 FileLoaderDelegate(self).relevant_crates(file_id)
78 } 79 }
79} 80}
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 8a5031756..37659cd02 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -95,7 +95,7 @@ fn foo() {
95fn infer_ranges() { 95fn infer_ranges() {
96 let (db, pos) = TestDB::with_position( 96 let (db, pos) = TestDB::with_position(
97 r#" 97 r#"
98//- /main.rs crate:main deps:std 98//- /main.rs crate:main deps:core
99fn test() { 99fn test() {
100 let a = ..; 100 let a = ..;
101 let b = 1..; 101 let b = 1..;
@@ -108,7 +108,7 @@ fn test() {
108 t<|>; 108 t<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112#[prelude_import] use prelude::*; 112#[prelude_import] use prelude::*;
113mod prelude {} 113mod prelude {}
114 114
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 133fb5f39..e81193a3c 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -10,7 +10,7 @@ use super::{infer, infer_with_mismatches, type_at, type_at_pos};
10fn infer_await() { 10fn infer_await() {
11 let (db, pos) = TestDB::with_position( 11 let (db, pos) = TestDB::with_position(
12 r#" 12 r#"
13//- /main.rs crate:main deps:std 13//- /main.rs crate:main deps:core
14 14
15struct IntFuture; 15struct IntFuture;
16 16
@@ -24,7 +24,7 @@ fn test() {
24 v<|>; 24 v<|>;
25} 25}
26 26
27//- /std.rs crate:std 27//- /core.rs crate:core
28#[prelude_import] use future::*; 28#[prelude_import] use future::*;
29mod future { 29mod future {
30 #[lang = "future_trait"] 30 #[lang = "future_trait"]
@@ -42,7 +42,7 @@ mod future {
42fn infer_async() { 42fn infer_async() {
43 let (db, pos) = TestDB::with_position( 43 let (db, pos) = TestDB::with_position(
44 r#" 44 r#"
45//- /main.rs crate:main deps:std 45//- /main.rs crate:main deps:core
46 46
47async fn foo() -> u64 { 47async fn foo() -> u64 {
48 128 48 128
@@ -54,7 +54,7 @@ fn test() {
54 v<|>; 54 v<|>;
55} 55}
56 56
57//- /std.rs crate:std 57//- /core.rs crate:core
58#[prelude_import] use future::*; 58#[prelude_import] use future::*;
59mod future { 59mod future {
60 #[lang = "future_trait"] 60 #[lang = "future_trait"]
@@ -72,7 +72,7 @@ mod future {
72fn infer_desugar_async() { 72fn infer_desugar_async() {
73 let (db, pos) = TestDB::with_position( 73 let (db, pos) = TestDB::with_position(
74 r#" 74 r#"
75//- /main.rs crate:main deps:std 75//- /main.rs crate:main deps:core
76 76
77async fn foo() -> u64 { 77async fn foo() -> u64 {
78 128 78 128
@@ -83,7 +83,7 @@ fn test() {
83 r<|>; 83 r<|>;
84} 84}
85 85
86//- /std.rs crate:std 86//- /core.rs crate:core
87#[prelude_import] use future::*; 87#[prelude_import] use future::*;
88mod future { 88mod future {
89 trait Future { 89 trait Future {
@@ -100,7 +100,7 @@ mod future {
100fn infer_try() { 100fn infer_try() {
101 let (db, pos) = TestDB::with_position( 101 let (db, pos) = TestDB::with_position(
102 r#" 102 r#"
103//- /main.rs crate:main deps:std 103//- /main.rs crate:main deps:core
104 104
105fn test() { 105fn test() {
106 let r: Result<i32, u64> = Result::Ok(1); 106 let r: Result<i32, u64> = Result::Ok(1);
@@ -108,7 +108,7 @@ fn test() {
108 v<|>; 108 v<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112 112
113#[prelude_import] use ops::*; 113#[prelude_import] use ops::*;
114mod ops { 114mod ops {
@@ -140,9 +140,9 @@ mod result {
140fn infer_for_loop() { 140fn infer_for_loop() {
141 let (db, pos) = TestDB::with_position( 141 let (db, pos) = TestDB::with_position(
142 r#" 142 r#"
143//- /main.rs crate:main deps:std 143//- /main.rs crate:main deps:core,alloc
144 144
145use std::collections::Vec; 145use alloc::collections::Vec;
146 146
147fn test() { 147fn test() {
148 let v = Vec::new(); 148 let v = Vec::new();
@@ -152,7 +152,7 @@ fn test() {
152 } 152 }
153} 153}
154 154
155//- /std.rs crate:std 155//- /core.rs crate:core
156 156
157#[prelude_import] use iter::*; 157#[prelude_import] use iter::*;
158mod iter { 158mod iter {
@@ -161,6 +161,8 @@ mod iter {
161 } 161 }
162} 162}
163 163
164//- /alloc.rs crate:alloc deps:core
165
164mod collections { 166mod collections {
165 struct Vec<T> {} 167 struct Vec<T> {}
166 impl<T> Vec<T> { 168 impl<T> Vec<T> {
@@ -168,7 +170,7 @@ mod collections {
168 fn push(&mut self, t: T) { } 170 fn push(&mut self, t: T) { }
169 } 171 }
170 172
171 impl<T> crate::iter::IntoIterator for Vec<T> { 173 impl<T> IntoIterator for Vec<T> {
172 type Item=T; 174 type Item=T;
173 } 175 }
174} 176}
@@ -2846,12 +2848,12 @@ fn test() {
2846fn integer_range_iterate() { 2848fn integer_range_iterate() {
2847 let t = type_at( 2849 let t = type_at(
2848 r#" 2850 r#"
2849//- /main.rs crate:main deps:std 2851//- /main.rs crate:main deps:core
2850fn test() { 2852fn test() {
2851 for x in 0..100 { x<|>; } 2853 for x in 0..100 { x<|>; }
2852} 2854}
2853 2855
2854//- /std.rs crate:std 2856//- /core.rs crate:core
2855pub mod ops { 2857pub mod ops {
2856 pub struct Range<Idx> { 2858 pub struct Range<Idx> {
2857 pub start: Idx, 2859 pub start: Idx,
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index bf14a467f..f44feaf69 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -321,29 +321,26 @@ mod tests {
321 fn test_wrap_return_type() { 321 fn test_wrap_return_type() {
322 let before = r#" 322 let before = r#"
323 //- /main.rs 323 //- /main.rs
324 use std::{string::String, result::Result::{self, Ok, Err}}; 324 use core::result::Result::{self, Ok, Err};
325 325
326 fn div(x: i32, y: i32) -> Result<i32, String> { 326 fn div(x: i32, y: i32) -> Result<i32, ()> {
327 if y == 0 { 327 if y == 0 {
328 return Err("div by zero".into()); 328 return Err(());
329 } 329 }
330 x / y<|> 330 x / y<|>
331 } 331 }
332 332
333 //- /std/lib.rs 333 //- /core/lib.rs
334 pub mod string {
335 pub struct String { }
336 }
337 pub mod result { 334 pub mod result {
338 pub enum Result<T, E> { Ok(T), Err(E) } 335 pub enum Result<T, E> { Ok(T), Err(E) }
339 } 336 }
340 "#; 337 "#;
341 let after = r#" 338 let after = r#"
342 use std::{string::String, result::Result::{self, Ok, Err}}; 339 use core::result::Result::{self, Ok, Err};
343 340
344 fn div(x: i32, y: i32) -> Result<i32, String> { 341 fn div(x: i32, y: i32) -> Result<i32, ()> {
345 if y == 0 { 342 if y == 0 {
346 return Err("div by zero".into()); 343 return Err(());
347 } 344 }
348 Ok(x / y) 345 Ok(x / y)
349 } 346 }
@@ -355,7 +352,7 @@ mod tests {
355 fn test_wrap_return_type_handles_generic_functions() { 352 fn test_wrap_return_type_handles_generic_functions() {
356 let before = r#" 353 let before = r#"
357 //- /main.rs 354 //- /main.rs
358 use std::result::Result::{self, Ok, Err}; 355 use core::result::Result::{self, Ok, Err};
359 356
360 fn div<T>(x: T) -> Result<T, i32> { 357 fn div<T>(x: T) -> Result<T, i32> {
361 if x == 0 { 358 if x == 0 {
@@ -364,13 +361,13 @@ mod tests {
364 <|>x 361 <|>x
365 } 362 }
366 363
367 //- /std/lib.rs 364 //- /core/lib.rs
368 pub mod result { 365 pub mod result {
369 pub enum Result<T, E> { Ok(T), Err(E) } 366 pub enum Result<T, E> { Ok(T), Err(E) }
370 } 367 }
371 "#; 368 "#;
372 let after = r#" 369 let after = r#"
373 use std::result::Result::{self, Ok, Err}; 370 use core::result::Result::{self, Ok, Err};
374 371
375 fn div<T>(x: T) -> Result<T, i32> { 372 fn div<T>(x: T) -> Result<T, i32> {
376 if x == 0 { 373 if x == 0 {
@@ -386,32 +383,29 @@ mod tests {
386 fn test_wrap_return_type_handles_type_aliases() { 383 fn test_wrap_return_type_handles_type_aliases() {
387 let before = r#" 384 let before = r#"
388 //- /main.rs 385 //- /main.rs
389 use std::{string::String, result::Result::{self, Ok, Err}}; 386 use core::result::Result::{self, Ok, Err};
390 387
391 type MyResult<T> = Result<T, String>; 388 type MyResult<T> = Result<T, ()>;
392 389
393 fn div(x: i32, y: i32) -> MyResult<i32> { 390 fn div(x: i32, y: i32) -> MyResult<i32> {
394 if y == 0 { 391 if y == 0 {
395 return Err("div by zero".into()); 392 return Err(());
396 } 393 }
397 x <|>/ y 394 x <|>/ y
398 } 395 }
399 396
400 //- /std/lib.rs 397 //- /core/lib.rs
401 pub mod string {
402 pub struct String { }
403 }
404 pub mod result { 398 pub mod result {
405 pub enum Result<T, E> { Ok(T), Err(E) } 399 pub enum Result<T, E> { Ok(T), Err(E) }
406 } 400 }
407 "#; 401 "#;
408 let after = r#" 402 let after = r#"
409 use std::{string::String, result::Result::{self, Ok, Err}}; 403 use core::result::Result::{self, Ok, Err};
410 404
411 type MyResult<T> = Result<T, String>; 405 type MyResult<T> = Result<T, ()>;
412 fn div(x: i32, y: i32) -> MyResult<i32> { 406 fn div(x: i32, y: i32) -> MyResult<i32> {
413 if y == 0 { 407 if y == 0 {
414 return Err("div by zero".into()); 408 return Err(());
415 } 409 }
416 Ok(x / y) 410 Ok(x / y)
417 } 411 }
@@ -423,16 +417,13 @@ mod tests {
423 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 417 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
424 let content = r#" 418 let content = r#"
425 //- /main.rs 419 //- /main.rs
426 use std::{string::String, result::Result::{self, Ok, Err}}; 420 use core::result::Result::{self, Ok, Err};
427 421
428 fn foo() -> Result<String, i32> { 422 fn foo() -> Result<(), i32> {
429 0<|> 423 0<|>
430 } 424 }
431 425
432 //- /std/lib.rs 426 //- /core/lib.rs
433 pub mod string {
434 pub struct String { }
435 }
436 pub mod result { 427 pub mod result {
437 pub enum Result<T, E> { Ok(T), Err(E) } 428 pub enum Result<T, E> { Ok(T), Err(E) }
438 } 429 }
@@ -444,7 +435,7 @@ mod tests {
444 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 435 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
445 let content = r#" 436 let content = r#"
446 //- /main.rs 437 //- /main.rs
447 use std::{string::String, result::Result::{self, Ok, Err}}; 438 use core::result::Result::{self, Ok, Err};
448 439
449 enum SomeOtherEnum { 440 enum SomeOtherEnum {
450 Ok(i32), 441 Ok(i32),
@@ -455,10 +446,7 @@ mod tests {
455 0<|> 446 0<|>
456 } 447 }
457 448
458 //- /std/lib.rs 449 //- /core/lib.rs
459 pub mod string {
460 pub struct String { }
461 }
462 pub mod result { 450 pub mod result {
463 pub enum Result<T, E> { Ok(T), Err(E) } 451 pub enum Result<T, E> { Ok(T), Err(E) }
464 } 452 }
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index e34ff5a7d..33548d43c 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -84,7 +84,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
84 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; 84 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
85 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>; 85 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
86 86
87 <span class="variable mutable">y</span>; 87 <span class="keyword">let</span> <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable declaration">z</span>, <span class="field">y</span> } = <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable">z</span>, <span class="field">y</span> };
88
89 <span class="variable">y</span>;
88} 90}
89 91
90<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; { 92<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; {
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 021f8e7e2..949bf59a0 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -7,18 +7,6 @@ use crate::{
7 FileRange, TextRange, 7 FileRange, TextRange,
8}; 8};
9 9
10/// Highlights the code given by the `ra_fixture` argument, renders the
11/// result as HTML, and compares it with the HTML file given as `snapshot`.
12/// Note that the `snapshot` file is overwritten by the rendered HTML.
13fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
14 let (analysis, file_id) = single_file(ra_fixture);
15 let dst_file = project_dir().join(snapshot);
16 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
17 let expected_html = &read_text(&dst_file);
18 fs::write(dst_file, &actual_html).unwrap();
19 assert_eq_text!(expected_html, actual_html);
20}
21
22#[test] 10#[test]
23fn test_highlighting() { 11fn test_highlighting() {
24 check_highlighting( 12 check_highlighting(
@@ -77,6 +65,8 @@ fn main() {
77 let y = &mut x; 65 let y = &mut x;
78 let z = &y; 66 let z = &y;
79 67
68 let Foo { x: z, y } = Foo { x: z, y };
69
80 y; 70 y;
81} 71}
82 72
@@ -334,3 +324,15 @@ impl Foo {
334 false, 324 false,
335 ) 325 )
336} 326}
327
328/// Highlights the code given by the `ra_fixture` argument, renders the
329/// result as HTML, and compares it with the HTML file given as `snapshot`.
330/// Note that the `snapshot` file is overwritten by the rendered HTML.
331fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
332 let (analysis, file_id) = single_file(ra_fixture);
333 let dst_file = project_dir().join(snapshot);
334 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
335 let expected_html = &read_text(&dst_file);
336 fs::write(dst_file, &actual_html).unwrap();
337 assert_eq_text!(expected_html, actual_html);
338}
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 533306e2e..83776d2b6 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -21,7 +21,9 @@ use ra_ide_db::{source_change::SourceFileEdit, RootDatabase};
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
24 AstNode, SourceFile, TextRange, TextSize, 24 AstNode, SourceFile,
25 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
26 TextRange, TextSize,
25}; 27};
26 28
27use ra_text_edit::TextEdit; 29use ra_text_edit::TextEdit;
@@ -98,9 +100,12 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
98 }; 100 };
99 let current_indent_len = TextSize::of(current_indent); 101 let current_indent_len = TextSize::of(current_indent);
100 102
103 let parent = whitespace.syntax().parent();
101 // Make sure dot is a part of call chain 104 // Make sure dot is a part of call chain
102 let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?; 105 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
103 let prev_indent = leading_indent(field_expr.syntax())?; 106 return None;
107 }
108 let prev_indent = leading_indent(&parent)?;
104 let target_indent = format!(" {}", prev_indent); 109 let target_indent = format!(" {}", prev_indent);
105 let target_indent_len = TextSize::of(&target_indent); 110 let target_indent_len = TextSize::of(&target_indent);
106 if current_indent_len == target_indent_len { 111 if current_indent_len == target_indent_len {
@@ -143,11 +148,11 @@ mod tests {
143 }) 148 })
144 } 149 }
145 150
146 fn type_char(char_typed: char, before: &str, after: &str) { 151 fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
147 let actual = do_type_char(char_typed, before) 152 let actual = do_type_char(char_typed, ra_fixture_before)
148 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 153 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
149 154
150 assert_eq_text!(after, &actual); 155 assert_eq_text!(ra_fixture_after, &actual);
151 } 156 }
152 157
153 fn type_char_noop(char_typed: char, before: &str) { 158 fn type_char_noop(char_typed: char, before: &str) {
@@ -249,6 +254,27 @@ fn foo() {
249 } 254 }
250 255
251 #[test] 256 #[test]
257 fn indents_new_chain_call_with_let() {
258 type_char(
259 '.',
260 r#"
261fn main() {
262 let _ = foo
263 <|>
264 bar()
265}
266"#,
267 r#"
268fn main() {
269 let _ = foo
270 .
271 bar()
272}
273"#,
274 );
275 }
276
277 #[test]
252 fn indents_continued_chain_call() { 278 fn indents_continued_chain_call() {
253 type_char( 279 type_char(
254 '.', 280 '.',
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index 5dbe1c1b7..2fc796a85 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -16,7 +16,7 @@ use rustc_hash::FxHashMap;
16 16
17use crate::{ 17use crate::{
18 symbol_index::{SymbolIndex, SymbolsDatabase}, 18 symbol_index::{SymbolIndex, SymbolsDatabase},
19 DebugData, RootDatabase, 19 RootDatabase,
20}; 20};
21 21
22#[derive(Default)] 22#[derive(Default)]
@@ -26,7 +26,6 @@ pub struct AnalysisChange {
26 files_changed: Vec<(FileId, Arc<String>)>, 26 files_changed: Vec<(FileId, Arc<String>)>,
27 libraries_added: Vec<LibraryData>, 27 libraries_added: Vec<LibraryData>,
28 crate_graph: Option<CrateGraph>, 28 crate_graph: Option<CrateGraph>,
29 debug_data: DebugData,
30} 29}
31 30
32impl fmt::Debug for AnalysisChange { 31impl fmt::Debug for AnalysisChange {
@@ -87,10 +86,6 @@ impl AnalysisChange {
87 pub fn set_crate_graph(&mut self, graph: CrateGraph) { 86 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
88 self.crate_graph = Some(graph); 87 self.crate_graph = Some(graph);
89 } 88 }
90
91 pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) {
92 self.debug_data.root_paths.insert(source_root_id, path);
93 }
94} 89}
95 90
96#[derive(Debug)] 91#[derive(Debug)]
@@ -218,8 +213,6 @@ impl RootDatabase {
218 if let Some(crate_graph) = change.crate_graph { 213 if let Some(crate_graph) = change.crate_graph {
219 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) 214 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
220 } 215 }
221
222 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
223 } 216 }
224 217
225 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { 218 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 1826f3ac6..3ef5e74b6 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -78,6 +78,7 @@ impl Definition {
78 } 78 }
79} 79}
80 80
81#[derive(Debug)]
81pub enum NameClass { 82pub enum NameClass {
82 Definition(Definition), 83 Definition(Definition),
83 /// `None` in `if let None = Some(82) {}` 84 /// `None` in `if let None = Some(82) {}`
@@ -131,9 +132,11 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
131 let local = sema.to_def(&it)?; 132 let local = sema.to_def(&it)?;
132 133
133 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) { 134 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) {
134 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { 135 if record_field_pat.name_ref().is_none() {
135 let field = Definition::Field(field); 136 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
136 return Some(NameClass::FieldShorthand { local, field }); 137 let field = Definition::Field(field);
138 return Some(NameClass::FieldShorthand { local, field });
139 }
137 } 140 }
138 } 141 }
139 142
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index bf0d8db60..fff112e66 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use hir::{MacroDef, ModuleDef, Semantics}; 4use hir::{Crate, MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -11,44 +11,46 @@ use crate::{
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either; 13use either::Either;
14use rustc_hash::FxHashSet;
14 15
15pub struct ImportsLocator<'a> { 16pub struct ImportsLocator<'a> {
16 sema: Semantics<'a, RootDatabase>, 17 sema: Semantics<'a, RootDatabase>,
18 krate: Crate,
17} 19}
18 20
19impl<'a> ImportsLocator<'a> { 21impl<'a> ImportsLocator<'a> {
20 pub fn new(db: &'a RootDatabase) -> Self { 22 pub fn new(db: &'a RootDatabase, krate: Crate) -> Self {
21 Self { sema: Semantics::new(db) } 23 Self { sema: Semantics::new(db), krate }
22 } 24 }
23 25
24 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { 26 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> {
25 let _p = profile("search_for_imports"); 27 let _p = profile("search_for_imports");
26 let db = self.sema.db; 28 let db = self.sema.db;
27 29
28 let project_results = { 30 // Query dependencies first.
29 let mut query = Query::new(name_to_import.to_string()); 31 let mut candidates: FxHashSet<_> =
30 query.exact(); 32 self.krate.query_external_importables(db, name_to_import).collect();
31 query.limit(40); 33
32 symbol_index::world_symbols(db, query) 34 // Query the local crate using the symbol index.
33 }; 35 let local_results = {
34 let lib_results = {
35 let mut query = Query::new(name_to_import.to_string()); 36 let mut query = Query::new(name_to_import.to_string());
36 query.libs();
37 query.exact(); 37 query.exact();
38 query.limit(40); 38 query.limit(40);
39 symbol_index::world_symbols(db, query) 39 symbol_index::crate_symbols(db, self.krate.into(), query)
40 }; 40 };
41 41
42 project_results 42 candidates.extend(
43 .into_iter() 43 local_results
44 .chain(lib_results.into_iter()) 44 .into_iter()
45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) 45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
46 .filter_map(|name_definition_to_import| match name_definition_to_import { 46 .filter_map(|name_definition_to_import| match name_definition_to_import {
47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
49 _ => None, 49 _ => None,
50 }) 50 }),
51 .collect() 51 );
52
53 candidates.into_iter().collect()
52 } 54 }
53 55
54 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { 56 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> {
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 727d743b5..a808de4f1 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -17,9 +17,9 @@ use hir::db::{AstDatabase, DefDatabase};
17use ra_db::{ 17use ra_db::{
18 salsa::{self, Database, Durability}, 18 salsa::{self, Database, Durability},
19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, 19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
20 SourceRootId, Upcast, 20 Upcast,
21}; 21};
22use rustc_hash::FxHashMap; 22use rustc_hash::FxHashSet;
23 23
24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; 24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
25 25
@@ -36,7 +36,6 @@ use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
36#[derive(Debug)] 36#[derive(Debug)]
37pub struct RootDatabase { 37pub struct RootDatabase {
38 runtime: salsa::Runtime<RootDatabase>, 38 runtime: salsa::Runtime<RootDatabase>,
39 pub(crate) debug_data: Arc<DebugData>,
40 pub last_gc: crate::wasm_shims::Instant, 39 pub last_gc: crate::wasm_shims::Instant,
41 pub last_gc_check: crate::wasm_shims::Instant, 40 pub last_gc_check: crate::wasm_shims::Instant,
42} 41}
@@ -60,7 +59,7 @@ impl FileLoader for RootDatabase {
60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 59 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
61 FileLoaderDelegate(self).resolve_path(anchor, path) 60 FileLoaderDelegate(self).resolve_path(anchor, path)
62 } 61 }
63 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 62 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
64 FileLoaderDelegate(self).relevant_crates(file_id) 63 FileLoaderDelegate(self).relevant_crates(file_id)
65 } 64 }
66} 65}
@@ -98,7 +97,6 @@ impl RootDatabase {
98 runtime: salsa::Runtime::default(), 97 runtime: salsa::Runtime::default(),
99 last_gc: crate::wasm_shims::Instant::now(), 98 last_gc: crate::wasm_shims::Instant::now(),
100 last_gc_check: crate::wasm_shims::Instant::now(), 99 last_gc_check: crate::wasm_shims::Instant::now(),
101 debug_data: Default::default(),
102 }; 100 };
103 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 101 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
104 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 102 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -121,7 +119,6 @@ impl salsa::ParallelDatabase for RootDatabase {
121 runtime: self.runtime.snapshot(self), 119 runtime: self.runtime.snapshot(self),
122 last_gc: self.last_gc, 120 last_gc: self.last_gc,
123 last_gc_check: self.last_gc_check, 121 last_gc_check: self.last_gc_check,
124 debug_data: Arc::clone(&self.debug_data),
125 }) 122 })
126 } 123 }
127} 124}
@@ -135,14 +132,3 @@ fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
135 let text = db.file_text(file_id); 132 let text = db.file_text(file_id);
136 Arc::new(LineIndex::new(&*text)) 133 Arc::new(LineIndex::new(&*text))
137} 134}
138
139#[derive(Debug, Default, Clone)]
140pub(crate) struct DebugData {
141 pub(crate) root_paths: FxHashMap<SourceRootId, String>,
142}
143
144impl DebugData {
145 pub(crate) fn merge(&mut self, other: DebugData) {
146 self.root_paths.extend(other.root_paths.into_iter());
147 }
148}
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index acc31fe3b..aab918973 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -29,9 +29,10 @@ use std::{
29}; 29};
30 30
31use fst::{self, Streamer}; 31use fst::{self, Streamer};
32use hir::db::DefDatabase;
32use ra_db::{ 33use ra_db::{
33 salsa::{self, ParallelDatabase}, 34 salsa::{self, ParallelDatabase},
34 FileId, SourceDatabaseExt, SourceRootId, 35 CrateId, FileId, SourceDatabaseExt, SourceRootId,
35}; 36};
36use ra_syntax::{ 37use ra_syntax::{
37 ast::{self, NameOwner}, 38 ast::{self, NameOwner},
@@ -110,6 +111,14 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
110 Arc::new(SymbolIndex::new(symbols)) 111 Arc::new(SymbolIndex::new(symbols))
111} 112}
112 113
114/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
115struct Snap(salsa::Snapshot<RootDatabase>);
116impl Clone for Snap {
117 fn clone(&self) -> Snap {
118 Snap(self.0.snapshot())
119 }
120}
121
113// Feature: Workspace Symbol 122// Feature: Workspace Symbol
114// 123//
115// Uses fuzzy-search to find types, modules and functions by name across your 124// Uses fuzzy-search to find types, modules and functions by name across your
@@ -132,13 +141,7 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
132// | VS Code | kbd:[Ctrl+T] 141// | VS Code | kbd:[Ctrl+T]
133// |=== 142// |===
134pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 143pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
135 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 144 let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());
136 struct Snap(salsa::Snapshot<RootDatabase>);
137 impl Clone for Snap {
138 fn clone(&self) -> Snap {
139 Snap(self.0.snapshot())
140 }
141 }
142 145
143 let buf: Vec<Arc<SymbolIndex>> = if query.libs { 146 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
144 let snap = Snap(db.snapshot()); 147 let snap = Snap(db.snapshot());
@@ -173,6 +176,33 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
173 query.search(&buf) 176 query.search(&buf)
174} 177}
175 178
179pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
180 // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from
181 // that instead?
182
183 let def_map = db.crate_def_map(krate);
184 let mut files = Vec::new();
185 let mut modules = vec![def_map.root];
186 while let Some(module) = modules.pop() {
187 let data = &def_map[module];
188 files.extend(data.origin.file_id());
189 modules.extend(data.children.values());
190 }
191
192 let snap = Snap(db.snapshot());
193
194 #[cfg(not(feature = "wasm"))]
195 let buf = files
196 .par_iter()
197 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
198 .collect::<Vec<_>>();
199
200 #[cfg(feature = "wasm")]
201 let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>();
202
203 query.search(&buf)
204}
205
176pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> { 206pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
177 let name = name_ref.text(); 207 let name = name_ref.text();
178 let mut query = Query::new(name.to_string()); 208 let mut query = Query::new(name.to_string());
@@ -298,9 +328,6 @@ impl Query {
298 let mut stream = op.union(); 328 let mut stream = op.union();
299 let mut res = Vec::new(); 329 let mut res = Vec::new();
300 while let Some((_, indexed_values)) = stream.next() { 330 while let Some((_, indexed_values)) = stream.next() {
301 if res.len() >= self.limit {
302 break;
303 }
304 for indexed_value in indexed_values { 331 for indexed_value in indexed_values {
305 let symbol_index = &indices[indexed_value.index]; 332 let symbol_index = &indices[indexed_value.index];
306 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); 333 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
@@ -312,7 +339,11 @@ impl Query {
312 if self.exact && symbol.name != self.query { 339 if self.exact && symbol.name != self.query {
313 continue; 340 continue;
314 } 341 }
342
315 res.push(symbol.clone()); 343 res.push(symbol.clone());
344 if res.len() >= self.limit {
345 return res;
346 }
316 } 347 }
317 } 348 }
318 } 349 }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index c1f7e3ac5..cb0e27dce 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -47,17 +47,21 @@ pub struct PackageRoot {
47 path: PathBuf, 47 path: PathBuf,
48 /// Is a member of the current workspace 48 /// Is a member of the current workspace
49 is_member: bool, 49 is_member: bool,
50 out_dir: Option<PathBuf>,
50} 51}
51impl PackageRoot { 52impl PackageRoot {
52 pub fn new_member(path: PathBuf) -> PackageRoot { 53 pub fn new_member(path: PathBuf) -> PackageRoot {
53 Self { path, is_member: true } 54 Self { path, is_member: true, out_dir: None }
54 } 55 }
55 pub fn new_non_member(path: PathBuf) -> PackageRoot { 56 pub fn new_non_member(path: PathBuf) -> PackageRoot {
56 Self { path, is_member: false } 57 Self { path, is_member: false, out_dir: None }
57 } 58 }
58 pub fn path(&self) -> &Path { 59 pub fn path(&self) -> &Path {
59 &self.path 60 &self.path
60 } 61 }
62 pub fn out_dir(&self) -> Option<&Path> {
63 self.out_dir.as_deref()
64 }
61 pub fn is_member(&self) -> bool { 65 pub fn is_member(&self) -> bool {
62 self.is_member 66 self.is_member
63 } 67 }
@@ -204,6 +208,7 @@ impl ProjectWorkspace {
204 .map(|pkg| PackageRoot { 208 .map(|pkg| PackageRoot {
205 path: cargo[pkg].root().to_path_buf(), 209 path: cargo[pkg].root().to_path_buf(),
206 is_member: cargo[pkg].is_member, 210 is_member: cargo[pkg].is_member,
211 out_dir: cargo[pkg].out_dir.clone(),
207 }) 212 })
208 .chain(sysroot.crates().map(|krate| { 213 .chain(sysroot.crates().map(|krate| {
209 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 214 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
@@ -212,17 +217,6 @@ impl ProjectWorkspace {
212 } 217 }
213 } 218 }
214 219
215 pub fn out_dirs(&self) -> Vec<PathBuf> {
216 match self {
217 ProjectWorkspace::Json { project } => {
218 project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect()
219 }
220 ProjectWorkspace::Cargo { cargo, sysroot: _ } => {
221 cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect()
222 }
223 }
224 }
225
226 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { 220 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
227 match self { 221 match self {
228 ProjectWorkspace::Json { project } => project 222 ProjectWorkspace::Json { project } => project
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 8f2aeac77..97367d7c6 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -36,28 +36,28 @@ pub fn load_cargo(
36 )?; 36 )?;
37 37
38 let mut extern_dirs = FxHashSet::default(); 38 let mut extern_dirs = FxHashSet::default();
39 extern_dirs.extend(ws.out_dirs());
40
41 let mut project_roots = ws.to_roots();
42 project_roots.extend(extern_dirs.iter().cloned().map(PackageRoot::new_non_member));
43 39
44 let (sender, receiver) = unbounded(); 40 let (sender, receiver) = unbounded();
45 let sender = Box::new(move |t| sender.send(t).unwrap()); 41 let sender = Box::new(move |t| sender.send(t).unwrap());
46 let (mut vfs, roots) = Vfs::new( 42
47 project_roots 43 let mut roots = Vec::new();
48 .iter() 44 let project_roots = ws.to_roots();
49 .map(|pkg_root| { 45 for root in &project_roots {
50 RootEntry::new( 46 roots.push(RootEntry::new(
51 pkg_root.path().to_owned(), 47 root.path().to_owned(),
52 RustPackageFilterBuilder::default() 48 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
53 .set_member(pkg_root.is_member()) 49 ));
54 .into_vfs_filter(), 50
55 ) 51 if let Some(out_dir) = root.out_dir() {
56 }) 52 extern_dirs.insert(out_dir.to_path_buf());
57 .collect(), 53 roots.push(RootEntry::new(
58 sender, 54 out_dir.to_owned(),
59 Watch(false), 55 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
60 ); 56 ))
57 }
58 }
59
60 let (mut vfs, roots) = Vfs::new(roots, sender, Watch(false));
61 61
62 let source_roots = roots 62 let source_roots = roots
63 .into_iter() 63 .into_iter()
@@ -111,10 +111,6 @@ pub(crate) fn load(
111 vfs.root2path(root) 111 vfs.root2path(root)
112 ); 112 );
113 analysis_change.add_root(source_root_id, is_local); 113 analysis_change.add_root(source_root_id, is_local);
114 analysis_change.set_debug_root_path(
115 source_root_id,
116 source_roots[&source_root_id].path().display().to_string(),
117 );
118 114
119 let vfs_root_path = vfs.root2path(root); 115 let vfs_root_path = vfs.root2path(root);
120 if extern_dirs.contains(&vfs_root_path) { 116 if extern_dirs.contains(&vfs_root_path) {
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 73b0f881d..21116e165 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -89,8 +89,7 @@ impl GlobalState {
89 ) -> GlobalState { 89 ) -> GlobalState {
90 let mut change = AnalysisChange::new(); 90 let mut change = AnalysisChange::new();
91 91
92 let extern_dirs: FxHashSet<_> = 92 let mut extern_dirs: FxHashSet<PathBuf> = FxHashSet::default();
93 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect();
94 93
95 let mut local_roots = Vec::new(); 94 let mut local_roots = Vec::new();
96 let roots: Vec<_> = { 95 let roots: Vec<_> = {
@@ -100,22 +99,22 @@ impl GlobalState {
100 .exclude(exclude_globs.iter().cloned()) 99 .exclude(exclude_globs.iter().cloned())
101 .into_vfs_filter() 100 .into_vfs_filter()
102 }; 101 };
103 workspaces 102 let mut roots = Vec::new();
104 .iter() 103 for root in workspaces.iter().flat_map(ProjectWorkspace::to_roots) {
105 .flat_map(ProjectWorkspace::to_roots) 104 let path = root.path().to_owned();
106 .map(|pkg_root| { 105 if root.is_member() {
107 let path = pkg_root.path().to_owned(); 106 local_roots.push(path.clone());
108 if pkg_root.is_member() { 107 }
109 local_roots.push(path.clone()); 108 roots.push(RootEntry::new(path, create_filter(root.is_member())));
110 } 109 if let Some(out_dir) = root.out_dir() {
111 RootEntry::new(path, create_filter(pkg_root.is_member())) 110 extern_dirs.insert(out_dir.to_path_buf());
112 }) 111 roots.push(RootEntry::new(
113 .chain( 112 out_dir.to_path_buf(),
114 extern_dirs 113 create_filter(root.is_member()),
115 .iter() 114 ))
116 .map(|path| RootEntry::new(path.to_owned(), create_filter(false))), 115 }
117 ) 116 }
118 .collect() 117 roots
119 }; 118 };
120 119
121 let (task_sender, task_receiver) = unbounded(); 120 let (task_sender, task_receiver) = unbounded();
@@ -127,7 +126,6 @@ impl GlobalState {
127 let vfs_root_path = vfs.root2path(r); 126 let vfs_root_path = vfs.root2path(r);
128 let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it)); 127 let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it));
129 change.add_root(SourceRootId(r.0), is_local); 128 change.add_root(SourceRootId(r.0), is_local);
130 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
131 129
132 // FIXME: add path2root in vfs to simpily this logic 130 // FIXME: add path2root in vfs to simpily this logic
133 if extern_dirs.contains(&vfs_root_path) { 131 if extern_dirs.contains(&vfs_root_path) {
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 0330939b6..ef5ffbf59 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -303,6 +303,17 @@ Rather than talking in definitions and references, it talks in Strings and textu
303In general, API is centered around UI concerns -- the result of the call is what the user sees in the editor, and not what the compiler sees underneath. 303In general, API is centered around UI concerns -- the result of the call is what the user sees in the editor, and not what the compiler sees underneath.
304The results are 100% Rust specific though. 304The results are 100% Rust specific though.
305 305
306## Parser Tests
307
308Test for parser (`ra_parser`) live in `ra_syntax` crate (see `test_data` direcotory).
309There are two kinds of tests:
310
311* Manually written test cases in `parser/ok` and `parser/err`
312* "Inline" tests in `parser/inline` (these are generated) from comments in `ra_parser` crate.
313
314The purpose of inline tests is not to achieve full coverage by test cases, but to explain to the reader of the code what each particular `if` and `match` is responsible for.
315If you are tempted to add a large inline test, it might be a good idea to leave only the simplest example in place, and move the test to a manual `parser/ok` test.
316
306# Logging 317# Logging
307 318
308Logging is done by both rust-analyzer and VS Code, so it might be tricky to 319Logging is done by both rust-analyzer and VS Code, so it might be tricky to