aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/doc_tests.rs7
-rw-r--r--crates/ra_assists/src/lib.rs40
-rw-r--r--crates/ra_assists/src/test_db.rs45
-rw-r--r--crates/ra_batch/Cargo.toml2
-rw-r--r--crates/ra_cli/Cargo.toml2
-rw-r--r--crates/ra_hir/src/code_model.rs5
-rw-r--r--crates/ra_hir/src/code_model/attrs.rs92
-rw-r--r--crates/ra_hir/src/db.rs8
-rw-r--r--crates/ra_hir/src/expr/scope.rs8
-rw-r--r--crates/ra_hir/src/lib.rs10
-rw-r--r--crates/ra_hir/src/mock.rs262
-rw-r--r--crates/ra_hir/src/test_db.rs120
-rw-r--r--crates/ra_hir/src/ty.rs2
-rw-r--r--crates/ra_hir/src/ty/infer/expr.rs6
-rw-r--r--crates/ra_hir/src/ty/infer/path.rs4
-rw-r--r--crates/ra_hir/src/ty/lower.rs15
-rw-r--r--crates/ra_hir/src/ty/tests.rs32
-rw-r--r--crates/ra_hir/src/ty/traits/chalk.rs7
-rw-r--r--crates/ra_hir_def/src/nameres.rs270
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs10
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs12
-rw-r--r--crates/ra_hir_def/src/nameres/path_resolution.rs261
-rw-r--r--crates/ra_hir_def/src/nameres/per_ns.rs8
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs48
-rw-r--r--crates/ra_ide_api/src/completion/complete_postfix.rs109
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs27
-rw-r--r--crates/ra_lsp_server/Cargo.toml4
-rw-r--r--crates/ra_parser/src/grammar/types.rs6
28 files changed, 730 insertions, 692 deletions
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs
index 6e1e3de84..a8f8446cb 100644
--- a/crates/ra_assists/src/doc_tests.rs
+++ b/crates/ra_assists/src/doc_tests.rs
@@ -5,13 +5,14 @@
5 5
6mod generated; 6mod generated;
7 7
8use hir::mock::MockDatabase; 8use ra_db::{fixture::WithFixture, FileRange};
9use ra_db::FileRange;
10use test_utils::{assert_eq_text, extract_range_or_offset}; 9use test_utils::{assert_eq_text, extract_range_or_offset};
11 10
11use crate::test_db::TestDB;
12
12fn check(assist_id: &str, before: &str, after: &str) { 13fn check(assist_id: &str, before: &str, after: &str) {
13 let (selection, before) = extract_range_or_offset(before); 14 let (selection, before) = extract_range_or_offset(before);
14 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 15 let (db, file_id) = TestDB::with_single_file(&before);
15 let frange = FileRange { file_id, range: selection.into() }; 16 let frange = FileRange { file_id, range: selection.into() };
16 17
17 let (_assist_id, action) = crate::assists(&db, frange) 18 let (_assist_id, action) = crate::assists(&db, frange)
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 38599d4f1..39c1c283f 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -9,6 +9,8 @@ mod assist_ctx;
9mod marks; 9mod marks;
10#[cfg(test)] 10#[cfg(test)]
11mod doc_tests; 11mod doc_tests;
12#[cfg(test)]
13mod test_db;
12 14
13use hir::db::HirDatabase; 15use hir::db::HirDatabase;
14use ra_db::FileRange; 16use ra_db::FileRange;
@@ -146,20 +148,19 @@ mod assists {
146 148
147#[cfg(test)] 149#[cfg(test)]
148mod helpers { 150mod helpers {
149 use hir::mock::MockDatabase; 151 use ra_db::{fixture::WithFixture, FileRange};
150 use ra_db::FileRange;
151 use ra_syntax::TextRange; 152 use ra_syntax::TextRange;
152 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 153 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
153 154
154 use crate::{Assist, AssistCtx}; 155 use crate::{test_db::TestDB, Assist, AssistCtx};
155 156
156 pub(crate) fn check_assist( 157 pub(crate) fn check_assist(
157 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 158 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
158 before: &str, 159 before: &str,
159 after: &str, 160 after: &str,
160 ) { 161 ) {
161 let (before_cursor_pos, before) = extract_offset(before); 162 let (before_cursor_pos, before) = extract_offset(before);
162 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 163 let (db, file_id) = TestDB::with_single_file(&before);
163 let frange = 164 let frange =
164 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 165 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
165 let assist = 166 let assist =
@@ -182,12 +183,12 @@ mod helpers {
182 } 183 }
183 184
184 pub(crate) fn check_assist_range( 185 pub(crate) fn check_assist_range(
185 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 186 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
186 before: &str, 187 before: &str,
187 after: &str, 188 after: &str,
188 ) { 189 ) {
189 let (range, before) = extract_range(before); 190 let (range, before) = extract_range(before);
190 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 191 let (db, file_id) = TestDB::with_single_file(&before);
191 let frange = FileRange { file_id, range }; 192 let frange = FileRange { file_id, range };
192 let assist = 193 let assist =
193 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 194 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -204,12 +205,12 @@ mod helpers {
204 } 205 }
205 206
206 pub(crate) fn check_assist_target( 207 pub(crate) fn check_assist_target(
207 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 208 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
208 before: &str, 209 before: &str,
209 target: &str, 210 target: &str,
210 ) { 211 ) {
211 let (before_cursor_pos, before) = extract_offset(before); 212 let (before_cursor_pos, before) = extract_offset(before);
212 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 213 let (db, file_id) = TestDB::with_single_file(&before);
213 let frange = 214 let frange =
214 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 215 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
215 let assist = 216 let assist =
@@ -224,12 +225,12 @@ mod helpers {
224 } 225 }
225 226
226 pub(crate) fn check_assist_range_target( 227 pub(crate) fn check_assist_range_target(
227 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 228 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
228 before: &str, 229 before: &str,
229 target: &str, 230 target: &str,
230 ) { 231 ) {
231 let (range, before) = extract_range(before); 232 let (range, before) = extract_range(before);
232 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 233 let (db, file_id) = TestDB::with_single_file(&before);
233 let frange = FileRange { file_id, range }; 234 let frange = FileRange { file_id, range };
234 let assist = 235 let assist =
235 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 236 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -243,11 +244,11 @@ mod helpers {
243 } 244 }
244 245
245 pub(crate) fn check_assist_not_applicable( 246 pub(crate) fn check_assist_not_applicable(
246 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 247 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
247 before: &str, 248 before: &str,
248 ) { 249 ) {
249 let (before_cursor_pos, before) = extract_offset(before); 250 let (before_cursor_pos, before) = extract_offset(before);
250 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 251 let (db, file_id) = TestDB::with_single_file(&before);
251 let frange = 252 let frange =
252 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 253 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
253 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 254 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
@@ -255,11 +256,11 @@ mod helpers {
255 } 256 }
256 257
257 pub(crate) fn check_assist_range_not_applicable( 258 pub(crate) fn check_assist_range_not_applicable(
258 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 259 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
259 before: &str, 260 before: &str,
260 ) { 261 ) {
261 let (range, before) = extract_range(before); 262 let (range, before) = extract_range(before);
262 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 263 let (db, file_id) = TestDB::with_single_file(&before);
263 let frange = FileRange { file_id, range }; 264 let frange = FileRange { file_id, range };
264 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 265 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
265 assert!(assist.is_none()); 266 assert!(assist.is_none());
@@ -268,16 +269,17 @@ mod helpers {
268 269
269#[cfg(test)] 270#[cfg(test)]
270mod tests { 271mod tests {
271 use hir::mock::MockDatabase; 272 use ra_db::{fixture::WithFixture, FileRange};
272 use ra_db::FileRange;
273 use ra_syntax::TextRange; 273 use ra_syntax::TextRange;
274 use test_utils::{extract_offset, extract_range}; 274 use test_utils::{extract_offset, extract_range};
275 275
276 use crate::test_db::TestDB;
277
276 #[test] 278 #[test]
277 fn assist_order_field_struct() { 279 fn assist_order_field_struct() {
278 let before = "struct Foo { <|>bar: u32 }"; 280 let before = "struct Foo { <|>bar: u32 }";
279 let (before_cursor_pos, before) = extract_offset(before); 281 let (before_cursor_pos, before) = extract_offset(before);
280 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 282 let (db, file_id) = TestDB::with_single_file(&before);
281 let frange = 283 let frange =
282 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 284 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
283 let assists = super::assists(&db, frange); 285 let assists = super::assists(&db, frange);
@@ -298,7 +300,7 @@ mod tests {
298 } 300 }
299 }"; 301 }";
300 let (range, before) = extract_range(before); 302 let (range, before) = extract_range(before);
301 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 303 let (db, file_id) = TestDB::with_single_file(&before);
302 let frange = FileRange { file_id, range }; 304 let frange = FileRange { file_id, range };
303 let assists = super::assists(&db, frange); 305 let assists = super::assists(&db, frange);
304 let mut assists = assists.iter(); 306 let mut assists = assists.iter();
diff --git a/crates/ra_assists/src/test_db.rs b/crates/ra_assists/src/test_db.rs
new file mode 100644
index 000000000..5be7383ed
--- /dev/null
+++ b/crates/ra_assists/src/test_db.rs
@@ -0,0 +1,45 @@
1//! Database used for testing `ra_assists`.
2
3use std::sync::Arc;
4
5use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
6
7#[salsa::database(
8 ra_db::SourceDatabaseExtStorage,
9 ra_db::SourceDatabaseStorage,
10 hir::db::InternDatabaseStorage,
11 hir::db::AstDatabaseStorage,
12 hir::db::DefDatabaseStorage,
13 hir::db::DefDatabase2Storage,
14 hir::db::HirDatabaseStorage
15)]
16#[derive(Debug, Default)]
17pub struct TestDB {
18 runtime: salsa::Runtime<TestDB>,
19}
20
21impl salsa::Database for TestDB {
22 fn salsa_runtime(&self) -> &salsa::Runtime<Self> {
23 &self.runtime
24 }
25}
26
27impl std::panic::RefUnwindSafe for TestDB {}
28
29impl FileLoader for TestDB {
30 fn file_text(&self, file_id: FileId) -> Arc<String> {
31 FileLoaderDelegate(self).file_text(file_id)
32 }
33 fn resolve_relative_path(
34 &self,
35 anchor: FileId,
36 relative_path: &RelativePath,
37 ) -> Option<FileId> {
38 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
39 }
40 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
41 FileLoaderDelegate(self).relevant_crates(file_id)
42 }
43}
44
45impl hir::debug::HirDebugHelper for TestDB {}
diff --git a/crates/ra_batch/Cargo.toml b/crates/ra_batch/Cargo.toml
index c85da7a2c..d08c5ceac 100644
--- a/crates/ra_batch/Cargo.toml
+++ b/crates/ra_batch/Cargo.toml
@@ -7,7 +7,7 @@ authors = ["rust-analyzer developers"]
7[dependencies] 7[dependencies]
8log = "0.4.5" 8log = "0.4.5"
9rustc-hash = "1.0" 9rustc-hash = "1.0"
10crossbeam-channel = "0.3.5" 10crossbeam-channel = "0.4.0"
11 11
12ra_vfs = "0.5.0" 12ra_vfs = "0.5.0"
13ra_vfs_glob = { path = "../ra_vfs_glob" } 13ra_vfs_glob = { path = "../ra_vfs_glob" }
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml
index fcd102e8b..c9d3bdb77 100644
--- a/crates/ra_cli/Cargo.toml
+++ b/crates/ra_cli/Cargo.toml
@@ -8,7 +8,7 @@ publish = false
8[dependencies] 8[dependencies]
9pico-args = "0.3.0" 9pico-args = "0.3.0"
10flexi_logger = "0.14.0" 10flexi_logger = "0.14.0"
11indicatif = "0.12.0" 11indicatif = "0.13.0"
12 12
13ra_syntax = { path = "../ra_syntax" } 13ra_syntax = { path = "../ra_syntax" }
14ra_ide_api = { path = "../ra_ide_api" } 14ra_ide_api = { path = "../ra_ide_api" }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 181c5d47a..9d0db8024 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -2,6 +2,7 @@
2 2
3pub(crate) mod src; 3pub(crate) mod src;
4pub(crate) mod docs; 4pub(crate) mod docs;
5pub(crate) mod attrs;
5 6
6use std::sync::Arc; 7use std::sync::Arc;
7 8
@@ -30,8 +31,8 @@ use crate::{
30 impl_block::ImplBlock, 31 impl_block::ImplBlock,
31 resolve::{Resolver, Scope, TypeNs}, 32 resolve::{Resolver, Scope, TypeNs},
32 traits::TraitData, 33 traits::TraitData,
33 ty::{InferenceResult, TraitRef}, 34 ty::{InferenceResult, Namespace, TraitRef},
34 Either, HasSource, Name, ScopeDef, Ty, {ImportId, Namespace}, 35 Either, HasSource, ImportId, Name, ScopeDef, Ty,
35}; 36};
36 37
37/// hir::Crate describes a single crate. It's the main interface with which 38/// hir::Crate describes a single crate. It's the main interface with which
diff --git a/crates/ra_hir/src/code_model/attrs.rs b/crates/ra_hir/src/code_model/attrs.rs
new file mode 100644
index 000000000..f7db36b66
--- /dev/null
+++ b/crates/ra_hir/src/code_model/attrs.rs
@@ -0,0 +1,92 @@
1//! FIXME: write short doc here
2
3use crate::{
4 db::{AstDatabase, DefDatabase, HirDatabase},
5 Adt, Const, Enum, EnumVariant, FieldSource, Function, HasSource, MacroDef, Module, Static,
6 Struct, StructField, Trait, TypeAlias, Union,
7};
8use hir_def::attr::Attr;
9use hir_expand::hygiene::Hygiene;
10use ra_syntax::ast;
11use std::sync::Arc;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum AttrDef {
15 Module(Module),
16 StructField(StructField),
17 Adt(Adt),
18 Function(Function),
19 EnumVariant(EnumVariant),
20 Static(Static),
21 Const(Const),
22 Trait(Trait),
23 TypeAlias(TypeAlias),
24 MacroDef(MacroDef),
25}
26
27impl_froms!(
28 AttrDef: Module,
29 StructField,
30 Adt(Struct, Enum, Union),
31 EnumVariant,
32 Static,
33 Const,
34 Function,
35 Trait,
36 TypeAlias,
37 MacroDef
38);
39
40pub trait Attrs {
41 fn attrs(&self, db: &impl HirDatabase) -> Option<Arc<[Attr]>>;
42}
43
44pub(crate) fn attributes_query(
45 db: &(impl DefDatabase + AstDatabase),
46 def: AttrDef,
47) -> Option<Arc<[Attr]>> {
48 match def {
49 AttrDef::Module(it) => {
50 let src = it.declaration_source(db)?;
51 let hygiene = Hygiene::new(db, src.file_id);
52 Attr::from_attrs_owner(&src.ast, &hygiene)
53 }
54 AttrDef::StructField(it) => match it.source(db).ast {
55 FieldSource::Named(named) => {
56 let src = it.source(db);
57 let hygiene = Hygiene::new(db, src.file_id);
58 Attr::from_attrs_owner(&named, &hygiene)
59 }
60 FieldSource::Pos(..) => None,
61 },
62 AttrDef::Adt(it) => match it {
63 Adt::Struct(it) => attrs_from_ast(it, db),
64 Adt::Enum(it) => attrs_from_ast(it, db),
65 Adt::Union(it) => attrs_from_ast(it, db),
66 },
67 AttrDef::EnumVariant(it) => attrs_from_ast(it, db),
68 AttrDef::Static(it) => attrs_from_ast(it, db),
69 AttrDef::Const(it) => attrs_from_ast(it, db),
70 AttrDef::Function(it) => attrs_from_ast(it, db),
71 AttrDef::Trait(it) => attrs_from_ast(it, db),
72 AttrDef::TypeAlias(it) => attrs_from_ast(it, db),
73 AttrDef::MacroDef(it) => attrs_from_ast(it, db),
74 }
75}
76
77fn attrs_from_ast<T, D>(node: T, db: &D) -> Option<Arc<[Attr]>>
78where
79 T: HasSource,
80 T::Ast: ast::AttrsOwner,
81 D: DefDatabase + AstDatabase,
82{
83 let src = node.source(db);
84 let hygiene = Hygiene::new(db, src.file_id);
85 Attr::from_attrs_owner(&src.ast, &hygiene)
86}
87
88impl<T: Into<AttrDef> + Copy> Attrs for T {
89 fn attrs(&self, db: &impl HirDatabase) -> Option<Arc<[Attr]>> {
90 db.attrs((*self).into())
91 }
92}
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index eb66325f7..75c322c99 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -2,6 +2,7 @@
2 2
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::attr::Attr;
5use ra_db::salsa; 6use ra_db::salsa;
6use ra_syntax::SmolStr; 7use ra_syntax::SmolStr;
7 8
@@ -14,10 +15,10 @@ use crate::{
14 traits::TraitData, 15 traits::TraitData,
15 ty::{ 16 ty::{
16 method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate, 17 method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate,
17 InferenceResult, Substs, Ty, TypableDef, TypeCtor, 18 InferenceResult, Namespace, Substs, Ty, TypableDef, TypeCtor,
18 }, 19 },
19 type_alias::TypeAliasData, 20 type_alias::TypeAliasData,
20 Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Namespace, Static, 21 Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Static,
21 StructField, Trait, TypeAlias, 22 StructField, Trait, TypeAlias,
22}; 23};
23 24
@@ -75,6 +76,9 @@ pub trait DefDatabase: HirDebugDatabase + DefDatabase2 {
75 76
76 #[salsa::invoke(crate::code_model::docs::documentation_query)] 77 #[salsa::invoke(crate::code_model::docs::documentation_query)]
77 fn documentation(&self, def: crate::DocDef) -> Option<crate::Documentation>; 78 fn documentation(&self, def: crate::DocDef) -> Option<crate::Documentation>;
79
80 #[salsa::invoke(crate::code_model::attrs::attributes_query)]
81 fn attrs(&self, def: crate::AttrDef) -> Option<Arc<[Attr]>>;
78} 82}
79 83
80#[salsa::query_group(HirDatabaseStorage)] 84#[salsa::query_group(HirDatabaseStorage)]
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index 5496822e7..c14c2ab66 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -174,11 +174,11 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
174 174
175#[cfg(test)] 175#[cfg(test)]
176mod tests { 176mod tests {
177 use ra_db::SourceDatabase; 177 use ra_db::{fixture::WithFixture, SourceDatabase};
178 use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 178 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
179 use test_utils::{assert_eq_text, extract_offset}; 179 use test_utils::{assert_eq_text, extract_offset};
180 180
181 use crate::{mock::MockDatabase, source_binder::SourceAnalyzer}; 181 use crate::{source_binder::SourceAnalyzer, test_db::TestDB};
182 182
183 fn do_check(code: &str, expected: &[&str]) { 183 fn do_check(code: &str, expected: &[&str]) {
184 let (off, code) = extract_offset(code); 184 let (off, code) = extract_offset(code);
@@ -191,7 +191,7 @@ mod tests {
191 buf 191 buf
192 }; 192 };
193 193
194 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code); 194 let (db, file_id) = TestDB::with_single_file(&code);
195 let file = db.parse(file_id).ok().unwrap(); 195 let file = db.parse(file_id).ok().unwrap();
196 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 196 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
197 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None); 197 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
@@ -288,7 +288,7 @@ mod tests {
288 fn do_check_local_name(code: &str, expected_offset: u32) { 288 fn do_check_local_name(code: &str, expected_offset: u32) {
289 let (off, code) = extract_offset(code); 289 let (off, code) = extract_offset(code);
290 290
291 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code); 291 let (db, file_id) = TestDB::with_single_file(&code);
292 let file = db.parse(file_id).ok().unwrap(); 292 let file = db.parse(file_id).ok().unwrap();
293 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) 293 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
294 .expect("failed to find a name at the target offset"); 294 .expect("failed to find a name at the target offset");
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 3ba99d92d..131f6c797 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -29,8 +29,6 @@ macro_rules! impl_froms {
29pub mod debug; 29pub mod debug;
30 30
31pub mod db; 31pub mod db;
32#[macro_use]
33pub mod mock;
34pub mod source_binder; 32pub mod source_binder;
35 33
36mod ids; 34mod ids;
@@ -52,6 +50,8 @@ mod code_model;
52pub mod from_source; 50pub mod from_source;
53 51
54#[cfg(test)] 52#[cfg(test)]
53mod test_db;
54#[cfg(test)]
55mod marks; 55mod marks;
56 56
57use hir_expand::AstId; 57use hir_expand::AstId;
@@ -61,6 +61,7 @@ use crate::{ids::MacroFileKind, resolve::Resolver};
61pub use crate::{ 61pub use crate::{
62 adt::VariantDef, 62 adt::VariantDef,
63 code_model::{ 63 code_model::{
64 attrs::{AttrDef, Attrs},
64 docs::{DocDef, Docs, Documentation}, 65 docs::{DocDef, Docs, Documentation},
65 src::{HasBodySource, HasSource}, 66 src::{HasBodySource, HasSource},
66 Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum, 67 Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
@@ -81,10 +82,7 @@ pub use crate::{
81 82
82pub use hir_def::{ 83pub use hir_def::{
83 builtin_type::BuiltinType, 84 builtin_type::BuiltinType,
84 nameres::{ 85 nameres::{per_ns::PerNs, raw::ImportId},
85 per_ns::{Namespace, PerNs},
86 raw::ImportId,
87 },
88 path::{Path, PathKind}, 86 path::{Path, PathKind},
89 type_ref::Mutability, 87 type_ref::Mutability,
90}; 88};
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
deleted file mode 100644
index ab97a09b9..000000000
--- a/crates/ra_hir/src/mock.rs
+++ /dev/null
@@ -1,262 +0,0 @@
1//! FIXME: write short doc here
2
3use std::{panic, sync::Arc};
4
5use hir_expand::diagnostics::DiagnosticSink;
6use parking_lot::Mutex;
7use ra_cfg::CfgOptions;
8use ra_db::{
9 salsa, CrateGraph, CrateId, Edition, FileId, FileLoader, FileLoaderDelegate, FilePosition,
10 RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
11};
12use rustc_hash::FxHashMap;
13use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
14
15use crate::{db, debug::HirDebugHelper};
16
17pub const WORKSPACE: SourceRootId = SourceRootId(0);
18
19#[salsa::database(
20 ra_db::SourceDatabaseExtStorage,
21 ra_db::SourceDatabaseStorage,
22 db::InternDatabaseStorage,
23 db::AstDatabaseStorage,
24 db::DefDatabaseStorage,
25 db::DefDatabase2Storage,
26 db::HirDatabaseStorage
27)]
28#[derive(Debug)]
29pub struct MockDatabase {
30 events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>,
31 runtime: salsa::Runtime<MockDatabase>,
32 files: FxHashMap<String, FileId>,
33 crate_names: Arc<FxHashMap<CrateId, String>>,
34 file_paths: Arc<FxHashMap<FileId, String>>,
35}
36
37impl panic::RefUnwindSafe for MockDatabase {}
38
39impl FileLoader for MockDatabase {
40 fn file_text(&self, file_id: FileId) -> Arc<String> {
41 FileLoaderDelegate(self).file_text(file_id)
42 }
43 fn resolve_relative_path(
44 &self,
45 anchor: FileId,
46 relative_path: &RelativePath,
47 ) -> Option<FileId> {
48 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
49 }
50 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
51 FileLoaderDelegate(self).relevant_crates(file_id)
52 }
53}
54
55impl HirDebugHelper for MockDatabase {
56 fn crate_name(&self, krate: CrateId) -> Option<String> {
57 self.crate_names.get(&krate).cloned()
58 }
59
60 fn file_path(&self, file_id: FileId) -> Option<String> {
61 self.file_paths.get(&file_id).cloned()
62 }
63}
64
65impl MockDatabase {
66 pub fn with_files(fixture: &str) -> MockDatabase {
67 let (db, position) = MockDatabase::from_fixture(fixture);
68 assert!(position.is_none());
69 db
70 }
71
72 pub fn with_single_file(text: &str) -> (MockDatabase, SourceRoot, FileId) {
73 let mut db = MockDatabase::default();
74 let mut source_root = SourceRoot::default();
75 let file_id = db.add_file(WORKSPACE, "/", &mut source_root, "/main.rs", text);
76 db.set_source_root(WORKSPACE, Arc::new(source_root.clone()));
77 (db, source_root, file_id)
78 }
79
80 pub fn file_id_of(&self, path: &str) -> FileId {
81 match self.files.get(path) {
82 Some(it) => *it,
83 None => panic!("unknown file: {:?}\nexisting files:\n{:#?}", path, self.files),
84 }
85 }
86
87 pub fn diagnostics(&self) -> String {
88 let mut buf = String::new();
89 let mut files: Vec<FileId> = self.files.values().copied().collect();
90 files.sort();
91 for file in files {
92 let src = crate::Source {
93 file_id: file.into(),
94 ast: crate::ModuleSource::new(self, Some(file), None),
95 };
96 let module = crate::Module::from_definition(self, src).unwrap();
97 module.diagnostics(
98 self,
99 &mut DiagnosticSink::new(|d| {
100 buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message());
101 }),
102 )
103 }
104 buf
105 }
106
107 fn from_fixture(fixture: &str) -> (MockDatabase, Option<FilePosition>) {
108 let mut db = MockDatabase::default();
109
110 let pos = db.add_fixture(fixture);
111
112 (db, pos)
113 }
114
115 fn add_fixture(&mut self, fixture: &str) -> Option<FilePosition> {
116 let mut position = None;
117 let mut source_root = SourceRoot::default();
118 let mut source_root_id = WORKSPACE;
119 let mut source_root_prefix = "/".to_string();
120 for entry in parse_fixture(fixture) {
121 if entry.meta.starts_with("root") {
122 self.set_source_root(source_root_id, Arc::new(source_root));
123 source_root = SourceRoot::default();
124
125 source_root_id = SourceRootId(source_root_id.0 + 1);
126 source_root_prefix = entry.meta["root".len()..].trim().to_string();
127 continue;
128 }
129 if entry.text.contains(CURSOR_MARKER) {
130 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
131 position = Some(self.add_file_with_position(
132 source_root_id,
133 &source_root_prefix,
134 &mut source_root,
135 &entry.meta,
136 &entry.text,
137 ));
138 } else {
139 self.add_file(
140 source_root_id,
141 &source_root_prefix,
142 &mut source_root,
143 &entry.meta,
144 &entry.text,
145 );
146 }
147 }
148 self.set_source_root(source_root_id, Arc::new(source_root));
149 position
150 }
151
152 fn add_file(
153 &mut self,
154 source_root_id: SourceRootId,
155 source_root_prefix: &str,
156 source_root: &mut SourceRoot,
157 path: &str,
158 text: &str,
159 ) -> FileId {
160 assert!(source_root_prefix.starts_with('/'));
161 assert!(source_root_prefix.ends_with('/'));
162 assert!(path.starts_with(source_root_prefix));
163 let rel_path = RelativePathBuf::from_path(&path[source_root_prefix.len()..]).unwrap();
164
165 let is_crate_root = rel_path == "lib.rs" || rel_path == "/main.rs";
166
167 let file_id = FileId(self.files.len() as u32);
168
169 let prev = self.files.insert(path.to_string(), file_id);
170 assert!(prev.is_none(), "duplicate files in the text fixture");
171 Arc::make_mut(&mut self.file_paths).insert(file_id, path.to_string());
172
173 let text = Arc::new(text.to_string());
174 self.set_file_text(file_id, text);
175 self.set_file_relative_path(file_id, rel_path.clone());
176 self.set_file_source_root(file_id, source_root_id);
177 source_root.insert_file(rel_path, file_id);
178
179 if is_crate_root {
180 let mut crate_graph = CrateGraph::default();
181 crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default());
182 self.set_crate_graph(Arc::new(crate_graph));
183 }
184 file_id
185 }
186
187 fn add_file_with_position(
188 &mut self,
189 source_root_id: SourceRootId,
190 source_root_prefix: &str,
191 source_root: &mut SourceRoot,
192 path: &str,
193 text: &str,
194 ) -> FilePosition {
195 let (offset, text) = extract_offset(text);
196 let file_id = self.add_file(source_root_id, source_root_prefix, source_root, path, &text);
197 FilePosition { file_id, offset }
198 }
199}
200
201impl salsa::Database for MockDatabase {
202 fn salsa_runtime(&self) -> &salsa::Runtime<MockDatabase> {
203 &self.runtime
204 }
205
206 fn salsa_event(&self, event: impl Fn() -> salsa::Event<MockDatabase>) {
207 let mut events = self.events.lock();
208 if let Some(events) = &mut *events {
209 events.push(event());
210 }
211 }
212}
213
214impl Default for MockDatabase {
215 fn default() -> MockDatabase {
216 let mut db = MockDatabase {
217 events: Default::default(),
218 runtime: salsa::Runtime::default(),
219 files: FxHashMap::default(),
220 crate_names: Default::default(),
221 file_paths: Default::default(),
222 };
223 db.set_crate_graph(Default::default());
224 db
225 }
226}
227
228impl salsa::ParallelDatabase for MockDatabase {
229 fn snapshot(&self) -> salsa::Snapshot<MockDatabase> {
230 salsa::Snapshot::new(MockDatabase {
231 events: Default::default(),
232 runtime: self.runtime.snapshot(self),
233 // only the root database can be used to get file_id by path.
234 files: FxHashMap::default(),
235 file_paths: Arc::clone(&self.file_paths),
236 crate_names: Arc::clone(&self.crate_names),
237 })
238 }
239}
240
241impl MockDatabase {
242 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> {
243 *self.events.lock() = Some(Vec::new());
244 f();
245 self.events.lock().take().unwrap()
246 }
247
248 pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
249 let events = self.log(f);
250 events
251 .into_iter()
252 .filter_map(|e| match e.kind {
253 // This pretty horrible, but `Debug` is the only way to inspect
254 // QueryDescriptor at the moment.
255 salsa::EventKind::WillExecute { database_key } => {
256 Some(format!("{:?}", database_key))
257 }
258 _ => None,
259 })
260 .collect()
261 }
262}
diff --git a/crates/ra_hir/src/test_db.rs b/crates/ra_hir/src/test_db.rs
new file mode 100644
index 000000000..5237b303a
--- /dev/null
+++ b/crates/ra_hir/src/test_db.rs
@@ -0,0 +1,120 @@
1//! Database used for testing `hir`.
2
3use std::{panic, sync::Arc};
4
5use hir_def::{db::DefDatabase2, ModuleId};
6use hir_expand::diagnostics::DiagnosticSink;
7use parking_lot::Mutex;
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase};
9
10use crate::{db, debug::HirDebugHelper};
11
12#[salsa::database(
13 ra_db::SourceDatabaseExtStorage,
14 ra_db::SourceDatabaseStorage,
15 db::InternDatabaseStorage,
16 db::AstDatabaseStorage,
17 db::DefDatabaseStorage,
18 db::DefDatabase2Storage,
19 db::HirDatabaseStorage
20)]
21#[derive(Debug, Default)]
22pub struct TestDB {
23 events: Mutex<Option<Vec<salsa::Event<TestDB>>>>,
24 runtime: salsa::Runtime<TestDB>,
25}
26
27impl salsa::Database for TestDB {
28 fn salsa_runtime(&self) -> &salsa::Runtime<TestDB> {
29 &self.runtime
30 }
31
32 fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
33 let mut events = self.events.lock();
34 if let Some(events) = &mut *events {
35 events.push(event());
36 }
37 }
38}
39
40impl salsa::ParallelDatabase for TestDB {
41 fn snapshot(&self) -> salsa::Snapshot<TestDB> {
42 salsa::Snapshot::new(TestDB {
43 events: Default::default(),
44 runtime: self.runtime.snapshot(self),
45 })
46 }
47}
48
49impl panic::RefUnwindSafe for TestDB {}
50
51impl FileLoader for TestDB {
52 fn file_text(&self, file_id: FileId) -> Arc<String> {
53 FileLoaderDelegate(self).file_text(file_id)
54 }
55 fn resolve_relative_path(
56 &self,
57 anchor: FileId,
58 relative_path: &RelativePath,
59 ) -> Option<FileId> {
60 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
61 }
62 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
63 FileLoaderDelegate(self).relevant_crates(file_id)
64 }
65}
66
67// FIXME: improve `WithFixture` to bring useful hir debugging back
68impl HirDebugHelper for TestDB {
69 fn crate_name(&self, _krate: CrateId) -> Option<String> {
70 None
71 }
72
73 fn file_path(&self, _file_id: FileId) -> Option<String> {
74 None
75 }
76}
77
78impl TestDB {
79 pub fn diagnostics(&self) -> String {
80 let mut buf = String::new();
81 let crate_graph = self.crate_graph();
82 for krate in crate_graph.iter().next() {
83 let crate_def_map = self.crate_def_map(krate);
84 for (module_id, _) in crate_def_map.modules.iter() {
85 let module_id = ModuleId { krate, module_id };
86 let module = crate::Module::from(module_id);
87 module.diagnostics(
88 self,
89 &mut DiagnosticSink::new(|d| {
90 buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message());
91 }),
92 )
93 }
94 }
95 buf
96 }
97}
98
99impl TestDB {
100 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<TestDB>> {
101 *self.events.lock() = Some(Vec::new());
102 f();
103 self.events.lock().take().unwrap()
104 }
105
106 pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
107 let events = self.log(f);
108 events
109 .into_iter()
110 .filter_map(|e| match e.kind {
111 // This pretty horrible, but `Debug` is the only way to inspect
112 // QueryDescriptor at the moment.
113 salsa::EventKind::WillExecute { database_key } => {
114 Some(format!("{:?}", database_key))
115 }
116 _ => None,
117 })
118 .collect()
119 }
120}
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index d1a9d7411..cd2ac0e8b 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -27,7 +27,7 @@ pub(crate) use infer::{infer_query, InferTy, InferenceResult};
27pub use lower::CallableDef; 27pub use lower::CallableDef;
28pub(crate) use lower::{ 28pub(crate) use lower::{
29 callable_item_sig, generic_defaults_query, generic_predicates_for_param_query, 29 callable_item_sig, generic_defaults_query, generic_predicates_for_param_query,
30 generic_predicates_query, type_for_def, type_for_field, TypableDef, 30 generic_predicates_query, type_for_def, type_for_field, Namespace, TypableDef,
31}; 31};
32pub(crate) use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; 32pub(crate) use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
33 33
diff --git a/crates/ra_hir/src/ty/infer/expr.rs b/crates/ra_hir/src/ty/infer/expr.rs
index a09ef5c5d..4af1d65ee 100644
--- a/crates/ra_hir/src/ty/infer/expr.rs
+++ b/crates/ra_hir/src/ty/infer/expr.rs
@@ -12,10 +12,10 @@ use crate::{
12 expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, 12 expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
13 generics::{GenericParams, HasGenericParams}, 13 generics::{GenericParams, HasGenericParams},
14 ty::{ 14 ty::{
15 autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Obligation, 15 autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Namespace,
16 ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk, 16 Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
17 }, 17 },
18 Adt, Name, Namespace, 18 Adt, Name,
19}; 19};
20 20
21impl<'a, D: HirDatabase> InferenceContext<'a, D> { 21impl<'a, D: HirDatabase> InferenceContext<'a, D> {
diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs
index 59b7f7eb6..865ced5a1 100644
--- a/crates/ra_hir/src/ty/infer/path.rs
+++ b/crates/ra_hir/src/ty/infer/path.rs
@@ -6,8 +6,8 @@ use super::{ExprOrPatId, InferenceContext, TraitRef};
6use crate::{ 6use crate::{
7 db::HirDatabase, 7 db::HirDatabase,
8 resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, 8 resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
9 ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk}, 9 ty::{method_resolution, Namespace, Substs, Ty, TypableDef, TypeWalk},
10 AssocItem, Container, HasGenericParams, Name, Namespace, Path, 10 AssocItem, Container, HasGenericParams, Name, Path,
11}; 11};
12 12
13impl<'a, D: HirDatabase> InferenceContext<'a, D> { 13impl<'a, D: HirDatabase> InferenceContext<'a, D> {
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index e29ab8492..d26b16cb2 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -29,10 +29,21 @@ use crate::{
29 Adt, 29 Adt,
30 }, 30 },
31 util::make_mut_slice, 31 util::make_mut_slice,
32 Const, Enum, EnumVariant, Function, ModuleDef, Namespace, Path, Static, Struct, StructField, 32 Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, Trait,
33 Trait, TypeAlias, Union, 33 TypeAlias, Union,
34}; 34};
35 35
36// FIXME: this is only really used in `type_for_def`, which contains a bunch of
37// impossible cases. Perhaps we should recombine `TypeableDef` and `Namespace`
38// into a `AsTypeDef`, `AsValueDef` enums?
39#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
40pub enum Namespace {
41 Types,
42 Values,
43 // Note that only type inference uses this enum, and it doesn't care about macros.
44 // Macro,
45}
46
36impl Ty { 47impl Ty {
37 pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &TypeRef) -> Self { 48 pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &TypeRef) -> Self {
38 match type_ref { 49 match type_ref {
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 4b7e34878..e56b9356e 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11use test_utils::covers; 11use test_utils::covers;
12 12
13use crate::{ 13use crate::{
14 expr::BodySourceMap, mock::MockDatabase, ty::display::HirDisplay, ty::InferenceResult, 14 expr::BodySourceMap, test_db::TestDB, ty::display::HirDisplay, ty::InferenceResult,
15 SourceAnalyzer, 15 SourceAnalyzer,
16}; 16};
17 17
@@ -24,7 +24,7 @@ mod coercion;
24 24
25#[test] 25#[test]
26fn cfg_impl_block() { 26fn cfg_impl_block() {
27 let (db, pos) = MockDatabase::with_position( 27 let (db, pos) = TestDB::with_position(
28 r#" 28 r#"
29//- /main.rs crate:main deps:foo cfg:test 29//- /main.rs crate:main deps:foo cfg:test
30use foo::S as T; 30use foo::S as T;
@@ -64,7 +64,7 @@ impl S {
64 64
65#[test] 65#[test]
66fn infer_await() { 66fn infer_await() {
67 let (db, pos) = MockDatabase::with_position( 67 let (db, pos) = TestDB::with_position(
68 r#" 68 r#"
69//- /main.rs crate:main deps:std 69//- /main.rs crate:main deps:std
70 70
@@ -95,7 +95,7 @@ mod future {
95 95
96#[test] 96#[test]
97fn infer_box() { 97fn infer_box() {
98 let (db, pos) = MockDatabase::with_position( 98 let (db, pos) = TestDB::with_position(
99 r#" 99 r#"
100//- /main.rs crate:main deps:std 100//- /main.rs crate:main deps:std
101 101
@@ -122,7 +122,7 @@ mod boxed {
122 122
123#[test] 123#[test]
124fn infer_adt_self() { 124fn infer_adt_self() {
125 let (db, pos) = MockDatabase::with_position( 125 let (db, pos) = TestDB::with_position(
126 r#" 126 r#"
127//- /main.rs 127//- /main.rs
128enum Nat { Succ(Self), Demo(Nat), Zero } 128enum Nat { Succ(Self), Demo(Nat), Zero }
@@ -141,7 +141,7 @@ fn test() {
141 141
142#[test] 142#[test]
143fn infer_try() { 143fn infer_try() {
144 let (db, pos) = MockDatabase::with_position( 144 let (db, pos) = TestDB::with_position(
145 r#" 145 r#"
146//- /main.rs crate:main deps:std 146//- /main.rs crate:main deps:std
147 147
@@ -181,7 +181,7 @@ mod result {
181 181
182#[test] 182#[test]
183fn infer_for_loop() { 183fn infer_for_loop() {
184 let (db, pos) = MockDatabase::with_position( 184 let (db, pos) = TestDB::with_position(
185 r#" 185 r#"
186//- /main.rs crate:main deps:std 186//- /main.rs crate:main deps:std
187 187
@@ -223,7 +223,7 @@ mod collections {
223#[test] 223#[test]
224fn infer_while_let() { 224fn infer_while_let() {
225 covers!(infer_while_let); 225 covers!(infer_while_let);
226 let (db, pos) = MockDatabase::with_position( 226 let (db, pos) = TestDB::with_position(
227 r#" 227 r#"
228//- /main.rs 228//- /main.rs
229enum Option<T> { Some(T), None } 229enum Option<T> { Some(T), None }
@@ -2484,7 +2484,7 @@ pub fn main_loop() {
2484 2484
2485#[test] 2485#[test]
2486fn cross_crate_associated_method_call() { 2486fn cross_crate_associated_method_call() {
2487 let (db, pos) = MockDatabase::with_position( 2487 let (db, pos) = TestDB::with_position(
2488 r#" 2488 r#"
2489//- /main.rs crate:main deps:other_crate 2489//- /main.rs crate:main deps:other_crate
2490fn test() { 2490fn test() {
@@ -3378,7 +3378,7 @@ fn test() { S.foo()<|>; }
3378 3378
3379#[test] 3379#[test]
3380fn infer_macro_with_dollar_crate_is_correct_in_expr() { 3380fn infer_macro_with_dollar_crate_is_correct_in_expr() {
3381 let (db, pos) = MockDatabase::with_position( 3381 let (db, pos) = TestDB::with_position(
3382 r#" 3382 r#"
3383//- /main.rs crate:main deps:foo 3383//- /main.rs crate:main deps:foo
3384fn test() { 3384fn test() {
@@ -3482,7 +3482,7 @@ fn test() { (&S).foo()<|>; }
3482 3482
3483#[test] 3483#[test]
3484fn method_resolution_trait_from_prelude() { 3484fn method_resolution_trait_from_prelude() {
3485 let (db, pos) = MockDatabase::with_position( 3485 let (db, pos) = TestDB::with_position(
3486 r#" 3486 r#"
3487//- /main.rs crate:main deps:other_crate 3487//- /main.rs crate:main deps:other_crate
3488struct S; 3488struct S;
@@ -4651,7 +4651,7 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
4651 assert_eq!(t, "{unknown}"); 4651 assert_eq!(t, "{unknown}");
4652} 4652}
4653 4653
4654fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { 4654fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
4655 let file = db.parse(pos.file_id).ok().unwrap(); 4655 let file = db.parse(pos.file_id).ok().unwrap();
4656 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 4656 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
4657 let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset)); 4657 let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset));
@@ -4660,12 +4660,12 @@ fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
4660} 4660}
4661 4661
4662fn type_at(content: &str) -> String { 4662fn type_at(content: &str) -> String {
4663 let (db, file_pos) = MockDatabase::with_position(content); 4663 let (db, file_pos) = TestDB::with_position(content);
4664 type_at_pos(&db, file_pos) 4664 type_at_pos(&db, file_pos)
4665} 4665}
4666 4666
4667fn infer(content: &str) -> String { 4667fn infer(content: &str) -> String {
4668 let (db, _, file_id) = MockDatabase::with_single_file(content); 4668 let (db, file_id) = TestDB::with_single_file(content);
4669 let source_file = db.parse(file_id).ok().unwrap(); 4669 let source_file = db.parse(file_id).ok().unwrap();
4670 4670
4671 let mut acc = String::new(); 4671 let mut acc = String::new();
@@ -4748,7 +4748,7 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
4748 4748
4749#[test] 4749#[test]
4750fn typing_whitespace_inside_a_function_should_not_invalidate_types() { 4750fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
4751 let (mut db, pos) = MockDatabase::with_position( 4751 let (mut db, pos) = TestDB::with_position(
4752 " 4752 "
4753 //- /lib.rs 4753 //- /lib.rs
4754 fn foo() -> i32 { 4754 fn foo() -> i32 {
@@ -4788,7 +4788,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
4788 4788
4789#[test] 4789#[test]
4790fn no_such_field_diagnostics() { 4790fn no_such_field_diagnostics() {
4791 let diagnostics = MockDatabase::with_files( 4791 let diagnostics = TestDB::with_files(
4792 r" 4792 r"
4793 //- /lib.rs 4793 //- /lib.rs
4794 struct S { foo: i32, bar: () } 4794 struct S { foo: i32, bar: () }
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs
index 39ef92182..c694952f3 100644
--- a/crates/ra_hir/src/ty/traits/chalk.rs
+++ b/crates/ra_hir/src/ty/traits/chalk.rs
@@ -18,7 +18,10 @@ use crate::{
18 db::HirDatabase, 18 db::HirDatabase,
19 generics::GenericDef, 19 generics::GenericDef,
20 ty::display::HirDisplay, 20 ty::display::HirDisplay,
21 ty::{ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk}, 21 ty::{
22 ApplicationTy, GenericPredicate, Namespace, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
23 TypeWalk,
24 },
22 AssocItem, Crate, HasGenericParams, ImplBlock, Trait, TypeAlias, 25 AssocItem, Crate, HasGenericParams, ImplBlock, Trait, TypeAlias,
23}; 26};
24 27
@@ -652,7 +655,7 @@ fn impl_block_datum(
652 }) 655 })
653 .filter_map(|t| { 656 .filter_map(|t| {
654 let assoc_ty = trait_.associated_type_by_name(db, &t.name(db))?; 657 let assoc_ty = trait_.associated_type_by_name(db, &t.name(db))?;
655 let ty = db.type_for_def(t.into(), crate::Namespace::Types).subst(&bound_vars); 658 let ty = db.type_for_def(t.into(), Namespace::Types).subst(&bound_vars);
656 Some(chalk_rust_ir::AssociatedTyValue { 659 Some(chalk_rust_ir::AssociatedTyValue {
657 impl_id, 660 impl_id,
658 associated_ty_id: assoc_ty.to_chalk(db), 661 associated_ty_id: assoc_ty.to_chalk(db),
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 433bdde48..d3ecabb9b 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -14,10 +14,10 @@
14//! 14//!
15//! ## Collecting RawItems 15//! ## Collecting RawItems
16//! 16//!
17//! This happens in the `raw` module, which parses a single source file into a 17//! This happens in the `raw` module, which parses a single source file into a
18//! set of top-level items. Nested imports are desugared to flat imports in 18//! set of top-level items. Nested imports are desugared to flat imports in this
19//! this phase. Macro calls are represented as a triple of (Path, Option<Name>, 19//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
20//! TokenTree). 20//! TokenTree).
21//! 21//!
22//! ## Collecting Modules 22//! ## Collecting Modules
23//! 23//!
@@ -44,14 +44,14 @@
44//! Macros from other crates (including proc-macros) can be used with 44//! Macros from other crates (including proc-macros) can be used with
45//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of 45//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
46//! unexpanded macros. On every iteration, we try to resolve each macro call 46//! unexpanded macros. On every iteration, we try to resolve each macro call
47//! path and, upon success, we run macro expansion and "collect module" phase 47//! path and, upon success, we run macro expansion and "collect module" phase on
48//! on the result 48//! the result
49 49
50// FIXME: review privacy of submodules
51pub mod raw; 50pub mod raw;
52pub mod per_ns; 51pub mod per_ns;
53pub mod collector; 52mod collector;
54pub mod mod_resolution; 53mod mod_resolution;
54mod path_resolution;
55 55
56#[cfg(test)] 56#[cfg(test)]
57mod tests; 57mod tests;
@@ -65,14 +65,15 @@ use ra_db::{CrateId, Edition, FileId};
65use ra_prof::profile; 65use ra_prof::profile;
66use ra_syntax::ast; 66use ra_syntax::ast;
67use rustc_hash::{FxHashMap, FxHashSet}; 67use rustc_hash::{FxHashMap, FxHashSet};
68use test_utils::tested_by;
69 68
70use crate::{ 69use crate::{
71 builtin_type::BuiltinType, 70 builtin_type::BuiltinType,
72 db::DefDatabase2, 71 db::DefDatabase2,
73 nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId}, 72 nameres::{
74 path::{Path, PathKind}, 73 diagnostics::DefDiagnostic, path_resolution::ResolveMode, per_ns::PerNs, raw::ImportId,
75 AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId, 74 },
75 path::Path,
76 AstId, CrateModuleId, ModuleDefId, ModuleId, TraitId,
76}; 77};
77 78
78/// Contains all top-level defs from a macro-expanded crate 79/// Contains all top-level defs from a macro-expanded crate
@@ -195,45 +196,6 @@ pub struct Resolution {
195 pub import: Option<ImportId>, 196 pub import: Option<ImportId>,
196} 197}
197 198
198impl Resolution {
199 pub(crate) fn from_macro(macro_: MacroDefId) -> Self {
200 Resolution { def: PerNs::macros(macro_), import: None }
201 }
202}
203
204#[derive(Debug, Clone)]
205struct ResolvePathResult {
206 resolved_def: PerNs,
207 segment_index: Option<usize>,
208 reached_fixedpoint: ReachedFixedPoint,
209}
210
211impl ResolvePathResult {
212 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
213 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
214 }
215
216 fn with(
217 resolved_def: PerNs,
218 reached_fixedpoint: ReachedFixedPoint,
219 segment_index: Option<usize>,
220 ) -> ResolvePathResult {
221 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
226enum ResolveMode {
227 Import,
228 Other,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232enum ReachedFixedPoint {
233 Yes,
234 No,
235}
236
237impl CrateDefMap { 199impl CrateDefMap {
238 pub(crate) fn crate_def_map_query( 200 pub(crate) fn crate_def_map_query(
239 // Note that this doesn't have `+ AstDatabase`! 201 // Note that this doesn't have `+ AstDatabase`!
@@ -296,210 +258,6 @@ impl CrateDefMap {
296 let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); 258 let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
297 (res.resolved_def, res.segment_index) 259 (res.resolved_def, res.segment_index)
298 } 260 }
299
300 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
301 // the result.
302 fn resolve_path_fp_with_macro(
303 &self,
304 db: &impl DefDatabase2,
305 mode: ResolveMode,
306 original_module: CrateModuleId,
307 path: &Path,
308 ) -> ResolvePathResult {
309 let mut segments = path.segments.iter().enumerate();
310 let mut curr_per_ns: PerNs = match path.kind {
311 PathKind::DollarCrate(krate) => {
312 if krate == self.krate {
313 tested_by!(macro_dollar_crate_self);
314 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
315 } else {
316 let def_map = db.crate_def_map(krate);
317 let module = ModuleId { krate, module_id: def_map.root };
318 tested_by!(macro_dollar_crate_other);
319 PerNs::types(module.into())
320 }
321 }
322 PathKind::Crate => {
323 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
324 }
325 PathKind::Self_ => {
326 PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
327 }
328 // plain import or absolute path in 2015: crate-relative with
329 // fallback to extern prelude (with the simplification in
330 // rust-lang/rust#57745)
331 // FIXME there must be a nicer way to write this condition
332 PathKind::Plain | PathKind::Abs
333 if self.edition == Edition::Edition2015
334 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
335 {
336 let segment = match segments.next() {
337 Some((_, segment)) => segment,
338 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
339 };
340 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
341 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
342 }
343 PathKind::Plain => {
344 let segment = match segments.next() {
345 Some((_, segment)) => segment,
346 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
347 };
348 log::debug!("resolving {:?} in module", segment);
349 self.resolve_name_in_module(db, original_module, &segment.name)
350 }
351 PathKind::Super => {
352 if let Some(p) = self.modules[original_module].parent {
353 PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
354 } else {
355 log::debug!("super path in root module");
356 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
357 }
358 }
359 PathKind::Abs => {
360 // 2018-style absolute path -- only extern prelude
361 let segment = match segments.next() {
362 Some((_, segment)) => segment,
363 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
364 };
365 if let Some(def) = self.extern_prelude.get(&segment.name) {
366 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
367 PerNs::types(*def)
368 } else {
369 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
370 }
371 }
372 PathKind::Type(_) => {
373 // This is handled in `infer::infer_path_expr`
374 // The result returned here does not matter
375 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
376 }
377 };
378
379 for (i, segment) in segments {
380 let curr = match curr_per_ns.take_types() {
381 Some(r) => r,
382 None => {
383 // we still have path segments left, but the path so far
384 // didn't resolve in the types namespace => no resolution
385 // (don't break here because `curr_per_ns` might contain
386 // something in the value namespace, and it would be wrong
387 // to return that)
388 return ResolvePathResult::empty(ReachedFixedPoint::No);
389 }
390 };
391 // resolve segment in curr
392
393 curr_per_ns = match curr {
394 ModuleDefId::ModuleId(module) => {
395 if module.krate != self.krate {
396 let path =
397 Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
398 log::debug!("resolving {:?} in other crate", path);
399 let defp_map = db.crate_def_map(module.krate);
400 let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
401 return ResolvePathResult::with(
402 def,
403 ReachedFixedPoint::Yes,
404 s.map(|s| s + i),
405 );
406 }
407
408 // Since it is a qualified path here, it should not contains legacy macros
409 match self[module.module_id].scope.get(&segment.name) {
410 Some(res) => res.def,
411 _ => {
412 log::debug!("path segment {:?} not found", segment.name);
413 return ResolvePathResult::empty(ReachedFixedPoint::No);
414 }
415 }
416 }
417 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
418 // enum variant
419 tested_by!(can_import_enum_variant);
420 let enum_data = db.enum_data(e);
421 match enum_data.variant(&segment.name) {
422 Some(local_id) => {
423 let variant = EnumVariantId { parent: e, local_id };
424 PerNs::both(variant.into(), variant.into())
425 }
426 None => {
427 return ResolvePathResult::with(
428 PerNs::types(e.into()),
429 ReachedFixedPoint::Yes,
430 Some(i),
431 );
432 }
433 }
434 }
435 s => {
436 // could be an inherent method call in UFCS form
437 // (`Struct::method`), or some other kind of associated item
438 log::debug!(
439 "path segment {:?} resolved to non-module {:?}, but is not last",
440 segment.name,
441 curr,
442 );
443
444 return ResolvePathResult::with(
445 PerNs::types(s),
446 ReachedFixedPoint::Yes,
447 Some(i),
448 );
449 }
450 };
451 }
452 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
453 }
454
455 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
456 let from_crate_root =
457 self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
458 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
459
460 from_crate_root.or(from_extern_prelude)
461 }
462
463 pub(crate) fn resolve_name_in_module(
464 &self,
465 db: &impl DefDatabase2,
466 module: CrateModuleId,
467 name: &Name,
468 ) -> PerNs {
469 // Resolve in:
470 // - legacy scope of macro
471 // - current module / scope
472 // - extern prelude
473 // - std prelude
474 let from_legacy_macro =
475 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
476 let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
477 let from_extern_prelude =
478 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
479 let from_prelude = self.resolve_in_prelude(db, name);
480
481 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
482 }
483
484 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
485 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
486 }
487
488 fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
489 if let Some(prelude) = self.prelude {
490 let keep;
491 let def_map = if prelude.krate == self.krate {
492 self
493 } else {
494 // Extend lifetime
495 keep = db.crate_def_map(prelude.krate);
496 &keep
497 };
498 def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
499 } else {
500 PerNs::none()
501 }
502 }
503} 261}
504 262
505mod diagnostics { 263mod diagnostics {
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 3b61d9895..aacd50df8 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -14,8 +14,8 @@ use crate::{
14 attr::Attr, 14 attr::Attr,
15 db::DefDatabase2, 15 db::DefDatabase2,
16 nameres::{ 16 nameres::{
17 diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap, 17 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
18 ModuleData, ReachedFixedPoint, Resolution, ResolveMode, 18 per_ns::PerNs, raw, CrateDefMap, ModuleData, Resolution, ResolveMode,
19 }, 19 },
20 path::{Path, PathKind}, 20 path::{Path, PathKind},
21 AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId, 21 AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId,
@@ -182,7 +182,11 @@ where
182 // In Rust, `#[macro_export]` macros are unconditionally visible at the 182 // In Rust, `#[macro_export]` macros are unconditionally visible at the
183 // crate root, even if the parent modules is **not** visible. 183 // crate root, even if the parent modules is **not** visible.
184 if export { 184 if export {
185 self.update(self.def_map.root, None, &[(name, Resolution::from_macro(macro_))]); 185 self.update(
186 self.def_map.root,
187 None,
188 &[(name, Resolution { def: PerNs::macros(macro_), import: None })],
189 );
186 } 190 }
187 } 191 }
188 192
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index f6b0b8fb1..b3b1379d0 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -6,7 +6,7 @@ use ra_syntax::SmolStr;
6use crate::{db::DefDatabase2, HirFileId}; 6use crate::{db::DefDatabase2, HirFileId};
7 7
8#[derive(Clone, Debug)] 8#[derive(Clone, Debug)]
9pub struct ModDir { 9pub(super) struct ModDir {
10 /// `.` for `mod.rs`, `lib.rs` 10 /// `.` for `mod.rs`, `lib.rs`
11 /// `./foo` for `foo.rs` 11 /// `./foo` for `foo.rs`
12 /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs` 12 /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs`
@@ -16,11 +16,15 @@ pub struct ModDir {
16} 16}
17 17
18impl ModDir { 18impl ModDir {
19 pub fn root() -> ModDir { 19 pub(super) fn root() -> ModDir {
20 ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false } 20 ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false }
21 } 21 }
22 22
23 pub fn descend_into_definition(&self, name: &Name, attr_path: Option<&SmolStr>) -> ModDir { 23 pub(super) fn descend_into_definition(
24 &self,
25 name: &Name,
26 attr_path: Option<&SmolStr>,
27 ) -> ModDir {
24 let mut path = self.path.clone(); 28 let mut path = self.path.clone();
25 match attr_to_path(attr_path) { 29 match attr_to_path(attr_path) {
26 None => path.push(&name.to_string()), 30 None => path.push(&name.to_string()),
@@ -34,7 +38,7 @@ impl ModDir {
34 ModDir { path, root_non_dir_owner: false } 38 ModDir { path, root_non_dir_owner: false }
35 } 39 }
36 40
37 pub fn resolve_declaration( 41 pub(super) fn resolve_declaration(
38 &self, 42 &self,
39 db: &impl DefDatabase2, 43 db: &impl DefDatabase2,
40 file_id: HirFileId, 44 file_id: HirFileId,
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs
new file mode 100644
index 000000000..95692f826
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/path_resolution.rs
@@ -0,0 +1,261 @@
1//! This modules implements a function to resolve a path `foo::bar::baz` to a
2//! def, which is used within the name resolution.
3//!
4//! When name resolution is finished, the result of resolving a path is either
5//! `Some(def)` or `None`. However, when we are in process of resolving imports
6//! or macros, there's a third possibility:
7//!
8//! I can't resolve this path right now, but I might be resolve this path
9//! later, when more macros are expanded.
10//!
11//! `ReachedFixedPoint` signals about this.
12
13use hir_expand::name::Name;
14use ra_db::Edition;
15use test_utils::tested_by;
16
17use crate::{
18 db::DefDatabase2,
19 nameres::{per_ns::PerNs, CrateDefMap},
20 path::{Path, PathKind},
21 AdtId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId,
22};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub(super) enum ResolveMode {
26 Import,
27 Other,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub(super) enum ReachedFixedPoint {
32 Yes,
33 No,
34}
35
36#[derive(Debug, Clone)]
37pub(super) struct ResolvePathResult {
38 pub(super) resolved_def: PerNs,
39 pub(super) segment_index: Option<usize>,
40 pub(super) reached_fixedpoint: ReachedFixedPoint,
41}
42
43impl ResolvePathResult {
44 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
45 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
46 }
47
48 fn with(
49 resolved_def: PerNs,
50 reached_fixedpoint: ReachedFixedPoint,
51 segment_index: Option<usize>,
52 ) -> ResolvePathResult {
53 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
54 }
55}
56
57impl CrateDefMap {
58 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
59 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
60 }
61
62 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
63 // the result.
64 pub(super) fn resolve_path_fp_with_macro(
65 &self,
66 db: &impl DefDatabase2,
67 mode: ResolveMode,
68 original_module: CrateModuleId,
69 path: &Path,
70 ) -> ResolvePathResult {
71 let mut segments = path.segments.iter().enumerate();
72 let mut curr_per_ns: PerNs = match path.kind {
73 PathKind::DollarCrate(krate) => {
74 if krate == self.krate {
75 tested_by!(macro_dollar_crate_self);
76 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
77 } else {
78 let def_map = db.crate_def_map(krate);
79 let module = ModuleId { krate, module_id: def_map.root };
80 tested_by!(macro_dollar_crate_other);
81 PerNs::types(module.into())
82 }
83 }
84 PathKind::Crate => {
85 PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into())
86 }
87 PathKind::Self_ => {
88 PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into())
89 }
90 // plain import or absolute path in 2015: crate-relative with
91 // fallback to extern prelude (with the simplification in
92 // rust-lang/rust#57745)
93 // FIXME there must be a nicer way to write this condition
94 PathKind::Plain | PathKind::Abs
95 if self.edition == Edition::Edition2015
96 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
97 {
98 let segment = match segments.next() {
99 Some((_, segment)) => segment,
100 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
101 };
102 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
103 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
104 }
105 PathKind::Plain => {
106 let segment = match segments.next() {
107 Some((_, segment)) => segment,
108 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
109 };
110 log::debug!("resolving {:?} in module", segment);
111 self.resolve_name_in_module(db, original_module, &segment.name)
112 }
113 PathKind::Super => {
114 if let Some(p) = self.modules[original_module].parent {
115 PerNs::types(ModuleId { krate: self.krate, module_id: p }.into())
116 } else {
117 log::debug!("super path in root module");
118 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
119 }
120 }
121 PathKind::Abs => {
122 // 2018-style absolute path -- only extern prelude
123 let segment = match segments.next() {
124 Some((_, segment)) => segment,
125 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
126 };
127 if let Some(def) = self.extern_prelude.get(&segment.name) {
128 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
129 PerNs::types(*def)
130 } else {
131 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
132 }
133 }
134 PathKind::Type(_) => {
135 // This is handled in `infer::infer_path_expr`
136 // The result returned here does not matter
137 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
138 }
139 };
140
141 for (i, segment) in segments {
142 let curr = match curr_per_ns.take_types() {
143 Some(r) => r,
144 None => {
145 // we still have path segments left, but the path so far
146 // didn't resolve in the types namespace => no resolution
147 // (don't break here because `curr_per_ns` might contain
148 // something in the value namespace, and it would be wrong
149 // to return that)
150 return ResolvePathResult::empty(ReachedFixedPoint::No);
151 }
152 };
153 // resolve segment in curr
154
155 curr_per_ns = match curr {
156 ModuleDefId::ModuleId(module) => {
157 if module.krate != self.krate {
158 let path =
159 Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
160 log::debug!("resolving {:?} in other crate", path);
161 let defp_map = db.crate_def_map(module.krate);
162 let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
163 return ResolvePathResult::with(
164 def,
165 ReachedFixedPoint::Yes,
166 s.map(|s| s + i),
167 );
168 }
169
170 // Since it is a qualified path here, it should not contains legacy macros
171 match self[module.module_id].scope.get(&segment.name) {
172 Some(res) => res.def,
173 _ => {
174 log::debug!("path segment {:?} not found", segment.name);
175 return ResolvePathResult::empty(ReachedFixedPoint::No);
176 }
177 }
178 }
179 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
180 // enum variant
181 tested_by!(can_import_enum_variant);
182 let enum_data = db.enum_data(e);
183 match enum_data.variant(&segment.name) {
184 Some(local_id) => {
185 let variant = EnumVariantId { parent: e, local_id };
186 PerNs::both(variant.into(), variant.into())
187 }
188 None => {
189 return ResolvePathResult::with(
190 PerNs::types(e.into()),
191 ReachedFixedPoint::Yes,
192 Some(i),
193 );
194 }
195 }
196 }
197 s => {
198 // could be an inherent method call in UFCS form
199 // (`Struct::method`), or some other kind of associated item
200 log::debug!(
201 "path segment {:?} resolved to non-module {:?}, but is not last",
202 segment.name,
203 curr,
204 );
205
206 return ResolvePathResult::with(
207 PerNs::types(s),
208 ReachedFixedPoint::Yes,
209 Some(i),
210 );
211 }
212 };
213 }
214 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
215 }
216
217 fn resolve_name_in_module(
218 &self,
219 db: &impl DefDatabase2,
220 module: CrateModuleId,
221 name: &Name,
222 ) -> PerNs {
223 // Resolve in:
224 // - legacy scope of macro
225 // - current module / scope
226 // - extern prelude
227 // - std prelude
228 let from_legacy_macro =
229 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros);
230 let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def);
231 let from_extern_prelude =
232 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
233 let from_prelude = self.resolve_in_prelude(db, name);
234
235 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
236 }
237
238 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
239 let from_crate_root =
240 self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def);
241 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
242
243 from_crate_root.or(from_extern_prelude)
244 }
245
246 fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs {
247 if let Some(prelude) = self.prelude {
248 let keep;
249 let def_map = if prelude.krate == self.krate {
250 self
251 } else {
252 // Extend lifetime
253 keep = db.crate_def_map(prelude.krate);
254 &keep
255 };
256 def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def)
257 } else {
258 PerNs::none()
259 }
260 }
261}
diff --git a/crates/ra_hir_def/src/nameres/per_ns.rs b/crates/ra_hir_def/src/nameres/per_ns.rs
index 298b0b0c7..717ed1ef9 100644
--- a/crates/ra_hir_def/src/nameres/per_ns.rs
+++ b/crates/ra_hir_def/src/nameres/per_ns.rs
@@ -4,14 +4,6 @@ use hir_expand::MacroDefId;
4 4
5use crate::ModuleDefId; 5use crate::ModuleDefId;
6 6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub enum Namespace {
9 Types,
10 Values,
11 // Note that only type inference uses this enum, and it doesn't care about macros.
12 // Macro,
13}
14
15#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 7#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
16pub struct PerNs { 8pub struct PerNs {
17 pub types: Option<ModuleDefId>, 9 pub types: Option<ModuleDefId>,
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index cb47fa317..369376f30 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -88,7 +88,7 @@ impl RawItems {
88 (Arc::new(collector.raw_items), Arc::new(collector.source_map)) 88 (Arc::new(collector.raw_items), Arc::new(collector.source_map))
89 } 89 }
90 90
91 pub fn items(&self) -> &[RawItem] { 91 pub(super) fn items(&self) -> &[RawItem] {
92 &self.items 92 &self.items
93 } 93 }
94} 94}
@@ -125,19 +125,19 @@ impl Index<Macro> for RawItems {
125type Attrs = Option<Arc<[Attr]>>; 125type Attrs = Option<Arc<[Attr]>>;
126 126
127#[derive(Debug, PartialEq, Eq, Clone)] 127#[derive(Debug, PartialEq, Eq, Clone)]
128pub struct RawItem { 128pub(super) struct RawItem {
129 attrs: Attrs, 129 attrs: Attrs,
130 pub kind: RawItemKind, 130 pub(super) kind: RawItemKind,
131} 131}
132 132
133impl RawItem { 133impl RawItem {
134 pub fn attrs(&self) -> &[Attr] { 134 pub(super) fn attrs(&self) -> &[Attr] {
135 self.attrs.as_ref().map_or(&[], |it| &*it) 135 self.attrs.as_ref().map_or(&[], |it| &*it)
136 } 136 }
137} 137}
138 138
139#[derive(Debug, PartialEq, Eq, Clone, Copy)] 139#[derive(Debug, PartialEq, Eq, Clone, Copy)]
140pub enum RawItemKind { 140pub(super) enum RawItemKind {
141 Module(Module), 141 Module(Module),
142 Import(ImportId), 142 Import(ImportId),
143 Def(Def), 143 Def(Def),
@@ -145,11 +145,11 @@ pub enum RawItemKind {
145} 145}
146 146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
148pub struct Module(RawId); 148pub(super) struct Module(RawId);
149impl_arena_id!(Module); 149impl_arena_id!(Module);
150 150
151#[derive(Debug, PartialEq, Eq)] 151#[derive(Debug, PartialEq, Eq)]
152pub enum ModuleData { 152pub(super) enum ModuleData {
153 Declaration { name: Name, ast_id: FileAstId<ast::Module> }, 153 Declaration { name: Name, ast_id: FileAstId<ast::Module> },
154 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, 154 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
155} 155}
@@ -160,26 +160,26 @@ impl_arena_id!(ImportId);
160 160
161#[derive(Debug, Clone, PartialEq, Eq)] 161#[derive(Debug, Clone, PartialEq, Eq)]
162pub struct ImportData { 162pub struct ImportData {
163 pub path: Path, 163 pub(super) path: Path,
164 pub alias: Option<Name>, 164 pub(super) alias: Option<Name>,
165 pub is_glob: bool, 165 pub(super) is_glob: bool,
166 pub is_prelude: bool, 166 pub(super) is_prelude: bool,
167 pub is_extern_crate: bool, 167 pub(super) is_extern_crate: bool,
168 pub is_macro_use: bool, 168 pub(super) is_macro_use: bool,
169} 169}
170 170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
172pub struct Def(RawId); 172pub(super) struct Def(RawId);
173impl_arena_id!(Def); 173impl_arena_id!(Def);
174 174
175#[derive(Debug, PartialEq, Eq)] 175#[derive(Debug, PartialEq, Eq)]
176pub struct DefData { 176pub(super) struct DefData {
177 pub name: Name, 177 pub(super) name: Name,
178 pub kind: DefKind, 178 pub(super) kind: DefKind,
179} 179}
180 180
181#[derive(Debug, PartialEq, Eq, Clone, Copy)] 181#[derive(Debug, PartialEq, Eq, Clone, Copy)]
182pub enum DefKind { 182pub(super) enum DefKind {
183 Function(FileAstId<ast::FnDef>), 183 Function(FileAstId<ast::FnDef>),
184 Struct(FileAstId<ast::StructDef>), 184 Struct(FileAstId<ast::StructDef>),
185 Union(FileAstId<ast::StructDef>), 185 Union(FileAstId<ast::StructDef>),
@@ -191,15 +191,15 @@ pub enum DefKind {
191} 191}
192 192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
194pub struct Macro(RawId); 194pub(super) struct Macro(RawId);
195impl_arena_id!(Macro); 195impl_arena_id!(Macro);
196 196
197#[derive(Debug, PartialEq, Eq)] 197#[derive(Debug, PartialEq, Eq)]
198pub struct MacroData { 198pub(super) struct MacroData {
199 pub ast_id: FileAstId<ast::MacroCall>, 199 pub(super) ast_id: FileAstId<ast::MacroCall>,
200 pub path: Path, 200 pub(super) path: Path,
201 pub name: Option<Name>, 201 pub(super) name: Option<Name>,
202 pub export: bool, 202 pub(super) export: bool,
203} 203}
204 204
205struct RawItemsCollector { 205struct RawItemsCollector {
diff --git a/crates/ra_ide_api/src/completion/complete_postfix.rs b/crates/ra_ide_api/src/completion/complete_postfix.rs
index 60ed3518b..4f9565441 100644
--- a/crates/ra_ide_api/src/completion/complete_postfix.rs
+++ b/crates/ra_ide_api/src/completion/complete_postfix.rs
@@ -1,5 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Ty, TypeCtor};
4use ra_syntax::{ast::AstNode, TextRange, TextUnit};
5use ra_text_edit::TextEdit;
6
3use crate::{ 7use crate::{
4 completion::{ 8 completion::{
5 completion_context::CompletionContext, 9 completion_context::CompletionContext,
@@ -7,9 +11,53 @@ use crate::{
7 }, 11 },
8 CompletionItem, 12 CompletionItem,
9}; 13};
10use hir::{Ty, TypeCtor}; 14
11use ra_syntax::{ast::AstNode, TextRange, TextUnit}; 15pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
12use ra_text_edit::TextEdit; 16 let dot_receiver = match &ctx.dot_receiver {
17 Some(it) => it,
18 None => return,
19 };
20
21 let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
22 let text = dot_receiver.syntax().text();
23 let without_dot = ..text.len() - TextUnit::of_char('.');
24 text.slice(without_dot).to_string()
25 } else {
26 dot_receiver.syntax().text().to_string()
27 };
28
29 let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
30
31 if is_bool_or_unknown(receiver_ty) {
32 postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
33 .add_to(acc);
34 postfix_snippet(
35 ctx,
36 "while",
37 "while expr {}",
38 &format!("while {} {{\n$0\n}}", receiver_text),
39 )
40 .add_to(acc);
41 }
42
43 postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
44
45 postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
46 postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
47
48 postfix_snippet(
49 ctx,
50 "match",
51 "match expr {}",
52 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
53 )
54 .add_to(acc);
55
56 postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
57
58 postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
59 .add_to(acc);
60}
13 61
14fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { 62fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
15 let edit = { 63 let edit = {
@@ -24,62 +72,19 @@ fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet:
24} 72}
25 73
26fn is_bool_or_unknown(ty: Option<Ty>) -> bool { 74fn is_bool_or_unknown(ty: Option<Ty>) -> bool {
27 if let Some(ty) = ty { 75 match &ty {
28 match ty { 76 Some(Ty::Apply(app)) if app.ctor == TypeCtor::Bool => true,
29 Ty::Apply(at) => match at.ctor { 77 Some(Ty::Unknown) | None => true,
30 TypeCtor::Bool => true, 78 Some(_) => false,
31 _ => false,
32 },
33 Ty::Unknown => true,
34 _ => false,
35 }
36 } else {
37 true
38 }
39}
40
41pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
42 if let Some(dot_receiver) = &ctx.dot_receiver {
43 let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
44 let text = dot_receiver.syntax().text();
45 let without_dot = ..text.len() - TextUnit::of_char('.');
46 text.slice(without_dot).to_string()
47 } else {
48 dot_receiver.syntax().text().to_string()
49 };
50 let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
51 if is_bool_or_unknown(receiver_ty) {
52 postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
53 .add_to(acc);
54 postfix_snippet(
55 ctx,
56 "while",
57 "while expr {}",
58 &format!("while {} {{\n$0\n}}", receiver_text),
59 )
60 .add_to(acc);
61 }
62 postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
63 postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
64 postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
65 postfix_snippet(
66 ctx,
67 "match",
68 "match expr {}",
69 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
70 )
71 .add_to(acc);
72 postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
73 postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
74 .add_to(acc);
75 } 79 }
76} 80}
77 81
78#[cfg(test)] 82#[cfg(test)]
79mod tests { 83mod tests {
80 use crate::completion::{do_completion, CompletionItem, CompletionKind};
81 use insta::assert_debug_snapshot; 84 use insta::assert_debug_snapshot;
82 85
86 use crate::completion::{do_completion, CompletionItem, CompletionKind};
87
83 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> { 88 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
84 do_completion(code, CompletionKind::Postfix) 89 do_completion(code, CompletionKind::Postfix)
85 } 90 }
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs
index cb55d1875..d861303b7 100644
--- a/crates/ra_ide_api/src/completion/presentation.rs
+++ b/crates/ra_ide_api/src/completion/presentation.rs
@@ -1,8 +1,8 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2 2
3use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; 3use hir::{db::HirDatabase, Attrs, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
4use join_to_string::join; 4use join_to_string::join;
5use ra_syntax::ast::{AttrsOwner, NameOwner}; 5use ra_syntax::ast::NameOwner;
6use test_utils::tested_by; 6use test_utils::tested_by;
7 7
8use crate::completion::{ 8use crate::completion::{
@@ -18,11 +18,7 @@ impl Completions {
18 field: hir::StructField, 18 field: hir::StructField,
19 substs: &hir::Substs, 19 substs: &hir::Substs,
20 ) { 20 ) {
21 let ast_node = field.source(ctx.db).ast; 21 let is_deprecated = is_deprecated(field, ctx.db);
22 let is_deprecated = match ast_node {
23 hir::FieldSource::Named(m) => is_deprecated(m),
24 hir::FieldSource::Pos(m) => is_deprecated(m),
25 };
26 CompletionItem::new( 22 CompletionItem::new(
27 CompletionKind::Reference, 23 CompletionKind::Reference,
28 ctx.source_range(), 24 ctx.source_range(),
@@ -185,7 +181,7 @@ impl Completions {
185 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), &macro_declaration) 181 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), &macro_declaration)
186 .kind(CompletionItemKind::Macro) 182 .kind(CompletionItemKind::Macro)
187 .set_documentation(docs.clone()) 183 .set_documentation(docs.clone())
188 .set_deprecated(is_deprecated(ast_node)) 184 .set_deprecated(is_deprecated(macro_, ctx.db))
189 .detail(detail); 185 .detail(detail);
190 186
191 builder = if ctx.use_item_syntax.is_some() { 187 builder = if ctx.use_item_syntax.is_some() {
@@ -218,7 +214,7 @@ impl Completions {
218 CompletionItemKind::Function 214 CompletionItemKind::Function
219 }) 215 })
220 .set_documentation(func.docs(ctx.db)) 216 .set_documentation(func.docs(ctx.db))
221 .set_deprecated(is_deprecated(ast_node)) 217 .set_deprecated(is_deprecated(func, ctx.db))
222 .detail(detail); 218 .detail(detail);
223 219
224 // Add `<>` for generic types 220 // Add `<>` for generic types
@@ -250,7 +246,7 @@ impl Completions {
250 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) 246 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
251 .kind(CompletionItemKind::Const) 247 .kind(CompletionItemKind::Const)
252 .set_documentation(constant.docs(ctx.db)) 248 .set_documentation(constant.docs(ctx.db))
253 .set_deprecated(is_deprecated(ast_node)) 249 .set_deprecated(is_deprecated(constant, ctx.db))
254 .detail(detail) 250 .detail(detail)
255 .add_to(self); 251 .add_to(self);
256 } 252 }
@@ -266,13 +262,13 @@ impl Completions {
266 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) 262 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
267 .kind(CompletionItemKind::TypeAlias) 263 .kind(CompletionItemKind::TypeAlias)
268 .set_documentation(type_alias.docs(ctx.db)) 264 .set_documentation(type_alias.docs(ctx.db))
269 .set_deprecated(is_deprecated(type_def)) 265 .set_deprecated(is_deprecated(type_alias, ctx.db))
270 .detail(detail) 266 .detail(detail)
271 .add_to(self); 267 .add_to(self);
272 } 268 }
273 269
274 pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { 270 pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
275 let is_deprecated = is_deprecated(variant.source(ctx.db).ast); 271 let is_deprecated = is_deprecated(variant, ctx.db);
276 let name = match variant.name(ctx.db) { 272 let name = match variant.name(ctx.db) {
277 Some(it) => it, 273 Some(it) => it,
278 None => return, 274 None => return,
@@ -291,8 +287,11 @@ impl Completions {
291 } 287 }
292} 288}
293 289
294fn is_deprecated(node: impl AttrsOwner) -> bool { 290fn is_deprecated(node: impl Attrs, db: &impl HirDatabase) -> bool {
295 node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated") 291 match node.attrs(db) {
292 None => false,
293 Some(attrs) => attrs.iter().any(|x| x.is_simple_atom("deprecated")),
294 }
296} 295}
297 296
298fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool { 297fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index dd4d3bf9a..743f6a60a 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -9,7 +9,7 @@ threadpool = "1.7.1"
9relative-path = "1.0.0" 9relative-path = "1.0.0"
10serde_json = "1.0.34" 10serde_json = "1.0.34"
11serde = { version = "1.0.83", features = ["derive"] } 11serde = { version = "1.0.83", features = ["derive"] }
12crossbeam-channel = "0.3.5" 12crossbeam-channel = "0.4"
13flexi_logger = "0.14.0" 13flexi_logger = "0.14.0"
14log = "0.4.3" 14log = "0.4.3"
15lsp-types = { version = "0.61.0", features = ["proposed"] } 15lsp-types = { version = "0.61.0", features = ["proposed"] }
@@ -20,7 +20,7 @@ ra_vfs = "0.5.0"
20ra_syntax = { path = "../ra_syntax" } 20ra_syntax = { path = "../ra_syntax" }
21ra_text_edit = { path = "../ra_text_edit" } 21ra_text_edit = { path = "../ra_text_edit" }
22ra_ide_api = { path = "../ra_ide_api" } 22ra_ide_api = { path = "../ra_ide_api" }
23lsp-server = "0.2.0" 23lsp-server = "0.3.0"
24ra_project_model = { path = "../ra_project_model" } 24ra_project_model = { path = "../ra_project_model" }
25ra_prof = { path = "../ra_prof" } 25ra_prof = { path = "../ra_prof" }
26ra_vfs_glob = { path = "../ra_vfs_glob" } 26ra_vfs_glob = { path = "../ra_vfs_glob" }
diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs
index d4ca94fca..9b2e440fb 100644
--- a/crates/ra_parser/src/grammar/types.rs
+++ b/crates/ra_parser/src/grammar/types.rs
@@ -28,7 +28,7 @@ fn type_with_bounds_cond(p: &mut Parser, allow_bounds: bool) {
28 T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), 28 T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p),
29 T![for] => for_type(p), 29 T![for] => for_type(p),
30 T![impl] => impl_trait_type(p), 30 T![impl] => impl_trait_type(p),
31 T![dyn ] => dyn_trait_type(p), 31 T![dyn] => dyn_trait_type(p),
32 // Some path types are not allowed to have bounds (no plus) 32 // Some path types are not allowed to have bounds (no plus)
33 T![<] => path_type_(p, allow_bounds), 33 T![<] => path_type_(p, allow_bounds),
34 _ if paths::is_use_path_start(p) => path_or_macro_type_(p, allow_bounds), 34 _ if paths::is_use_path_start(p) => path_or_macro_type_(p, allow_bounds),
@@ -234,9 +234,9 @@ fn impl_trait_type(p: &mut Parser) {
234// test dyn_trait_type 234// test dyn_trait_type
235// type A = dyn Iterator<Item=Foo<'a>> + 'a; 235// type A = dyn Iterator<Item=Foo<'a>> + 'a;
236fn dyn_trait_type(p: &mut Parser) { 236fn dyn_trait_type(p: &mut Parser) {
237 assert!(p.at(T![dyn ])); 237 assert!(p.at(T![dyn]));
238 let m = p.start(); 238 let m = p.start();
239 p.bump(T![dyn ]); 239 p.bump(T![dyn]);
240 type_params::bounds_without_colon(p); 240 type_params::bounds_without_colon(p);
241 m.complete(p, DYN_TRAIT_TYPE); 241 m.complete(p, DYN_TRAIT_TYPE);
242} 242}