aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db')
-rw-r--r--crates/ide_db/src/apply_change.rs1
-rw-r--r--crates/ide_db/src/call_info/tests.rs54
-rw-r--r--crates/ide_db/src/defs.rs80
-rw-r--r--crates/ide_db/src/helpers.rs90
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs120
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs267
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs18
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs2
-rw-r--r--crates/ide_db/src/imports_locator.rs8
-rw-r--r--crates/ide_db/src/search.rs114
-rw-r--r--crates/ide_db/src/source_change.rs56
-rw-r--r--crates/ide_db/src/traits/tests.rs18
12 files changed, 605 insertions, 223 deletions
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 71c19ed13..c770a236b 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -145,6 +145,7 @@ impl RootDatabase {
145 hir::db::MacroDefQuery 145 hir::db::MacroDefQuery
146 hir::db::ParseMacroExpansionQuery 146 hir::db::ParseMacroExpansionQuery
147 hir::db::MacroExpandQuery 147 hir::db::MacroExpandQuery
148 hir::db::HygieneFrameQuery
148 149
149 // DefDatabase 150 // DefDatabase
150 hir::db::ItemTreeQuery 151 hir::db::ItemTreeQuery
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 9335aeaa5..c714cf280 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -3,12 +3,12 @@ use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect}; 3use expect_test::{expect, Expect};
4use test_utils::{mark, RangeOrOffset}; 4use test_utils::{mark, RangeOrOffset};
5 5
6/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 6/// Creates analysis from a multi-file fixture, returns positions marked with $0.
7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
8 let change_fixture = ChangeFixture::parse(ra_fixture); 8 let change_fixture = ChangeFixture::parse(ra_fixture);
9 let mut database = RootDatabase::default(); 9 let mut database = RootDatabase::default();
10 database.apply_change(change_fixture.change); 10 database.apply_change(change_fixture.change);
11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
12 let offset = match range_or_offset { 12 let offset = match range_or_offset {
13 RangeOrOffset::Range(_) => panic!(), 13 RangeOrOffset::Range(_) => panic!(),
14 RangeOrOffset::Offset(it) => it, 14 RangeOrOffset::Offset(it) => it,
@@ -49,7 +49,7 @@ fn test_fn_signature_two_args() {
49 check( 49 check(
50 r#" 50 r#"
51fn foo(x: u32, y: u32) -> u32 {x + y} 51fn foo(x: u32, y: u32) -> u32 {x + y}
52fn bar() { foo(<|>3, ); } 52fn bar() { foo($03, ); }
53"#, 53"#,
54 expect![[r#" 54 expect![[r#"
55 fn foo(x: u32, y: u32) -> u32 55 fn foo(x: u32, y: u32) -> u32
@@ -59,7 +59,7 @@ fn bar() { foo(<|>3, ); }
59 check( 59 check(
60 r#" 60 r#"
61fn foo(x: u32, y: u32) -> u32 {x + y} 61fn foo(x: u32, y: u32) -> u32 {x + y}
62fn bar() { foo(3<|>, ); } 62fn bar() { foo(3$0, ); }
63"#, 63"#,
64 expect![[r#" 64 expect![[r#"
65 fn foo(x: u32, y: u32) -> u32 65 fn foo(x: u32, y: u32) -> u32
@@ -69,7 +69,7 @@ fn bar() { foo(3<|>, ); }
69 check( 69 check(
70 r#" 70 r#"
71fn foo(x: u32, y: u32) -> u32 {x + y} 71fn foo(x: u32, y: u32) -> u32 {x + y}
72fn bar() { foo(3,<|> ); } 72fn bar() { foo(3,$0 ); }
73"#, 73"#,
74 expect![[r#" 74 expect![[r#"
75 fn foo(x: u32, y: u32) -> u32 75 fn foo(x: u32, y: u32) -> u32
@@ -79,7 +79,7 @@ fn bar() { foo(3,<|> ); }
79 check( 79 check(
80 r#" 80 r#"
81fn foo(x: u32, y: u32) -> u32 {x + y} 81fn foo(x: u32, y: u32) -> u32 {x + y}
82fn bar() { foo(3, <|>); } 82fn bar() { foo(3, $0); }
83"#, 83"#,
84 expect![[r#" 84 expect![[r#"
85 fn foo(x: u32, y: u32) -> u32 85 fn foo(x: u32, y: u32) -> u32
@@ -93,7 +93,7 @@ fn test_fn_signature_two_args_empty() {
93 check( 93 check(
94 r#" 94 r#"
95fn foo(x: u32, y: u32) -> u32 {x + y} 95fn foo(x: u32, y: u32) -> u32 {x + y}
96fn bar() { foo(<|>); } 96fn bar() { foo($0); }
97"#, 97"#,
98 expect![[r#" 98 expect![[r#"
99 fn foo(x: u32, y: u32) -> u32 99 fn foo(x: u32, y: u32) -> u32
@@ -110,7 +110,7 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
110 where T: Copy + Display, U: Debug 110 where T: Copy + Display, U: Debug
111{ x + y } 111{ x + y }
112 112
113fn bar() { foo(<|>3, ); } 113fn bar() { foo($03, ); }
114"#, 114"#,
115 expect![[r#" 115 expect![[r#"
116 fn foo(x: i32, y: {unknown}) -> u32 116 fn foo(x: i32, y: {unknown}) -> u32
@@ -124,7 +124,7 @@ fn test_fn_signature_no_params() {
124 check( 124 check(
125 r#" 125 r#"
126fn foo<T>() -> T where T: Copy + Display {} 126fn foo<T>() -> T where T: Copy + Display {}
127fn bar() { foo(<|>); } 127fn bar() { foo($0); }
128"#, 128"#,
129 expect![[r#" 129 expect![[r#"
130 fn foo() -> {unknown} 130 fn foo() -> {unknown}
@@ -140,7 +140,7 @@ fn test_fn_signature_for_impl() {
140struct F; 140struct F;
141impl F { pub fn new() { } } 141impl F { pub fn new() { } }
142fn bar() { 142fn bar() {
143 let _ : F = F::new(<|>); 143 let _ : F = F::new($0);
144} 144}
145"#, 145"#,
146 expect![[r#" 146 expect![[r#"
@@ -159,7 +159,7 @@ impl S { pub fn do_it(&self) {} }
159 159
160fn bar() { 160fn bar() {
161 let s: S = S; 161 let s: S = S;
162 s.do_it(<|>); 162 s.do_it($0);
163} 163}
164"#, 164"#,
165 expect![[r#" 165 expect![[r#"
@@ -178,7 +178,7 @@ impl S {
178 fn foo(&self, x: i32) {} 178 fn foo(&self, x: i32) {}
179} 179}
180 180
181fn main() { S.foo(<|>); } 181fn main() { S.foo($0); }
182"#, 182"#,
183 expect![[r#" 183 expect![[r#"
184 fn foo(&self, x: i32) 184 fn foo(&self, x: i32)
@@ -196,7 +196,7 @@ impl S {
196 fn foo(&self, x: i32) {} 196 fn foo(&self, x: i32) {}
197} 197}
198 198
199fn main() { S::foo(<|>); } 199fn main() { S::foo($0); }
200"#, 200"#,
201 expect![[r#" 201 expect![[r#"
202 fn foo(self: &S, x: i32) 202 fn foo(self: &S, x: i32)
@@ -216,7 +216,7 @@ fn foo(j: u32) -> u32 {
216} 216}
217 217
218fn bar() { 218fn bar() {
219 let _ = foo(<|>); 219 let _ = foo($0);
220} 220}
221"#, 221"#,
222 expect![[r#" 222 expect![[r#"
@@ -246,7 +246,7 @@ pub fn add_one(x: i32) -> i32 {
246} 246}
247 247
248pub fn do() { 248pub fn do() {
249 add_one(<|> 249 add_one($0
250}"#, 250}"#,
251 expect![[r##" 251 expect![[r##"
252 Adds one to the number given. 252 Adds one to the number given.
@@ -287,7 +287,7 @@ impl addr {
287 287
288pub fn do_it() { 288pub fn do_it() {
289 addr {}; 289 addr {};
290 addr::add_one(<|>); 290 addr::add_one($0);
291} 291}
292"#, 292"#,
293 expect![[r##" 293 expect![[r##"
@@ -331,7 +331,7 @@ impl<E> WriteHandler<E> {
331} 331}
332 332
333pub fn foo(mut r: WriteHandler<()>) { 333pub fn foo(mut r: WriteHandler<()>) {
334 r.finished(<|>); 334 r.finished($0);
335} 335}
336"#, 336"#,
337 expect![[r#" 337 expect![[r#"
@@ -351,7 +351,7 @@ fn call_info_bad_offset() {
351 check( 351 check(
352 r#" 352 r#"
353fn foo(x: u32, y: u32) -> u32 {x + y} 353fn foo(x: u32, y: u32) -> u32 {x + y}
354fn bar() { foo <|> (3, ); } 354fn bar() { foo $0 (3, ); }
355"#, 355"#,
356 expect![[""]], 356 expect![[""]],
357 ); 357 );
@@ -368,7 +368,7 @@ fn bar(_: u32) { }
368 368
369fn main() { 369fn main() {
370 let foo = Foo; 370 let foo = Foo;
371 std::thread::spawn(move || foo.bar(<|>)); 371 std::thread::spawn(move || foo.bar($0));
372} 372}
373"#, 373"#,
374 expect![[r#" 374 expect![[r#"
@@ -385,7 +385,7 @@ fn works_for_tuple_structs() {
385/// A cool tuple struct 385/// A cool tuple struct
386struct S(u32, i32); 386struct S(u32, i32);
387fn main() { 387fn main() {
388 let s = S(0, <|>); 388 let s = S(0, $0);
389} 389}
390"#, 390"#,
391 expect![[r#" 391 expect![[r#"
@@ -403,7 +403,7 @@ fn generic_struct() {
403 r#" 403 r#"
404struct S<T>(T); 404struct S<T>(T);
405fn main() { 405fn main() {
406 let s = S(<|>); 406 let s = S($0);
407} 407}
408"#, 408"#,
409 expect![[r#" 409 expect![[r#"
@@ -427,7 +427,7 @@ enum E {
427} 427}
428 428
429fn main() { 429fn main() {
430 let a = E::A(<|>); 430 let a = E::A($0);
431} 431}
432"#, 432"#,
433 expect![[r#" 433 expect![[r#"
@@ -445,7 +445,7 @@ fn cant_call_struct_record() {
445 r#" 445 r#"
446struct S { x: u32, y: i32 } 446struct S { x: u32, y: i32 }
447fn main() { 447fn main() {
448 let s = S(<|>); 448 let s = S($0);
449} 449}
450"#, 450"#,
451 expect![[""]], 451 expect![[""]],
@@ -466,7 +466,7 @@ enum E {
466} 466}
467 467
468fn main() { 468fn main() {
469 let a = E::C(<|>); 469 let a = E::C($0);
470} 470}
471"#, 471"#,
472 expect![[""]], 472 expect![[""]],
@@ -480,7 +480,7 @@ fn fn_signature_for_call_in_macro() {
480macro_rules! id { ($($tt:tt)*) => { $($tt)* } } 480macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
481fn foo() { } 481fn foo() { }
482id! { 482id! {
483 fn bar() { foo(<|>); } 483 fn bar() { foo($0); }
484} 484}
485"#, 485"#,
486 expect![[r#" 486 expect![[r#"
@@ -497,7 +497,7 @@ fn call_info_for_lambdas() {
497struct S; 497struct S;
498fn foo(s: S) -> i32 { 92 } 498fn foo(s: S) -> i32 { 92 }
499fn main() { 499fn main() {
500 (|s| foo(s))(<|>) 500 (|s| foo(s))($0)
501} 501}
502 "#, 502 "#,
503 expect![[r#" 503 expect![[r#"
@@ -512,7 +512,7 @@ fn call_info_for_fn_ptr() {
512 check( 512 check(
513 r#" 513 r#"
514fn main(f: fn(i32, f64) -> char) { 514fn main(f: fn(i32, f64) -> char) {
515 f(0, <|>) 515 f(0, $0)
516} 516}
517 "#, 517 "#,
518 expect![[r#" 518 expect![[r#"
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index cc5078bf0..d9875ffef 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,11 +6,11 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, ConstParam, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, 9 db::HirDatabase, Crate, Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef,
10 MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, 10 Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode}, 13 ast::{self, AstNode, PathSegmentKind},
14 match_ast, SyntaxKind, SyntaxNode, 14 match_ast, SyntaxKind, SyntaxNode,
15}; 15};
16 16
@@ -24,9 +24,7 @@ pub enum Definition {
24 ModuleDef(ModuleDef), 24 ModuleDef(ModuleDef),
25 SelfType(Impl), 25 SelfType(Impl),
26 Local(Local), 26 Local(Local),
27 TypeParam(TypeParam), 27 GenericParam(GenericParam),
28 LifetimeParam(LifetimeParam),
29 ConstParam(ConstParam),
30 Label(Label), 28 Label(Label),
31} 29}
32 30
@@ -38,9 +36,7 @@ impl Definition {
38 Definition::ModuleDef(it) => it.module(db), 36 Definition::ModuleDef(it) => it.module(db),
39 Definition::SelfType(it) => Some(it.module(db)), 37 Definition::SelfType(it) => Some(it.module(db)),
40 Definition::Local(it) => Some(it.module(db)), 38 Definition::Local(it) => Some(it.module(db)),
41 Definition::TypeParam(it) => Some(it.module(db)), 39 Definition::GenericParam(it) => Some(it.module(db)),
42 Definition::LifetimeParam(it) => Some(it.module(db)),
43 Definition::ConstParam(it) => Some(it.module(db)),
44 Definition::Label(it) => Some(it.module(db)), 40 Definition::Label(it) => Some(it.module(db)),
45 } 41 }
46 } 42 }
@@ -52,9 +48,7 @@ impl Definition {
52 Definition::ModuleDef(def) => def.definition_visibility(db), 48 Definition::ModuleDef(def) => def.definition_visibility(db),
53 Definition::SelfType(_) => None, 49 Definition::SelfType(_) => None,
54 Definition::Local(_) => None, 50 Definition::Local(_) => None,
55 Definition::TypeParam(_) => None, 51 Definition::GenericParam(_) => None,
56 Definition::LifetimeParam(_) => None,
57 Definition::ConstParam(_) => None,
58 Definition::Label(_) => None, 52 Definition::Label(_) => None,
59 } 53 }
60 } 54 }
@@ -80,9 +74,7 @@ impl Definition {
80 }, 74 },
81 Definition::SelfType(_) => return None, 75 Definition::SelfType(_) => return None,
82 Definition::Local(it) => it.name(db)?, 76 Definition::Local(it) => it.name(db)?,
83 Definition::TypeParam(it) => it.name(db), 77 Definition::GenericParam(it) => it.name(db),
84 Definition::LifetimeParam(it) => it.name(db),
85 Definition::ConstParam(it) => it.name(db),
86 Definition::Label(it) => it.name(db), 78 Definition::Label(it) => it.name(db),
87 }; 79 };
88 Some(name) 80 Some(name)
@@ -143,24 +135,26 @@ impl NameClass {
143 let path = use_tree.path()?; 135 let path = use_tree.path()?;
144 let path_segment = path.segment()?; 136 let path_segment = path.segment()?;
145 let name_ref_class = path_segment 137 let name_ref_class = path_segment
146 .name_ref() 138 .kind()
147 // The rename might be from a `self` token, so fallback to the name higher 139 .and_then(|kind| {
148 // in the use tree. 140 match kind {
149 .or_else(||{ 141 // The rename might be from a `self` token, so fallback to the name higher
150 if path_segment.self_token().is_none() { 142 // in the use tree.
151 return None; 143 PathSegmentKind::SelfKw => {
144 let use_tree = use_tree
145 .syntax()
146 .parent()
147 .as_ref()
148 // Skip over UseTreeList
149 .and_then(SyntaxNode::parent)
150 .and_then(ast::UseTree::cast)?;
151 let path = use_tree.path()?;
152 let path_segment = path.segment()?;
153 path_segment.name_ref()
154 },
155 PathSegmentKind::Name(name_ref) => Some(name_ref),
156 _ => return None,
152 } 157 }
153
154 let use_tree = use_tree
155 .syntax()
156 .parent()
157 .as_ref()
158 // Skip over UseTreeList
159 .and_then(SyntaxNode::parent)
160 .and_then(ast::UseTree::cast)?;
161 let path = use_tree.path()?;
162 let path_segment = path.segment()?;
163 path_segment.name_ref()
164 }) 158 })
165 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?; 159 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
166 160
@@ -185,6 +179,10 @@ impl NameClass {
185 179
186 Some(NameClass::Definition(Definition::Local(local))) 180 Some(NameClass::Definition(Definition::Local(local)))
187 }, 181 },
182 ast::SelfParam(it) => {
183 let def = sema.to_def(&it)?;
184 Some(NameClass::Definition(Definition::Local(def.into())))
185 },
188 ast::RecordField(it) => { 186 ast::RecordField(it) => {
189 let field: hir::Field = sema.to_def(&it)?; 187 let field: hir::Field = sema.to_def(&it)?;
190 Some(NameClass::Definition(Definition::Field(field))) 188 Some(NameClass::Definition(Definition::Field(field)))
@@ -235,11 +233,11 @@ impl NameClass {
235 }, 233 },
236 ast::TypeParam(it) => { 234 ast::TypeParam(it) => {
237 let def = sema.to_def(&it)?; 235 let def = sema.to_def(&it)?;
238 Some(NameClass::Definition(Definition::TypeParam(def))) 236 Some(NameClass::Definition(Definition::GenericParam(def.into())))
239 }, 237 },
240 ast::ConstParam(it) => { 238 ast::ConstParam(it) => {
241 let def = sema.to_def(&it)?; 239 let def = sema.to_def(&it)?;
242 Some(NameClass::Definition(Definition::ConstParam(def))) 240 Some(NameClass::Definition(Definition::GenericParam(def.into())))
243 }, 241 },
244 _ => None, 242 _ => None,
245 } 243 }
@@ -257,7 +255,7 @@ impl NameClass {
257 match parent { 255 match parent {
258 ast::LifetimeParam(it) => { 256 ast::LifetimeParam(it) => {
259 let def = sema.to_def(&it)?; 257 let def = sema.to_def(&it)?;
260 Some(NameClass::Definition(Definition::LifetimeParam(def))) 258 Some(NameClass::Definition(Definition::GenericParam(def.into())))
261 }, 259 },
262 ast::Label(it) => { 260 ast::Label(it) => {
263 let def = sema.to_def(&it)?; 261 let def = sema.to_def(&it)?;
@@ -358,7 +356,7 @@ impl NameRefClass {
358 if let Some(path) = macro_call.path() { 356 if let Some(path) = macro_call.path() {
359 if path.qualifier().is_none() { 357 if path.qualifier().is_none() {
360 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment 358 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
361 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate). 359 // paths are handled below (allowing `log$0::info!` to resolve to the log crate).
362 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) { 360 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
363 return Some(NameRefClass::Definition(Definition::Macro(macro_def))); 361 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
364 } 362 }
@@ -393,7 +391,8 @@ impl NameRefClass {
393 | SyntaxKind::WHERE_PRED 391 | SyntaxKind::WHERE_PRED
394 | SyntaxKind::REF_TYPE => sema 392 | SyntaxKind::REF_TYPE => sema
395 .resolve_lifetime_param(lifetime) 393 .resolve_lifetime_param(lifetime)
396 .map(Definition::LifetimeParam) 394 .map(GenericParam::LifetimeParam)
395 .map(Definition::GenericParam)
397 .map(NameRefClass::Definition), 396 .map(NameRefClass::Definition),
398 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check 397 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
399 // if our lifetime is in a LifetimeParam without being the constrained lifetime 398 // if our lifetime is in a LifetimeParam without being the constrained lifetime
@@ -401,7 +400,8 @@ impl NameRefClass {
401 != Some(lifetime) => 400 != Some(lifetime) =>
402 { 401 {
403 sema.resolve_lifetime_param(lifetime) 402 sema.resolve_lifetime_param(lifetime)
404 .map(Definition::LifetimeParam) 403 .map(GenericParam::LifetimeParam)
404 .map(Definition::GenericParam)
405 .map(NameRefClass::Definition) 405 .map(NameRefClass::Definition)
406 } 406 }
407 _ => None, 407 _ => None,
@@ -422,10 +422,10 @@ impl From<PathResolution> for Definition {
422 Definition::ModuleDef(def) 422 Definition::ModuleDef(def)
423 } 423 }
424 PathResolution::Local(local) => Definition::Local(local), 424 PathResolution::Local(local) => Definition::Local(local),
425 PathResolution::TypeParam(par) => Definition::TypeParam(par), 425 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
426 PathResolution::Macro(def) => Definition::Macro(def), 426 PathResolution::Macro(def) => Definition::Macro(def),
427 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), 427 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
428 PathResolution::ConstParam(par) => Definition::ConstParam(par), 428 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
429 } 429 }
430 } 430 }
431} 431}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index e3e5670f1..0dcc4dd29 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -1,5 +1,6 @@
1//! A module with ide helpers for high-level ide features. 1//! A module with ide helpers for high-level ide features.
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets;
3 4
4use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 5use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
5use syntax::ast::{self, make}; 6use syntax::ast::{self, make};
@@ -38,94 +39,7 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr
38 39
39#[allow(non_snake_case)] 40#[allow(non_snake_case)]
40impl FamousDefs<'_, '_> { 41impl FamousDefs<'_, '_> {
41 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core 42 pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs");
42pub mod convert {
43 pub trait From<T> {
44 fn from(t: T) -> Self;
45 }
46}
47
48pub mod default {
49 pub trait Default {
50 fn default() -> Self;
51 }
52}
53
54pub mod iter {
55 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
56 mod traits {
57 pub(crate) mod iterator {
58 use crate::option::Option;
59 pub trait Iterator {
60 type Item;
61 fn next(&mut self) -> Option<Self::Item>;
62 fn by_ref(&mut self) -> &mut Self {
63 self
64 }
65 fn take(self, n: usize) -> crate::iter::Take<Self> {
66 crate::iter::Take { inner: self }
67 }
68 }
69
70 impl<I: Iterator> Iterator for &mut I {
71 type Item = I::Item;
72 fn next(&mut self) -> Option<I::Item> {
73 (**self).next()
74 }
75 }
76 }
77 pub(crate) mod collect {
78 pub trait IntoIterator {
79 type Item;
80 }
81 }
82 }
83
84 pub use self::sources::*;
85 pub(crate) mod sources {
86 use super::Iterator;
87 use crate::option::Option::{self, *};
88 pub struct Repeat<A> {
89 element: A,
90 }
91
92 pub fn repeat<T>(elt: T) -> Repeat<T> {
93 Repeat { element: elt }
94 }
95
96 impl<A> Iterator for Repeat<A> {
97 type Item = A;
98
99 fn next(&mut self) -> Option<A> {
100 None
101 }
102 }
103 }
104
105 pub use self::adapters::*;
106 pub(crate) mod adapters {
107 use super::Iterator;
108 use crate::option::Option::{self, *};
109 pub struct Take<I> { pub(crate) inner: I }
110 impl<I> Iterator for Take<I> where I: Iterator {
111 type Item = <I as Iterator>::Item;
112 fn next(&mut self) -> Option<<I as Iterator>::Item> {
113 None
114 }
115 }
116 }
117}
118
119pub mod option {
120 pub enum Option<T> { None, Some(T)}
121}
122
123pub mod prelude {
124 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
125}
126#[prelude_import]
127pub use prelude::*;
128"#;
129 43
130 pub fn core(&self) -> Option<Crate> { 44 pub fn core(&self) -> Option<Crate> {
131 self.find_crate("core") 45 self.find_crate("core")
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
new file mode 100644
index 000000000..5e88de64d
--- /dev/null
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -0,0 +1,120 @@
1//- /libcore.rs crate:core
2//! Signatures of traits, types and functions from the core lib for use in tests.
3pub mod convert {
4 pub trait From<T> {
5 fn from(t: T) -> Self;
6 }
7}
8
9pub mod default {
10 pub trait Default {
11 fn default() -> Self;
12 }
13}
14
15pub mod iter {
16 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
17 mod traits {
18 pub(crate) mod iterator {
19 use crate::option::Option;
20 pub trait Iterator {
21 type Item;
22 fn next(&mut self) -> Option<Self::Item>;
23 fn by_ref(&mut self) -> &mut Self {
24 self
25 }
26 fn take(self, n: usize) -> crate::iter::Take<Self> {
27 crate::iter::Take { inner: self }
28 }
29 }
30
31 impl<I: Iterator> Iterator for &mut I {
32 type Item = I::Item;
33 fn next(&mut self) -> Option<I::Item> {
34 (**self).next()
35 }
36 }
37 }
38 pub(crate) mod collect {
39 pub trait IntoIterator {
40 type Item;
41 }
42 }
43 }
44
45 pub use self::sources::*;
46 pub(crate) mod sources {
47 use super::Iterator;
48 use crate::option::Option::{self, *};
49 pub struct Repeat<A> {
50 element: A,
51 }
52
53 pub fn repeat<T>(elt: T) -> Repeat<T> {
54 Repeat { element: elt }
55 }
56
57 impl<A> Iterator for Repeat<A> {
58 type Item = A;
59
60 fn next(&mut self) -> Option<A> {
61 None
62 }
63 }
64 }
65
66 pub use self::adapters::*;
67 pub(crate) mod adapters {
68 use super::Iterator;
69 use crate::option::Option::{self, *};
70 pub struct Take<I> {
71 pub(crate) inner: I,
72 }
73 impl<I> Iterator for Take<I>
74 where
75 I: Iterator,
76 {
77 type Item = <I as Iterator>::Item;
78 fn next(&mut self) -> Option<<I as Iterator>::Item> {
79 None
80 }
81 }
82 }
83}
84
85pub mod ops {
86 #[lang = "fn"]
87 pub trait Fn<Args>: FnMut<Args> {
88 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
89 }
90
91 #[lang = "fn_mut"]
92 pub trait FnMut<Args>: FnOnce<Args> {
93 extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
94 }
95 #[lang = "fn_once"]
96 pub trait FnOnce<Args> {
97 #[lang = "fn_once_output"]
98 type Output;
99 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
100 }
101}
102
103pub mod option {
104 pub enum Option<T> {
105 None,
106 Some(T),
107 }
108}
109
110pub mod prelude {
111 pub use crate::{
112 convert::From,
113 default::Default,
114 iter::{IntoIterator, Iterator},
115 ops::{Fn, FnMut, FnOnce},
116 option::Option::{self, *},
117 };
118}
119#[prelude_import]
120pub use prelude::*;
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
new file mode 100644
index 000000000..edc3da318
--- /dev/null
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -0,0 +1,267 @@
1//! Look up accessible paths for items.
2use either::Either;
3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics};
4use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode, SyntaxNode};
6
7use crate::{imports_locator, RootDatabase};
8
9use super::insert_use::InsertUseConfig;
10
11#[derive(Debug)]
12pub enum ImportCandidate {
13 // A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
14 Path(PathImportCandidate),
15 /// A trait associated function (with no self parameter) or associated constant.
16 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
17 /// and `name` is the `test_function`
18 TraitAssocItem(TraitImportCandidate),
19 /// A trait method with self parameter.
20 /// For 'test_enum.test_method()', `ty` is the `test_enum` expression type
21 /// and `name` is the `test_method`
22 TraitMethod(TraitImportCandidate),
23}
24
25#[derive(Debug)]
26pub struct TraitImportCandidate {
27 pub ty: hir::Type,
28 pub name: ast::NameRef,
29}
30
31#[derive(Debug)]
32pub struct PathImportCandidate {
33 pub qualifier: Option<ast::Path>,
34 pub name: ast::NameRef,
35}
36
37#[derive(Debug)]
38pub struct ImportAssets {
39 import_candidate: ImportCandidate,
40 module_with_name_to_import: hir::Module,
41 syntax_under_caret: SyntaxNode,
42}
43
44impl ImportAssets {
45 pub fn for_method_call(
46 method_call: ast::MethodCallExpr,
47 sema: &Semantics<RootDatabase>,
48 ) -> Option<Self> {
49 let syntax_under_caret = method_call.syntax().to_owned();
50 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
51 Some(Self {
52 import_candidate: ImportCandidate::for_method_call(sema, &method_call)?,
53 module_with_name_to_import,
54 syntax_under_caret,
55 })
56 }
57
58 pub fn for_regular_path(
59 path_under_caret: ast::Path,
60 sema: &Semantics<RootDatabase>,
61 ) -> Option<Self> {
62 let syntax_under_caret = path_under_caret.syntax().to_owned();
63 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
64 return None;
65 }
66
67 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
68 Some(Self {
69 import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?,
70 module_with_name_to_import,
71 syntax_under_caret,
72 })
73 }
74
75 pub fn syntax_under_caret(&self) -> &SyntaxNode {
76 &self.syntax_under_caret
77 }
78
79 pub fn import_candidate(&self) -> &ImportCandidate {
80 &self.import_candidate
81 }
82
83 fn get_search_query(&self) -> &str {
84 match &self.import_candidate {
85 ImportCandidate::Path(candidate) => candidate.name.text(),
86 ImportCandidate::TraitAssocItem(candidate)
87 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(),
88 }
89 }
90
91 pub fn search_for_imports(
92 &self,
93 sema: &Semantics<RootDatabase>,
94 config: &InsertUseConfig,
95 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
96 let _p = profile::span("import_assists::search_for_imports");
97 self.search_for(sema, Some(config.prefix_kind))
98 }
99
100 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
101 #[allow(dead_code)]
102 pub fn search_for_relative_paths(
103 &self,
104 sema: &Semantics<RootDatabase>,
105 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
106 let _p = profile::span("import_assists::search_for_relative_paths");
107 self.search_for(sema, None)
108 }
109
110 fn search_for(
111 &self,
112 sema: &Semantics<RootDatabase>,
113 prefixed: Option<hir::PrefixKind>,
114 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
115 let db = sema.db;
116 let mut trait_candidates = FxHashSet::default();
117 let current_crate = self.module_with_name_to_import.krate();
118
119 let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| {
120 trait_candidates.clear();
121 match &self.import_candidate {
122 ImportCandidate::TraitAssocItem(trait_candidate) => {
123 let located_assoc_item = match candidate {
124 Either::Left(ModuleDef::Function(located_function)) => {
125 located_function.as_assoc_item(db)
126 }
127 Either::Left(ModuleDef::Const(located_const)) => {
128 located_const.as_assoc_item(db)
129 }
130 _ => None,
131 }
132 .map(|assoc| assoc.container(db))
133 .and_then(Self::assoc_to_trait)?;
134
135 trait_candidates.insert(located_assoc_item.into());
136
137 trait_candidate
138 .ty
139 .iterate_path_candidates(
140 db,
141 current_crate,
142 &trait_candidates,
143 None,
144 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
145 )
146 .map(ModuleDef::from)
147 .map(Either::Left)
148 }
149 ImportCandidate::TraitMethod(trait_candidate) => {
150 let located_assoc_item =
151 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
152 located_function
153 .as_assoc_item(db)
154 .map(|assoc| assoc.container(db))
155 .and_then(Self::assoc_to_trait)
156 } else {
157 None
158 }?;
159
160 trait_candidates.insert(located_assoc_item.into());
161
162 trait_candidate
163 .ty
164 .iterate_method_candidates(
165 db,
166 current_crate,
167 &trait_candidates,
168 None,
169 |_, function| {
170 Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
171 },
172 )
173 .map(ModuleDef::from)
174 .map(Either::Left)
175 }
176 _ => Some(candidate),
177 }
178 };
179
180 let mut res = imports_locator::find_exact_imports(
181 sema,
182 current_crate,
183 self.get_search_query().to_string(),
184 )
185 .filter_map(filter)
186 .filter_map(|candidate| {
187 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
188 if let Some(prefix_kind) = prefixed {
189 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
190 } else {
191 self.module_with_name_to_import.find_use_path(db, item)
192 }
193 .map(|path| (path, item))
194 })
195 .filter(|(use_path, _)| use_path.len() > 1)
196 .take(20)
197 .collect::<Vec<_>>();
198 res.sort_by_key(|(path, _)| path.clone());
199 res
200 }
201
202 fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> {
203 if let AssocItemContainer::Trait(extracted_trait) = assoc {
204 Some(extracted_trait)
205 } else {
206 None
207 }
208 }
209}
210
211impl ImportCandidate {
212 fn for_method_call(
213 sema: &Semantics<RootDatabase>,
214 method_call: &ast::MethodCallExpr,
215 ) -> Option<Self> {
216 match sema.resolve_method_call(method_call) {
217 Some(_) => None,
218 None => Some(Self::TraitMethod(TraitImportCandidate {
219 ty: sema.type_of_expr(&method_call.receiver()?)?,
220 name: method_call.name_ref()?,
221 })),
222 }
223 }
224
225 fn for_regular_path(
226 sema: &Semantics<RootDatabase>,
227 path_under_caret: &ast::Path,
228 ) -> Option<Self> {
229 if sema.resolve_path(path_under_caret).is_some() {
230 return None;
231 }
232
233 let segment = path_under_caret.segment()?;
234 let candidate = if let Some(qualifier) = path_under_caret.qualifier() {
235 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
236 let qualifier_start_path =
237 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
238 if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) {
239 let qualifier_resolution = if qualifier_start_path == qualifier {
240 qualifier_start_resolution
241 } else {
242 sema.resolve_path(&qualifier)?
243 };
244 match qualifier_resolution {
245 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
246 ImportCandidate::TraitAssocItem(TraitImportCandidate {
247 ty: assoc_item_path.ty(sema.db),
248 name: segment.name_ref()?,
249 })
250 }
251 _ => return None,
252 }
253 } else {
254 ImportCandidate::Path(PathImportCandidate {
255 qualifier: Some(qualifier),
256 name: qualifier_start,
257 })
258 }
259 } else {
260 ImportCandidate::Path(PathImportCandidate {
261 qualifier: None,
262 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?,
263 })
264 };
265 Some(candidate)
266 }
267}
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index d6b498be3..877d4f1c7 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -15,6 +15,12 @@ use syntax::{
15}; 15};
16use test_utils::mark; 16use test_utils::mark;
17 17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct InsertUseConfig {
20 pub merge: Option<MergeBehavior>,
21 pub prefix_kind: hir::PrefixKind,
22}
23
18#[derive(Debug, Clone)] 24#[derive(Debug, Clone)]
19pub enum ImportScope { 25pub enum ImportScope {
20 File(ast::SourceFile), 26 File(ast::SourceFile),
@@ -97,7 +103,7 @@ pub fn insert_use<'a>(
97) -> SyntaxRewriter<'a> { 103) -> SyntaxRewriter<'a> {
98 let _p = profile::span("insert_use"); 104 let _p = profile::span("insert_use");
99 let mut rewriter = SyntaxRewriter::default(); 105 let mut rewriter = SyntaxRewriter::default();
100 let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); 106 let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false));
101 // merge into existing imports if possible 107 // merge into existing imports if possible
102 if let Some(mb) = merge { 108 if let Some(mb) = merge {
103 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { 109 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
@@ -444,8 +450,14 @@ fn use_tree_path_cmp(a: &ast::Path, a_has_tl: bool, b: &ast::Path, b_has_tl: boo
444} 450}
445 451
446fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { 452fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
447 let a = a.name_ref(); 453 let a = a.kind().and_then(|kind| match kind {
448 let b = b.name_ref(); 454 PathSegmentKind::Name(name_ref) => Some(name_ref),
455 _ => None,
456 });
457 let b = b.kind().and_then(|kind| match kind {
458 PathSegmentKind::Name(name_ref) => Some(name_ref),
459 _ => None,
460 });
449 a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text)) 461 a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text))
450} 462}
451 463
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index a603fe87f..4bbe66f1f 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -599,7 +599,7 @@ fn check(
599 599
600 let rewriter = insert_use(&file, path, mb); 600 let rewriter = insert_use(&file, path, mb);
601 let result = rewriter.rewrite(file.as_syntax_node()).to_string(); 601 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
602 assert_eq_text!(&result, ra_fixture_after); 602 assert_eq_text!(ra_fixture_after, &result);
603} 603}
604 604
605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index 0782ab070..d111fba92 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,4 +1,4 @@
1//! This module contains an import search funcionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics};
@@ -12,6 +12,8 @@ use crate::{
12use either::Either; 12use either::Either;
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14 14
15const QUERY_SEARCH_LIMIT: usize = 40;
16
15pub fn find_exact_imports<'a>( 17pub fn find_exact_imports<'a>(
16 sema: &Semantics<'a, RootDatabase>, 18 sema: &Semantics<'a, RootDatabase>,
17 krate: Crate, 19 krate: Crate,
@@ -24,11 +26,11 @@ pub fn find_exact_imports<'a>(
24 { 26 {
25 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 27 let mut local_query = symbol_index::Query::new(name_to_import.clone());
26 local_query.exact(); 28 local_query.exact();
27 local_query.limit(40); 29 local_query.limit(QUERY_SEARCH_LIMIT);
28 local_query 30 local_query
29 }, 31 },
30 import_map::Query::new(name_to_import) 32 import_map::Query::new(name_to_import)
31 .limit(40) 33 .limit(QUERY_SEARCH_LIMIT)
32 .name_only() 34 .name_only()
33 .search_mode(import_map::SearchMode::Equals) 35 .search_mode(import_map::SearchMode::Equals)
34 .case_sensitive(), 36 .case_sensitive(),
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 37b06027c..0ecb13a64 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -18,9 +18,43 @@ use crate::{
18 RootDatabase, 18 RootDatabase,
19}; 19};
20 20
21#[derive(Debug, Default, Clone)]
22pub struct UsageSearchResult {
23 pub references: FxHashMap<FileId, Vec<FileReference>>,
24}
25
26impl UsageSearchResult {
27 pub fn is_empty(&self) -> bool {
28 self.references.is_empty()
29 }
30
31 pub fn len(&self) -> usize {
32 self.references.len()
33 }
34
35 pub fn iter(&self) -> impl Iterator<Item = (&FileId, &Vec<FileReference>)> + '_ {
36 self.references.iter()
37 }
38
39 pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ {
40 self.references.iter().flat_map(|(&file_id, refs)| {
41 refs.iter().map(move |&FileReference { range, .. }| FileRange { file_id, range })
42 })
43 }
44}
45
46impl IntoIterator for UsageSearchResult {
47 type Item = (FileId, Vec<FileReference>);
48 type IntoIter = <FxHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
49
50 fn into_iter(self) -> Self::IntoIter {
51 self.references.into_iter()
52 }
53}
54
21#[derive(Debug, Clone)] 55#[derive(Debug, Clone)]
22pub struct Reference { 56pub struct FileReference {
23 pub file_range: FileRange, 57 pub range: TextRange,
24 pub kind: ReferenceKind, 58 pub kind: ReferenceKind,
25 pub access: Option<ReferenceAccess>, 59 pub access: Option<ReferenceAccess>,
26} 60}
@@ -31,7 +65,7 @@ pub enum ReferenceKind {
31 FieldShorthandForLocal, 65 FieldShorthandForLocal,
32 StructLiteral, 66 StructLiteral,
33 RecordFieldExprOrPat, 67 RecordFieldExprOrPat,
34 SelfKw, 68 SelfParam,
35 EnumLiteral, 69 EnumLiteral,
36 Lifetime, 70 Lifetime,
37 Other, 71 Other,
@@ -136,7 +170,7 @@ impl Definition {
136 return SearchScope::new(res); 170 return SearchScope::new(res);
137 } 171 }
138 172
139 if let Definition::LifetimeParam(param) = self { 173 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
140 let range = match param.parent(db) { 174 let range = match param.parent(db) {
141 hir::GenericDef::Function(it) => { 175 hir::GenericDef::Function(it) => {
142 it.source(db).and_then(|src| Some(src.value.syntax().text_range())) 176 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
@@ -252,23 +286,23 @@ impl<'a> FindUsages<'a> {
252 286
253 pub fn at_least_one(self) -> bool { 287 pub fn at_least_one(self) -> bool {
254 let mut found = false; 288 let mut found = false;
255 self.search(&mut |_reference| { 289 self.search(&mut |_, _| {
256 found = true; 290 found = true;
257 true 291 true
258 }); 292 });
259 found 293 found
260 } 294 }
261 295
262 pub fn all(self) -> Vec<Reference> { 296 pub fn all(self) -> UsageSearchResult {
263 let mut res = Vec::new(); 297 let mut res = UsageSearchResult::default();
264 self.search(&mut |reference| { 298 self.search(&mut |file_id, reference| {
265 res.push(reference); 299 res.references.entry(file_id).or_default().push(reference);
266 false 300 false
267 }); 301 });
268 res 302 res
269 } 303 }
270 304
271 fn search(self, sink: &mut dyn FnMut(Reference) -> bool) { 305 fn search(self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
272 let _p = profile::span("FindUsages:search"); 306 let _p = profile::span("FindUsages:search");
273 let sema = self.sema; 307 let sema = self.sema;
274 308
@@ -320,16 +354,14 @@ impl<'a> FindUsages<'a> {
320 fn found_lifetime( 354 fn found_lifetime(
321 &self, 355 &self,
322 lifetime: &ast::Lifetime, 356 lifetime: &ast::Lifetime,
323 sink: &mut dyn FnMut(Reference) -> bool, 357 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
324 ) -> bool { 358 ) -> bool {
325 match NameRefClass::classify_lifetime(self.sema, lifetime) { 359 match NameRefClass::classify_lifetime(self.sema, lifetime) {
326 Some(NameRefClass::Definition(def)) if &def == self.def => { 360 Some(NameRefClass::Definition(def)) if &def == self.def => {
327 let reference = Reference { 361 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
328 file_range: self.sema.original_range(lifetime.syntax()), 362 let reference =
329 kind: ReferenceKind::Lifetime, 363 FileReference { range, kind: ReferenceKind::Lifetime, access: None };
330 access: None, 364 sink(file_id, reference)
331 };
332 sink(reference)
333 } 365 }
334 _ => false, // not a usage 366 _ => false, // not a usage
335 } 367 }
@@ -338,7 +370,7 @@ impl<'a> FindUsages<'a> {
338 fn found_name_ref( 370 fn found_name_ref(
339 &self, 371 &self,
340 name_ref: &ast::NameRef, 372 name_ref: &ast::NameRef,
341 sink: &mut dyn FnMut(Reference) -> bool, 373 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
342 ) -> bool { 374 ) -> bool {
343 match NameRefClass::classify(self.sema, &name_ref) { 375 match NameRefClass::classify(self.sema, &name_ref) {
344 Some(NameRefClass::Definition(def)) if &def == self.def => { 376 Some(NameRefClass::Definition(def)) if &def == self.def => {
@@ -352,46 +384,50 @@ impl<'a> FindUsages<'a> {
352 ReferenceKind::Other 384 ReferenceKind::Other
353 }; 385 };
354 386
355 let reference = Reference { 387 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
356 file_range: self.sema.original_range(name_ref.syntax()), 388 let reference =
357 kind, 389 FileReference { range, kind, access: reference_access(&def, &name_ref) };
358 access: reference_access(&def, &name_ref), 390 sink(file_id, reference)
359 };
360 sink(reference)
361 } 391 }
362 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 392 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
393 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
363 let reference = match self.def { 394 let reference = match self.def {
364 Definition::Field(_) if &field == self.def => Reference { 395 Definition::Field(_) if &field == self.def => FileReference {
365 file_range: self.sema.original_range(name_ref.syntax()), 396 range,
366 kind: ReferenceKind::FieldShorthandForField, 397 kind: ReferenceKind::FieldShorthandForField,
367 access: reference_access(&field, &name_ref), 398 access: reference_access(&field, &name_ref),
368 }, 399 },
369 Definition::Local(l) if &local == l => Reference { 400 Definition::Local(l) if &local == l => FileReference {
370 file_range: self.sema.original_range(name_ref.syntax()), 401 range,
371 kind: ReferenceKind::FieldShorthandForLocal, 402 kind: ReferenceKind::FieldShorthandForLocal,
372 access: reference_access(&Definition::Local(local), &name_ref), 403 access: reference_access(&Definition::Local(local), &name_ref),
373 }, 404 },
374 _ => return false, // not a usage 405 _ => return false, // not a usage
375 }; 406 };
376 sink(reference) 407 sink(file_id, reference)
377 } 408 }
378 _ => false, // not a usage 409 _ => false, // not a usage
379 } 410 }
380 } 411 }
381 412
382 fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { 413 fn found_name(
414 &self,
415 name: &ast::Name,
416 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
417 ) -> bool {
383 match NameClass::classify(self.sema, name) { 418 match NameClass::classify(self.sema, name) {
384 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { 419 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => {
385 let reference = match self.def { 420 if !matches!(self.def, Definition::Field(_) if &field_ref == self.def) {
386 Definition::Field(_) if &field_ref == self.def => Reference { 421 return false;
387 file_range: self.sema.original_range(name.syntax()), 422 }
388 kind: ReferenceKind::FieldShorthandForField, 423 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
389 // FIXME: mutable patterns should have `Write` access 424 let reference = FileReference {
390 access: Some(ReferenceAccess::Read), 425 range,
391 }, 426 kind: ReferenceKind::FieldShorthandForField,
392 _ => return false, // not a usage 427 // FIXME: mutable patterns should have `Write` access
428 access: Some(ReferenceAccess::Read),
393 }; 429 };
394 sink(reference) 430 sink(file_id, reference)
395 } 431 }
396 _ => false, // not a usage 432 _ => false, // not a usage
397 } 433 }
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index 10c0abdac..b1f87731b 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -3,12 +3,19 @@
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `AnalysisChange`.
5 5
6use std::{
7 collections::hash_map::Entry,
8 iter::{self, FromIterator},
9};
10
6use base_db::{AnchoredPathBuf, FileId}; 11use base_db::{AnchoredPathBuf, FileId};
12use rustc_hash::FxHashMap;
13use stdx::assert_never;
7use text_edit::TextEdit; 14use text_edit::TextEdit;
8 15
9#[derive(Default, Debug, Clone)] 16#[derive(Default, Debug, Clone)]
10pub struct SourceChange { 17pub struct SourceChange {
11 pub source_file_edits: Vec<SourceFileEdit>, 18 pub source_file_edits: FxHashMap<FileId, TextEdit>,
12 pub file_system_edits: Vec<FileSystemEdit>, 19 pub file_system_edits: Vec<FileSystemEdit>,
13 pub is_snippet: bool, 20 pub is_snippet: bool,
14} 21}
@@ -17,27 +24,50 @@ impl SourceChange {
17 /// Creates a new SourceChange with the given label 24 /// Creates a new SourceChange with the given label
18 /// from the edits. 25 /// from the edits.
19 pub fn from_edits( 26 pub fn from_edits(
20 source_file_edits: Vec<SourceFileEdit>, 27 source_file_edits: FxHashMap<FileId, TextEdit>,
21 file_system_edits: Vec<FileSystemEdit>, 28 file_system_edits: Vec<FileSystemEdit>,
22 ) -> Self { 29 ) -> Self {
23 SourceChange { source_file_edits, file_system_edits, is_snippet: false } 30 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
24 } 31 }
25}
26 32
27#[derive(Debug, Clone)] 33 pub fn from_text_edit(file_id: FileId, edit: TextEdit) -> Self {
28pub struct SourceFileEdit { 34 SourceChange {
29 pub file_id: FileId, 35 source_file_edits: FxHashMap::from_iter(iter::once((file_id, edit))),
30 pub edit: TextEdit, 36 ..Default::default()
37 }
38 }
39
40 pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) {
41 match self.source_file_edits.entry(file_id) {
42 Entry::Occupied(mut entry) => {
43 assert_never!(
44 entry.get_mut().union(edit).is_err(),
45 "overlapping edits for same file"
46 );
47 }
48 Entry::Vacant(entry) => {
49 entry.insert(edit);
50 }
51 }
52 }
53
54 pub fn push_file_system_edit(&mut self, edit: FileSystemEdit) {
55 self.file_system_edits.push(edit);
56 }
57
58 pub fn get_source_edit(&self, file_id: FileId) -> Option<&TextEdit> {
59 self.source_file_edits.get(&file_id)
60 }
31} 61}
32 62
33impl From<SourceFileEdit> for SourceChange { 63impl Extend<(FileId, TextEdit)> for SourceChange {
34 fn from(edit: SourceFileEdit) -> SourceChange { 64 fn extend<T: IntoIterator<Item = (FileId, TextEdit)>>(&mut self, iter: T) {
35 vec![edit].into() 65 iter.into_iter().for_each(|(file_id, edit)| self.insert_source_edit(file_id, edit));
36 } 66 }
37} 67}
38 68
39impl From<Vec<SourceFileEdit>> for SourceChange { 69impl From<FxHashMap<FileId, TextEdit>> for SourceChange {
40 fn from(source_file_edits: Vec<SourceFileEdit>) -> SourceChange { 70 fn from(source_file_edits: FxHashMap<FileId, TextEdit>) -> SourceChange {
41 SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } 71 SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
42 } 72 }
43} 73}
@@ -51,7 +81,7 @@ pub enum FileSystemEdit {
51impl From<FileSystemEdit> for SourceChange { 81impl From<FileSystemEdit> for SourceChange {
52 fn from(edit: FileSystemEdit) -> SourceChange { 82 fn from(edit: FileSystemEdit) -> SourceChange {
53 SourceChange { 83 SourceChange {
54 source_file_edits: Vec::new(), 84 source_file_edits: Default::default(),
55 file_system_edits: vec![edit], 85 file_system_edits: vec![edit],
56 is_snippet: false, 86 is_snippet: false,
57 } 87 }
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
index 09c7ac3ec..84bb25505 100644
--- a/crates/ide_db/src/traits/tests.rs
+++ b/crates/ide_db/src/traits/tests.rs
@@ -5,12 +5,12 @@ use hir::Semantics;
5use syntax::ast::{self, AstNode}; 5use syntax::ast::{self, AstNode};
6use test_utils::RangeOrOffset; 6use test_utils::RangeOrOffset;
7 7
8/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 8/// Creates analysis from a multi-file fixture, returns positions marked with $0.
9pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 9pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let change_fixture = ChangeFixture::parse(ra_fixture); 10 let change_fixture = ChangeFixture::parse(ra_fixture);
11 let mut database = RootDatabase::default(); 11 let mut database = RootDatabase::default();
12 database.apply_change(change_fixture.change); 12 database.apply_change(change_fixture.change);
13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
14 let offset = match range_or_offset { 14 let offset = match range_or_offset {
15 RangeOrOffset::Range(_) => panic!(), 15 RangeOrOffset::Range(_) => panic!(),
16 RangeOrOffset::Offset(it) => it, 16 RangeOrOffset::Offset(it) => it,
@@ -55,7 +55,7 @@ pub trait Foo {
55 fn bar(); 55 fn bar();
56} 56}
57impl Foo for u8 { 57impl Foo for u8 {
58 <|> 58 $0
59} 59}
60 "#, 60 "#,
61 expect![["Foo"]], 61 expect![["Foo"]],
@@ -68,7 +68,7 @@ pub trait Foo {
68impl Foo for u8 { 68impl Foo for u8 {
69 fn bar() { 69 fn bar() {
70 fn baz() { 70 fn baz() {
71 <|> 71 $0
72 } 72 }
73 baz(); 73 baz();
74 } 74 }
@@ -83,7 +83,7 @@ pub trait Foo {
83} 83}
84pub struct Bar; 84pub struct Bar;
85impl Bar { 85impl Bar {
86 <|> 86 $0
87} 87}
88 "#, 88 "#,
89 expect![[""]], 89 expect![[""]],
@@ -99,7 +99,7 @@ pub trait Foo {
99 fn bar(); 99 fn bar();
100} 100}
101impl Foo for u8 { 101impl Foo for u8 {
102 <|> 102 $0
103}"#, 103}"#,
104 expect![[r#" 104 expect![[r#"
105 FOO 105 FOO
@@ -114,7 +114,7 @@ pub trait Foo {
114} 114}
115impl Foo for u8 { 115impl Foo for u8 {
116 const FOO: u8 = 10; 116 const FOO: u8 = 10;
117 <|> 117 $0
118}"#, 118}"#,
119 expect![[r#" 119 expect![[r#"
120 bar"#]], 120 bar"#]],
@@ -128,7 +128,7 @@ pub trait Foo {
128} 128}
129impl Foo for u8 { 129impl Foo for u8 {
130 const FOO: u8 = 10; 130 const FOO: u8 = 10;
131 fn bar() {<|>} 131 fn bar() {$0}
132}"#, 132}"#,
133 expect![[r#""#]], 133 expect![[r#""#]],
134 ); 134 );
@@ -137,7 +137,7 @@ impl Foo for u8 {
137 r#" 137 r#"
138pub struct Foo; 138pub struct Foo;
139impl Foo { 139impl Foo {
140 fn bar() {<|>} 140 fn bar() {$0}
141}"#, 141}"#,
142 expect![[r#""#]], 142 expect![[r#""#]],
143 ); 143 );