diff options
author | Lukas Wirth <[email protected]> | 2021-01-12 14:51:02 +0000 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-01-12 14:51:02 +0000 |
commit | 2c1777a2e264e58fccd5ace94b238c8a497ddbda (patch) | |
tree | e7d47c95c6bcdeecd5f321f4ca969d04ca90dff7 /crates | |
parent | fbdb32adfc49e0d69b7fd8e44135bea59382d2cb (diff) |
Ensure uniqueness of file ids in reference search via hashmap
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 6 | ||||
-rw-r--r-- | crates/assists/src/handlers/inline_local_variable.rs | 14 | ||||
-rw-r--r-- | crates/assists/src/handlers/remove_unused_param.rs | 18 | ||||
-rw-r--r-- | crates/ide/src/call_hierarchy.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 97 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 20 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 48 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 24 | ||||
-rw-r--r-- | crates/ssr/src/search.rs | 12 |
9 files changed, 122 insertions, 121 deletions
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 21b13977b..e3ef04932 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -8,7 +8,7 @@ use ide_db::{ | |||
8 | insert_use::{insert_use, ImportScope}, | 8 | insert_use::{insert_use, ImportScope}, |
9 | mod_path_to_ast, | 9 | mod_path_to_ast, |
10 | }, | 10 | }, |
11 | search::{FileReference, FileReferences}, | 11 | search::FileReference, |
12 | RootDatabase, | 12 | RootDatabase, |
13 | }; | 13 | }; |
14 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
@@ -63,10 +63,10 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
63 | let current_module = enum_hir.module(ctx.db()); | 63 | let current_module = enum_hir.module(ctx.db()); |
64 | visited_modules_set.insert(current_module); | 64 | visited_modules_set.insert(current_module); |
65 | let mut def_rewriter = None; | 65 | let mut def_rewriter = None; |
66 | for FileReferences { file_id, references: refs } in usages { | 66 | for (file_id, references) in usages { |
67 | let mut rewriter = SyntaxRewriter::default(); | 67 | let mut rewriter = SyntaxRewriter::default(); |
68 | let source_file = ctx.sema.parse(file_id); | 68 | let source_file = ctx.sema.parse(file_id); |
69 | for reference in refs { | 69 | for reference in references { |
70 | update_reference( | 70 | update_reference( |
71 | ctx, | 71 | ctx, |
72 | &mut rewriter, | 72 | &mut rewriter, |
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs index 928df6825..dc798daaa 100644 --- a/crates/assists/src/handlers/inline_local_variable.rs +++ b/crates/assists/src/handlers/inline_local_variable.rs | |||
@@ -47,8 +47,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
47 | 47 | ||
48 | let def = ctx.sema.to_def(&bind_pat)?; | 48 | let def = ctx.sema.to_def(&bind_pat)?; |
49 | let def = Definition::Local(def); | 49 | let def = Definition::Local(def); |
50 | let refs = def.usages(&ctx.sema).all(); | 50 | let usages = def.usages(&ctx.sema).all(); |
51 | if refs.is_empty() { | 51 | if usages.is_empty() { |
52 | mark::hit!(test_not_applicable_if_variable_unused); | 52 | mark::hit!(test_not_applicable_if_variable_unused); |
53 | return None; | 53 | return None; |
54 | }; | 54 | }; |
@@ -66,9 +66,10 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
66 | let_stmt.syntax().text_range() | 66 | let_stmt.syntax().text_range() |
67 | }; | 67 | }; |
68 | 68 | ||
69 | let wrap_in_parens = refs | 69 | let wrap_in_parens = usages |
70 | .iter() | 70 | .references |
71 | .flat_map(|refs| &refs.references) | 71 | .values() |
72 | .flatten() | ||
72 | .map(|&FileReference { range, .. }| { | 73 | .map(|&FileReference { range, .. }| { |
73 | let usage_node = | 74 | let usage_node = |
74 | ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?; | 75 | ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?; |
@@ -115,8 +116,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
115 | target, | 116 | target, |
116 | move |builder| { | 117 | move |builder| { |
117 | builder.delete(delete_range); | 118 | builder.delete(delete_range); |
118 | for (reference, should_wrap) in | 119 | for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens) |
119 | refs.iter().flat_map(|refs| &refs.references).zip(wrap_in_parens) | ||
120 | { | 120 | { |
121 | let replacement = | 121 | let replacement = |
122 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | 122 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs index 4f3b8ac46..c961680e2 100644 --- a/crates/assists/src/handlers/remove_unused_param.rs +++ b/crates/assists/src/handlers/remove_unused_param.rs | |||
@@ -1,7 +1,4 @@ | |||
1 | use ide_db::{ | 1 | use ide_db::{base_db::FileId, defs::Definition, search::FileReference}; |
2 | defs::Definition, | ||
3 | search::{FileReference, FileReferences}, | ||
4 | }; | ||
5 | use syntax::{ | 2 | use syntax::{ |
6 | algo::find_node_at_range, | 3 | algo::find_node_at_range, |
7 | ast::{self, ArgListOwner}, | 4 | ast::{self, ArgListOwner}, |
@@ -61,8 +58,8 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
61 | param.syntax().text_range(), | 58 | param.syntax().text_range(), |
62 | |builder| { | 59 | |builder| { |
63 | builder.delete(range_to_remove(param.syntax())); | 60 | builder.delete(range_to_remove(param.syntax())); |
64 | for usages in fn_def.usages(&ctx.sema).all() { | 61 | for (file_id, references) in fn_def.usages(&ctx.sema).all() { |
65 | process_usages(ctx, builder, usages, param_position); | 62 | process_usages(ctx, builder, file_id, references, param_position); |
66 | } | 63 | } |
67 | }, | 64 | }, |
68 | ) | 65 | ) |
@@ -71,12 +68,13 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
71 | fn process_usages( | 68 | fn process_usages( |
72 | ctx: &AssistContext, | 69 | ctx: &AssistContext, |
73 | builder: &mut AssistBuilder, | 70 | builder: &mut AssistBuilder, |
74 | usages: FileReferences, | 71 | file_id: FileId, |
72 | references: Vec<FileReference>, | ||
75 | arg_to_remove: usize, | 73 | arg_to_remove: usize, |
76 | ) { | 74 | ) { |
77 | let source_file = ctx.sema.parse(usages.file_id); | 75 | let source_file = ctx.sema.parse(file_id); |
78 | builder.edit_file(usages.file_id); | 76 | builder.edit_file(file_id); |
79 | for usage in usages.references { | 77 | for usage in references { |
80 | if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) { | 78 | if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) { |
81 | builder.delete(text_range); | 79 | builder.delete(text_range); |
82 | } | 80 | } |
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 90d3b9a31..e8999a7f3 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -3,8 +3,8 @@ | |||
3 | use indexmap::IndexMap; | 3 | use indexmap::IndexMap; |
4 | 4 | ||
5 | use hir::Semantics; | 5 | use hir::Semantics; |
6 | use ide_db::call_info::FnCallNode; | ||
6 | use ide_db::RootDatabase; | 7 | use ide_db::RootDatabase; |
7 | use ide_db::{call_info::FnCallNode, search::FileReferences}; | ||
8 | use syntax::{ast, AstNode, TextRange}; | 8 | use syntax::{ast, AstNode, TextRange}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
@@ -47,7 +47,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
47 | 47 | ||
48 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
49 | 49 | ||
50 | for &FileReferences { file_id, ref references } in refs.info.references() { | 50 | for (&file_id, references) in refs.info.references().iter() { |
51 | let file = sema.parse(file_id); | 51 | let file = sema.parse(file_id); |
52 | let file = file.syntax(); | 52 | let file = file.syntax(); |
53 | for reference in references { | 53 | for reference in references { |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 132680bfb..c7943dc95 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -13,6 +13,7 @@ pub(crate) mod rename; | |||
13 | 13 | ||
14 | use hir::Semantics; | 14 | use hir::Semantics; |
15 | use ide_db::{ | 15 | use ide_db::{ |
16 | base_db::FileId, | ||
16 | defs::{Definition, NameClass, NameRefClass}, | 17 | defs::{Definition, NameClass, NameRefClass}, |
17 | search::{FileReference, FileReferences, ReferenceAccess, ReferenceKind, SearchScope}, | 18 | search::{FileReference, FileReferences, ReferenceAccess, ReferenceKind, SearchScope}, |
18 | RootDatabase, | 19 | RootDatabase, |
@@ -28,7 +29,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI | |||
28 | #[derive(Debug, Clone)] | 29 | #[derive(Debug, Clone)] |
29 | pub struct ReferenceSearchResult { | 30 | pub struct ReferenceSearchResult { |
30 | declaration: Declaration, | 31 | declaration: Declaration, |
31 | references: Vec<FileReferences>, | 32 | references: FileReferences, |
32 | } | 33 | } |
33 | 34 | ||
34 | #[derive(Debug, Clone)] | 35 | #[derive(Debug, Clone)] |
@@ -47,10 +48,21 @@ impl ReferenceSearchResult { | |||
47 | &self.declaration.nav | 48 | &self.declaration.nav |
48 | } | 49 | } |
49 | 50 | ||
50 | pub fn references(&self) -> &[FileReferences] { | 51 | pub fn references(&self) -> &FileReferences { |
51 | &self.references | 52 | &self.references |
52 | } | 53 | } |
53 | 54 | ||
55 | pub fn references_with_declaration(mut self) -> FileReferences { | ||
56 | let decl_ref = FileReference { | ||
57 | range: self.declaration.nav.focus_or_full_range(), | ||
58 | kind: self.declaration.kind, | ||
59 | access: self.declaration.access, | ||
60 | }; | ||
61 | let file_id = self.declaration.nav.file_id; | ||
62 | self.references.references.entry(file_id).or_default().push(decl_ref); | ||
63 | self.references | ||
64 | } | ||
65 | |||
54 | /// Total number of references | 66 | /// Total number of references |
55 | /// At least 1 since all valid references should | 67 | /// At least 1 since all valid references should |
56 | /// Have a declaration | 68 | /// Have a declaration |
@@ -62,23 +74,11 @@ impl ReferenceSearchResult { | |||
62 | // allow turning ReferenceSearchResult into an iterator | 74 | // allow turning ReferenceSearchResult into an iterator |
63 | // over References | 75 | // over References |
64 | impl IntoIterator for ReferenceSearchResult { | 76 | impl IntoIterator for ReferenceSearchResult { |
65 | type Item = FileReferences; | 77 | type Item = (FileId, Vec<FileReference>); |
66 | type IntoIter = std::vec::IntoIter<FileReferences>; | 78 | type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>; |
67 | 79 | ||
68 | fn into_iter(mut self) -> Self::IntoIter { | 80 | fn into_iter(self) -> Self::IntoIter { |
69 | let mut v = Vec::with_capacity(self.len()); | 81 | self.references_with_declaration().into_iter() |
70 | v.append(&mut self.references); | ||
71 | let decl_ref = FileReference { | ||
72 | range: self.declaration.nav.focus_or_full_range(), | ||
73 | kind: self.declaration.kind, | ||
74 | access: self.declaration.access, | ||
75 | }; | ||
76 | let file_id = self.declaration.nav.file_id; | ||
77 | match v.iter_mut().find(|it| it.file_id == file_id) { | ||
78 | Some(file_refs) => file_refs.references.push(decl_ref), | ||
79 | None => v.push(FileReferences { file_id, references: vec![decl_ref] }), | ||
80 | } | ||
81 | v.into_iter() | ||
82 | } | 82 | } |
83 | } | 83 | } |
84 | 84 | ||
@@ -110,11 +110,12 @@ pub(crate) fn find_all_refs( | |||
110 | 110 | ||
111 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; | 111 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; |
112 | 112 | ||
113 | let mut references = def.usages(sema).set_scope(search_scope).all(); | 113 | let mut usages = def.usages(sema).set_scope(search_scope).all(); |
114 | references.iter_mut().for_each(|it| { | 114 | usages |
115 | it.references.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | 115 | .references |
116 | }); | 116 | .values_mut() |
117 | references.retain(|r| !r.references.is_empty()); | 117 | .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); |
118 | usages.references.retain(|_, it| !it.is_empty()); | ||
118 | 119 | ||
119 | let nav = def.try_to_nav(sema.db)?; | 120 | let nav = def.try_to_nav(sema.db)?; |
120 | let decl_range = nav.focus_or_full_range(); | 121 | let decl_range = nav.focus_or_full_range(); |
@@ -138,7 +139,7 @@ pub(crate) fn find_all_refs( | |||
138 | 139 | ||
139 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | 140 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; |
140 | 141 | ||
141 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) | 142 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages })) |
142 | } | 143 | } |
143 | 144 | ||
144 | fn find_name( | 145 | fn find_name( |
@@ -292,32 +293,30 @@ fn try_find_self_references( | |||
292 | ReferenceAccess::Read | 293 | ReferenceAccess::Read |
293 | }), | 294 | }), |
294 | }; | 295 | }; |
295 | let references = function | 296 | let refs = function |
296 | .body() | 297 | .body() |
297 | .map(|body| { | 298 | .map(|body| { |
298 | FileReferences { | 299 | body.syntax() |
299 | file_id, | 300 | .descendants() |
300 | references: body | 301 | .filter_map(ast::PathExpr::cast) |
301 | .syntax() | 302 | .filter_map(|expr| { |
302 | .descendants() | 303 | let path = expr.path()?; |
303 | .filter_map(ast::PathExpr::cast) | 304 | if path.qualifier().is_none() { |
304 | .filter_map(|expr| { | 305 | path.segment()?.self_token() |
305 | let path = expr.path()?; | 306 | } else { |
306 | if path.qualifier().is_none() { | 307 | None |
307 | path.segment()?.self_token() | 308 | } |
308 | } else { | 309 | }) |
309 | None | 310 | .map(|token| FileReference { |
310 | } | 311 | range: token.text_range(), |
311 | }) | 312 | kind: ReferenceKind::SelfKw, |
312 | .map(|token| FileReference { | 313 | access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration |
313 | range: token.text_range(), | 314 | }) |
314 | kind: ReferenceKind::SelfKw, | 315 | .collect() |
315 | access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration | ||
316 | }) | ||
317 | .collect(), | ||
318 | } | ||
319 | }) | 316 | }) |
320 | .map_or_else(Vec::default, |it| vec![it]); | 317 | .unwrap_or_default(); |
318 | let mut references = FileReferences::default(); | ||
319 | references.references.insert(file_id, refs); | ||
321 | 320 | ||
322 | Some(RangeInfo::new( | 321 | Some(RangeInfo::new( |
323 | param_self_token.text_range(), | 322 | param_self_token.text_range(), |
@@ -328,7 +327,7 @@ fn try_find_self_references( | |||
328 | #[cfg(test)] | 327 | #[cfg(test)] |
329 | mod tests { | 328 | mod tests { |
330 | use expect_test::{expect, Expect}; | 329 | use expect_test::{expect, Expect}; |
331 | use ide_db::{base_db::FileId, search::FileReferences}; | 330 | use ide_db::base_db::FileId; |
332 | use stdx::format_to; | 331 | use stdx::format_to; |
333 | 332 | ||
334 | use crate::{fixture, SearchScope}; | 333 | use crate::{fixture, SearchScope}; |
@@ -1022,7 +1021,7 @@ impl Foo { | |||
1022 | actual += "\n\n"; | 1021 | actual += "\n\n"; |
1023 | } | 1022 | } |
1024 | 1023 | ||
1025 | for FileReferences { file_id, references } in refs.references { | 1024 | for (file_id, references) in refs.references { |
1026 | for r in references { | 1025 | for r in references { |
1027 | format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); | 1026 | format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); |
1028 | if let Some(access) = r.access { | 1027 | if let Some(access) = r.access { |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index dd08e1c32..5207388b5 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -9,7 +9,7 @@ use hir::{Module, ModuleDef, ModuleSource, Semantics}; | |||
9 | use ide_db::{ | 9 | use ide_db::{ |
10 | base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}, | 10 | base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt}, |
11 | defs::{Definition, NameClass, NameRefClass}, | 11 | defs::{Definition, NameClass, NameRefClass}, |
12 | search::FileReferences, | 12 | search::FileReference, |
13 | RootDatabase, | 13 | RootDatabase, |
14 | }; | 14 | }; |
15 | use syntax::{ | 15 | use syntax::{ |
@@ -176,7 +176,8 @@ fn find_all_refs( | |||
176 | 176 | ||
177 | fn source_edit_from_references( | 177 | fn source_edit_from_references( |
178 | sema: &Semantics<RootDatabase>, | 178 | sema: &Semantics<RootDatabase>, |
179 | &FileReferences { file_id, ref references }: &FileReferences, | 179 | file_id: FileId, |
180 | references: &[FileReference], | ||
180 | new_name: &str, | 181 | new_name: &str, |
181 | ) -> SourceFileEdit { | 182 | ) -> SourceFileEdit { |
182 | let mut edit = TextEdit::builder(); | 183 | let mut edit = TextEdit::builder(); |
@@ -283,10 +284,9 @@ fn rename_mod( | |||
283 | } | 284 | } |
284 | 285 | ||
285 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; | 286 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; |
286 | let ref_edits = refs | 287 | let ref_edits = refs.references().iter().map(|(&file_id, references)| { |
287 | .references() | 288 | source_edit_from_references(sema, file_id, references, new_name) |
288 | .iter() | 289 | }); |
289 | .map(|reference| source_edit_from_references(sema, reference, new_name)); | ||
290 | source_file_edits.extend(ref_edits); | 290 | source_file_edits.extend(ref_edits); |
291 | 291 | ||
292 | Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) | 292 | Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) |
@@ -341,7 +341,9 @@ fn rename_to_self( | |||
341 | let mut edits = refs | 341 | let mut edits = refs |
342 | .references() | 342 | .references() |
343 | .iter() | 343 | .iter() |
344 | .map(|reference| source_edit_from_references(sema, reference, "self")) | 344 | .map(|(&file_id, references)| { |
345 | source_edit_from_references(sema, file_id, references, "self") | ||
346 | }) | ||
345 | .collect::<Vec<_>>(); | 347 | .collect::<Vec<_>>(); |
346 | 348 | ||
347 | edits.push(SourceFileEdit { | 349 | edits.push(SourceFileEdit { |
@@ -467,7 +469,9 @@ fn rename_reference( | |||
467 | 469 | ||
468 | let edit = refs | 470 | let edit = refs |
469 | .into_iter() | 471 | .into_iter() |
470 | .map(|reference| source_edit_from_references(sema, &reference, new_name)) | 472 | .map(|(file_id, references)| { |
473 | source_edit_from_references(sema, file_id, &references, new_name) | ||
474 | }) | ||
471 | .collect::<Vec<_>>(); | 475 | .collect::<Vec<_>>(); |
472 | 476 | ||
473 | Ok(RangeInfo::new(range, SourceChange::from(edit))) | 477 | Ok(RangeInfo::new(range, SourceChange::from(edit))) |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index b8359a9b4..89a313e9b 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -8,7 +8,6 @@ use std::{convert::TryInto, mem}; | |||
8 | 8 | ||
9 | use base_db::{FileId, FileRange, SourceDatabaseExt}; | 9 | use base_db::{FileId, FileRange, SourceDatabaseExt}; |
10 | use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; | 10 | use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; |
11 | use itertools::Itertools; | ||
12 | use once_cell::unsync::Lazy; | 11 | use once_cell::unsync::Lazy; |
13 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
14 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; | 13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; |
@@ -19,17 +18,37 @@ use crate::{ | |||
19 | RootDatabase, | 18 | RootDatabase, |
20 | }; | 19 | }; |
21 | 20 | ||
22 | #[derive(Debug, Clone)] | 21 | #[derive(Debug, Default, Clone)] |
23 | pub struct FileReferences { | 22 | pub struct FileReferences { |
24 | pub file_id: FileId, | 23 | pub references: FxHashMap<FileId, Vec<FileReference>>, |
25 | pub references: Vec<FileReference>, | ||
26 | } | 24 | } |
27 | 25 | ||
28 | impl FileReferences { | 26 | impl FileReferences { |
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 | |||
29 | pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ { | 39 | pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ { |
30 | self.references | 40 | self.references.iter().flat_map(|(&file_id, refs)| { |
31 | .iter() | 41 | refs.iter().map(move |&FileReference { range, .. }| FileRange { file_id, range }) |
32 | .map(move |&FileReference { range, .. }| FileRange { file_id: self.file_id, range }) | 42 | }) |
43 | } | ||
44 | } | ||
45 | |||
46 | impl IntoIterator for FileReferences { | ||
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() | ||
33 | } | 52 | } |
34 | } | 53 | } |
35 | 54 | ||
@@ -275,21 +294,12 @@ impl<'a> FindUsages<'a> { | |||
275 | } | 294 | } |
276 | 295 | ||
277 | /// The [`FileReferences`] returned always have unique [`FileId`]s. | 296 | /// The [`FileReferences`] returned always have unique [`FileId`]s. |
278 | pub fn all(self) -> Vec<FileReferences> { | 297 | pub fn all(self) -> FileReferences { |
279 | let mut res = <Vec<FileReferences>>::new(); | 298 | let mut res = FileReferences::default(); |
280 | self.search(&mut |file_id, reference| { | 299 | self.search(&mut |file_id, reference| { |
281 | match res.iter_mut().find(|it| it.file_id == file_id) { | 300 | res.references.entry(file_id).or_default().push(reference); |
282 | Some(file_refs) => file_refs.references.push(reference), | ||
283 | _ => res.push(FileReferences { file_id, references: vec![reference] }), | ||
284 | } | ||
285 | false | 301 | false |
286 | }); | 302 | }); |
287 | assert!(res | ||
288 | .iter() | ||
289 | .map(|refs| refs.file_id) | ||
290 | .sorted_unstable() | ||
291 | .tuple_windows::<(_, _)>() | ||
292 | .all(|(a, b)| a < b)); | ||
293 | res | 303 | res |
294 | } | 304 | } |
295 | 305 | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index d862f370a..2cc57f022 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -12,7 +12,6 @@ use ide::{ | |||
12 | FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget, | 12 | FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget, |
13 | Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, SymbolKind, TextEdit, | 13 | Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, SymbolKind, TextEdit, |
14 | }; | 14 | }; |
15 | use ide_db::search::FileReferences; | ||
16 | use itertools::Itertools; | 15 | use itertools::Itertools; |
17 | use lsp_server::ErrorCode; | 16 | use lsp_server::ErrorCode; |
18 | use lsp_types::{ | 17 | use lsp_types::{ |
@@ -813,18 +812,14 @@ pub(crate) fn handle_references( | |||
813 | }; | 812 | }; |
814 | 813 | ||
815 | let locations = if params.context.include_declaration { | 814 | let locations = if params.context.include_declaration { |
816 | let mut locations = Vec::default(); | 815 | refs.references_with_declaration() |
817 | refs.into_iter().for_each(|it| { | 816 | .file_ranges() |
818 | locations.extend( | 817 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) |
819 | it.file_ranges().filter_map(|frange| to_proto::location(&snap, frange).ok()), | 818 | .collect() |
820 | ) | ||
821 | }); | ||
822 | locations | ||
823 | } else { | 819 | } else { |
824 | // Only iterate over the references if include_declaration was false | 820 | // Only iterate over the references if include_declaration was false |
825 | refs.references() | 821 | refs.references() |
826 | .iter() | 822 | .file_ranges() |
827 | .flat_map(FileReferences::file_ranges) | ||
828 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) | 823 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) |
829 | .collect() | 824 | .collect() |
830 | }; | 825 | }; |
@@ -1181,8 +1176,7 @@ pub(crate) fn handle_code_lens_resolve( | |||
1181 | .unwrap_or(None) | 1176 | .unwrap_or(None) |
1182 | .map(|r| { | 1177 | .map(|r| { |
1183 | r.references() | 1178 | r.references() |
1184 | .iter() | 1179 | .file_ranges() |
1185 | .flat_map(FileReferences::file_ranges) | ||
1186 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) | 1180 | .filter_map(|frange| to_proto::location(&snap, frange).ok()) |
1187 | .collect_vec() | 1181 | .collect_vec() |
1188 | }) | 1182 | }) |
@@ -1227,11 +1221,11 @@ pub(crate) fn handle_document_highlight( | |||
1227 | }; | 1221 | }; |
1228 | 1222 | ||
1229 | let res = refs | 1223 | let res = refs |
1230 | .into_iter() | 1224 | .references_with_declaration() |
1231 | .find(|refs| refs.file_id == position.file_id) | 1225 | .references |
1226 | .get(&position.file_id) | ||
1232 | .map(|file_refs| { | 1227 | .map(|file_refs| { |
1233 | file_refs | 1228 | file_refs |
1234 | .references | ||
1235 | .into_iter() | 1229 | .into_iter() |
1236 | .map(|r| DocumentHighlight { | 1230 | .map(|r| DocumentHighlight { |
1237 | range: to_proto::range(&line_index, r.range), | 1231 | range: to_proto::range(&line_index, r.range), |
diff --git a/crates/ssr/src/search.rs b/crates/ssr/src/search.rs index a1d653aff..a3eb2e800 100644 --- a/crates/ssr/src/search.rs +++ b/crates/ssr/src/search.rs | |||
@@ -20,7 +20,7 @@ use test_utils::mark; | |||
20 | /// them more than once. | 20 | /// them more than once. |
21 | #[derive(Default)] | 21 | #[derive(Default)] |
22 | pub(crate) struct UsageCache { | 22 | pub(crate) struct UsageCache { |
23 | usages: Vec<(Definition, Vec<FileReferences>)>, | 23 | usages: Vec<(Definition, FileReferences)>, |
24 | } | 24 | } |
25 | 25 | ||
26 | impl<'db> MatchFinder<'db> { | 26 | impl<'db> MatchFinder<'db> { |
@@ -58,11 +58,7 @@ impl<'db> MatchFinder<'db> { | |||
58 | ) { | 58 | ) { |
59 | if let Some(resolved_path) = pick_path_for_usages(pattern) { | 59 | if let Some(resolved_path) = pick_path_for_usages(pattern) { |
60 | let definition: Definition = resolved_path.resolution.clone().into(); | 60 | let definition: Definition = resolved_path.resolution.clone().into(); |
61 | for file_range in self | 61 | for file_range in self.find_usages(usage_cache, definition).file_ranges() { |
62 | .find_usages(usage_cache, definition) | ||
63 | .iter() | ||
64 | .flat_map(FileReferences::file_ranges) | ||
65 | { | ||
66 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { | 62 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { |
67 | if !is_search_permitted_ancestors(&node_to_match) { | 63 | if !is_search_permitted_ancestors(&node_to_match) { |
68 | mark::hit!(use_declaration_with_braces); | 64 | mark::hit!(use_declaration_with_braces); |
@@ -112,7 +108,7 @@ impl<'db> MatchFinder<'db> { | |||
112 | &self, | 108 | &self, |
113 | usage_cache: &'a mut UsageCache, | 109 | usage_cache: &'a mut UsageCache, |
114 | definition: Definition, | 110 | definition: Definition, |
115 | ) -> &'a [FileReferences] { | 111 | ) -> &'a FileReferences { |
116 | // Logically if a lookup succeeds we should just return it. Unfortunately returning it would | 112 | // Logically if a lookup succeeds we should just return it. Unfortunately returning it would |
117 | // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a | 113 | // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a |
118 | // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two | 114 | // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two |
@@ -254,7 +250,7 @@ fn is_search_permitted(node: &SyntaxNode) -> bool { | |||
254 | } | 250 | } |
255 | 251 | ||
256 | impl UsageCache { | 252 | impl UsageCache { |
257 | fn find(&mut self, definition: &Definition) -> Option<&[FileReferences]> { | 253 | fn find(&mut self, definition: &Definition) -> Option<&FileReferences> { |
258 | // We expect a very small number of cache entries (generally 1), so a linear scan should be | 254 | // We expect a very small number of cache entries (generally 1), so a linear scan should be |
259 | // fast enough and avoids the need to implement Hash for Definition. | 255 | // fast enough and avoids the need to implement Hash for Definition. |
260 | for (d, refs) in &self.usages { | 256 | for (d, refs) in &self.usages { |