aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs59
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs99
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs41
-rw-r--r--crates/assists/src/handlers/reorder_impl.rs201
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs35
-rw-r--r--crates/ide/src/call_hierarchy.rs29
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide/src/references.rs79
-rw-r--r--crates/ide/src/references/rename.rs103
-rw-r--r--crates/ide/src/syntax_highlighting/highlights.rs14
-rw-r--r--crates/ide/src/syntax_highlighting/injector.rs4
-rw-r--r--crates/ide_db/src/search.rs110
-rw-r--r--crates/rust-analyzer/src/caps.rs24
-rw-r--r--crates/rust-analyzer/src/handlers.rs69
-rw-r--r--crates/rust-analyzer/src/to_proto.rs7
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs144
-rw-r--r--crates/ssr/src/search.rs20
18 files changed, 748 insertions, 294 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 40028fc01..e3ef04932 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -2,12 +2,16 @@ use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant}; 4use hir::{AsName, Module, ModuleDef, Name, Variant};
5use ide_db::helpers::{ 5use ide_db::{
6 insert_use::{insert_use, ImportScope}, 6 defs::Definition,
7 mod_path_to_ast, 7 helpers::{
8 insert_use::{insert_use, ImportScope},
9 mod_path_to_ast,
10 },
11 search::FileReference,
12 RootDatabase,
8}; 13};
9use ide_db::{defs::Definition, search::Reference, RootDatabase}; 14use rustc_hash::FxHashSet;
10use rustc_hash::{FxHashMap, FxHashSet};
11use syntax::{ 15use syntax::{
12 algo::{find_node_at_offset, SyntaxRewriter}, 16 algo::{find_node_at_offset, SyntaxRewriter},
13 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, 17 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner},
@@ -58,29 +62,29 @@ pub(crate) fn extract_struct_from_enum_variant(
58 let mut visited_modules_set = FxHashSet::default(); 62 let mut visited_modules_set = FxHashSet::default();
59 let current_module = enum_hir.module(ctx.db()); 63 let current_module = enum_hir.module(ctx.db());
60 visited_modules_set.insert(current_module); 64 visited_modules_set.insert(current_module);
61 let mut rewriters = FxHashMap::default(); 65 let mut def_rewriter = None;
62 for reference in usages { 66 for (file_id, references) in usages {
63 let rewriter = rewriters 67 let mut rewriter = SyntaxRewriter::default();
64 .entry(reference.file_range.file_id) 68 let source_file = ctx.sema.parse(file_id);
65 .or_insert_with(SyntaxRewriter::default); 69 for reference in references {
66 let source_file = ctx.sema.parse(reference.file_range.file_id); 70 update_reference(
67 update_reference( 71 ctx,
68 ctx, 72 &mut rewriter,
69 rewriter, 73 reference,
70 reference, 74 &source_file,
71 &source_file, 75 &enum_module_def,
72 &enum_module_def, 76 &variant_hir_name,
73 &variant_hir_name, 77 &mut visited_modules_set,
74 &mut visited_modules_set, 78 );
75 ); 79 }
76 } 80 if file_id == ctx.frange.file_id {
77 let mut rewriter = 81 def_rewriter = Some(rewriter);
78 rewriters.remove(&ctx.frange.file_id).unwrap_or_else(SyntaxRewriter::default); 82 continue;
79 for (file_id, rewriter) in rewriters { 83 }
80 builder.edit_file(file_id); 84 builder.edit_file(file_id);
81 builder.rewrite(rewriter); 85 builder.rewrite(rewriter);
82 } 86 }
83 builder.edit_file(ctx.frange.file_id); 87 let mut rewriter = def_rewriter.unwrap_or_default();
84 update_variant(&mut rewriter, &variant); 88 update_variant(&mut rewriter, &variant);
85 extract_struct_def( 89 extract_struct_def(
86 &mut rewriter, 90 &mut rewriter,
@@ -90,6 +94,7 @@ pub(crate) fn extract_struct_from_enum_variant(
90 &variant.parent_enum().syntax().clone().into(), 94 &variant.parent_enum().syntax().clone().into(),
91 enum_ast.visibility(), 95 enum_ast.visibility(),
92 ); 96 );
97 builder.edit_file(ctx.frange.file_id);
93 builder.rewrite(rewriter); 98 builder.rewrite(rewriter);
94 }, 99 },
95 ) 100 )
@@ -205,13 +210,13 @@ fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Opti
205fn update_reference( 210fn update_reference(
206 ctx: &AssistContext, 211 ctx: &AssistContext,
207 rewriter: &mut SyntaxRewriter, 212 rewriter: &mut SyntaxRewriter,
208 reference: Reference, 213 reference: FileReference,
209 source_file: &SourceFile, 214 source_file: &SourceFile,
210 enum_module_def: &ModuleDef, 215 enum_module_def: &ModuleDef,
211 variant_hir_name: &Name, 216 variant_hir_name: &Name,
212 visited_modules_set: &mut FxHashSet<Module>, 217 visited_modules_set: &mut FxHashSet<Module>,
213) -> Option<()> { 218) -> Option<()> {
214 let offset = reference.file_range.range.start(); 219 let offset = reference.range.start();
215 let (segment, expr) = if let Some(path_expr) = 220 let (segment, expr) = if let Some(path_expr) =
216 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) 221 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset)
217 { 222 {
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index d559be9cb..dc798daaa 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -1,4 +1,7 @@
1use ide_db::{defs::Definition, search::ReferenceKind}; 1use ide_db::{
2 defs::Definition,
3 search::{FileReference, ReferenceKind},
4};
2use syntax::{ 5use syntax::{
3 ast::{self, AstNode, AstToken}, 6 ast::{self, AstNode, AstToken},
4 TextRange, 7 TextRange,
@@ -44,8 +47,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
44 47
45 let def = ctx.sema.to_def(&bind_pat)?; 48 let def = ctx.sema.to_def(&bind_pat)?;
46 let def = Definition::Local(def); 49 let def = Definition::Local(def);
47 let refs = def.usages(&ctx.sema).all(); 50 let usages = def.usages(&ctx.sema).all();
48 if refs.is_empty() { 51 if usages.is_empty() {
49 mark::hit!(test_not_applicable_if_variable_unused); 52 mark::hit!(test_not_applicable_if_variable_unused);
50 return None; 53 return None;
51 }; 54 };
@@ -63,48 +66,45 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
63 let_stmt.syntax().text_range() 66 let_stmt.syntax().text_range()
64 }; 67 };
65 68
66 let mut wrap_in_parens = vec![true; refs.len()]; 69 let wrap_in_parens = usages
67 70 .references
68 for (i, desc) in refs.iter().enumerate() { 71 .values()
69 let usage_node = ctx 72 .flatten()
70 .covering_node_for_range(desc.file_range.range) 73 .map(|&FileReference { range, .. }| {
71 .ancestors() 74 let usage_node =
72 .find_map(ast::PathExpr::cast)?; 75 ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?;
73 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); 76 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast);
74 let usage_parent = match usage_parent_option { 77 let usage_parent = match usage_parent_option {
75 Some(u) => u, 78 Some(u) => u,
76 None => { 79 None => return Ok(false),
77 wrap_in_parens[i] = false; 80 };
78 continue; 81
79 } 82 Ok(!matches!((&initializer_expr, usage_parent),
80 }; 83 (ast::Expr::CallExpr(_), _)
81 84 | (ast::Expr::IndexExpr(_), _)
82 wrap_in_parens[i] = match (&initializer_expr, usage_parent) { 85 | (ast::Expr::MethodCallExpr(_), _)
83 (ast::Expr::CallExpr(_), _) 86 | (ast::Expr::FieldExpr(_), _)
84 | (ast::Expr::IndexExpr(_), _) 87 | (ast::Expr::TryExpr(_), _)
85 | (ast::Expr::MethodCallExpr(_), _) 88 | (ast::Expr::RefExpr(_), _)
86 | (ast::Expr::FieldExpr(_), _) 89 | (ast::Expr::Literal(_), _)
87 | (ast::Expr::TryExpr(_), _) 90 | (ast::Expr::TupleExpr(_), _)
88 | (ast::Expr::RefExpr(_), _) 91 | (ast::Expr::ArrayExpr(_), _)
89 | (ast::Expr::Literal(_), _) 92 | (ast::Expr::ParenExpr(_), _)
90 | (ast::Expr::TupleExpr(_), _) 93 | (ast::Expr::PathExpr(_), _)
91 | (ast::Expr::ArrayExpr(_), _) 94 | (ast::Expr::BlockExpr(_), _)
92 | (ast::Expr::ParenExpr(_), _) 95 | (ast::Expr::EffectExpr(_), _)
93 | (ast::Expr::PathExpr(_), _) 96 | (_, ast::Expr::CallExpr(_))
94 | (ast::Expr::BlockExpr(_), _) 97 | (_, ast::Expr::TupleExpr(_))
95 | (ast::Expr::EffectExpr(_), _) 98 | (_, ast::Expr::ArrayExpr(_))
96 | (_, ast::Expr::CallExpr(_)) 99 | (_, ast::Expr::ParenExpr(_))
97 | (_, ast::Expr::TupleExpr(_)) 100 | (_, ast::Expr::ForExpr(_))
98 | (_, ast::Expr::ArrayExpr(_)) 101 | (_, ast::Expr::WhileExpr(_))
99 | (_, ast::Expr::ParenExpr(_)) 102 | (_, ast::Expr::BreakExpr(_))
100 | (_, ast::Expr::ForExpr(_)) 103 | (_, ast::Expr::ReturnExpr(_))
101 | (_, ast::Expr::WhileExpr(_)) 104 | (_, ast::Expr::MatchExpr(_))
102 | (_, ast::Expr::BreakExpr(_)) 105 ))
103 | (_, ast::Expr::ReturnExpr(_)) 106 })
104 | (_, ast::Expr::MatchExpr(_)) => false, 107 .collect::<Result<Vec<_>, _>>()?;
105 _ => true,
106 };
107 }
108 108
109 let init_str = initializer_expr.syntax().text().to_string(); 109 let init_str = initializer_expr.syntax().text().to_string();
110 let init_in_paren = format!("({})", &init_str); 110 let init_in_paren = format!("({})", &init_str);
@@ -116,15 +116,16 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
116 target, 116 target,
117 move |builder| { 117 move |builder| {
118 builder.delete(delete_range); 118 builder.delete(delete_range);
119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 119 for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens)
120 {
120 let replacement = 121 let replacement =
121 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 122 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
122 match desc.kind { 123 match reference.kind {
123 ReferenceKind::FieldShorthandForLocal => { 124 ReferenceKind::FieldShorthandForLocal => {
124 mark::hit!(inline_field_shorthand); 125 mark::hit!(inline_field_shorthand);
125 builder.insert(desc.file_range.range.end(), format!(": {}", replacement)) 126 builder.insert(reference.range.end(), format!(": {}", replacement))
126 } 127 }
127 _ => builder.replace(desc.file_range.range, replacement), 128 _ => builder.replace(reference.range, replacement),
128 } 129 }
129 } 130 }
130 }, 131 },
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index 56e8b5229..c961680e2 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -1,8 +1,8 @@
1use ide_db::{defs::Definition, search::Reference}; 1use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
2use syntax::{ 2use syntax::{
3 algo::find_node_at_range, 3 algo::find_node_at_range,
4 ast::{self, ArgListOwner}, 4 ast::{self, ArgListOwner},
5 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
6}; 6};
7use test_utils::mark; 7use test_utils::mark;
8use SyntaxKind::WHITESPACE; 8use SyntaxKind::WHITESPACE;
@@ -58,32 +58,41 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
58 param.syntax().text_range(), 58 param.syntax().text_range(),
59 |builder| { 59 |builder| {
60 builder.delete(range_to_remove(param.syntax())); 60 builder.delete(range_to_remove(param.syntax()));
61 for usage in fn_def.usages(&ctx.sema).all() { 61 for (file_id, references) in fn_def.usages(&ctx.sema).all() {
62 process_usage(ctx, builder, usage, param_position); 62 process_usages(ctx, builder, file_id, references, param_position);
63 } 63 }
64 }, 64 },
65 ) 65 )
66} 66}
67 67
68fn process_usage( 68fn process_usages(
69 ctx: &AssistContext, 69 ctx: &AssistContext,
70 builder: &mut AssistBuilder, 70 builder: &mut AssistBuilder,
71 usage: Reference, 71 file_id: FileId,
72 references: Vec<FileReference>,
72 arg_to_remove: usize, 73 arg_to_remove: usize,
73) -> Option<()> { 74) {
74 let source_file = ctx.sema.parse(usage.file_range.file_id); 75 let source_file = ctx.sema.parse(file_id);
75 let call_expr: ast::CallExpr = 76 builder.edit_file(file_id);
76 find_node_at_range(source_file.syntax(), usage.file_range.range)?; 77 for usage in references {
78 if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) {
79 builder.delete(text_range);
80 }
81 }
82}
83
84fn process_usage(
85 source_file: &SourceFile,
86 FileReference { range, .. }: FileReference,
87 arg_to_remove: usize,
88) -> Option<TextRange> {
89 let call_expr: ast::CallExpr = find_node_at_range(source_file.syntax(), range)?;
77 let call_expr_range = call_expr.expr()?.syntax().text_range(); 90 let call_expr_range = call_expr.expr()?.syntax().text_range();
78 if !call_expr_range.contains_range(usage.file_range.range) { 91 if !call_expr_range.contains_range(range) {
79 return None; 92 return None;
80 } 93 }
81 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; 94 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
82 95 Some(range_to_remove(arg.syntax()))
83 builder.edit_file(usage.file_range.file_id);
84 builder.delete(range_to_remove(arg.syntax()));
85
86 Some(())
87} 96}
88 97
89fn range_to_remove(node: &SyntaxNode) -> TextRange { 98fn range_to_remove(node: &SyntaxNode) -> TextRange {
diff --git a/crates/assists/src/handlers/reorder_impl.rs b/crates/assists/src/handlers/reorder_impl.rs
new file mode 100644
index 000000000..309f291c8
--- /dev/null
+++ b/crates/assists/src/handlers/reorder_impl.rs
@@ -0,0 +1,201 @@
1use itertools::Itertools;
2use rustc_hash::FxHashMap;
3
4use hir::{PathResolution, Semantics};
5use ide_db::RootDatabase;
6use syntax::{
7 algo,
8 ast::{self, NameOwner},
9 AstNode,
10};
11use test_utils::mark;
12
13use crate::{AssistContext, AssistId, AssistKind, Assists};
14
15// Assist: reorder_impl
16//
17// Reorder the methods of an `impl Trait`. The methods will be ordered
18// in the same order as in the trait definition.
19//
20// ```
21// trait Foo {
22// fn a() {}
23// fn b() {}
24// fn c() {}
25// }
26//
27// struct Bar;
28// $0impl Foo for Bar {
29// fn b() {}
30// fn c() {}
31// fn a() {}
32// }
33// ```
34// ->
35// ```
36// trait Foo {
37// fn a() {}
38// fn b() {}
39// fn c() {}
40// }
41//
42// struct Bar;
43// impl Foo for Bar {
44// fn a() {}
45// fn b() {}
46// fn c() {}
47// }
48// ```
49//
50pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
51 let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
52 let items = impl_ast.assoc_item_list()?;
53 let methods = get_methods(&items);
54
55 let path = impl_ast
56 .trait_()
57 .and_then(|t| match t {
58 ast::Type::PathType(path) => Some(path),
59 _ => None,
60 })?
61 .path()?;
62
63 let ranks = compute_method_ranks(&path, ctx)?;
64 let sorted: Vec<_> = methods
65 .iter()
66 .cloned()
67 .sorted_by_key(|f| {
68 f.name().and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value())
69 })
70 .collect();
71
72 // Don't edit already sorted methods:
73 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted);
75 return None;
76 }
77
78 let target = items.syntax().text_range();
79 acc.add(AssistId("reorder_impl", AssistKind::RefactorRewrite), "Sort methods", target, |edit| {
80 let mut rewriter = algo::SyntaxRewriter::default();
81 for (old, new) in methods.iter().zip(&sorted) {
82 rewriter.replace(old.syntax(), new.syntax());
83 }
84 edit.rewrite(rewriter);
85 })
86}
87
88fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
89 let td = trait_definition(path, &ctx.sema)?;
90
91 Some(
92 td.items(ctx.db())
93 .iter()
94 .flat_map(|i| match i {
95 hir::AssocItem::Function(f) => Some(f),
96 _ => None,
97 })
98 .enumerate()
99 .map(|(idx, func)| ((func.name(ctx.db()).to_string(), idx)))
100 .collect(),
101 )
102}
103
104fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<hir::Trait> {
105 match sema.resolve_path(path)? {
106 PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_),
107 _ => None,
108 }
109}
110
111fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
112 items
113 .assoc_items()
114 .flat_map(|i| match i {
115 ast::AssocItem::Fn(f) => Some(f),
116 _ => None,
117 })
118 .filter(|f| f.name().is_some())
119 .collect()
120}
121
122#[cfg(test)]
123mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable};
127
128 use super::*;
129
130 #[test]
131 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable(
134 reorder_impl,
135 r#"
136trait Bar {
137 fn a() {}
138 fn z() {}
139 fn b() {}
140}
141struct Foo;
142$0impl Bar for Foo {
143 fn a() {}
144 fn z() {}
145 fn b() {}
146}
147 "#,
148 )
149 }
150
151 #[test]
152 fn not_applicable_if_empty() {
153 check_assist_not_applicable(
154 reorder_impl,
155 r#"
156trait Bar {};
157struct Foo;
158$0impl Bar for Foo {}
159 "#,
160 )
161 }
162
163 #[test]
164 fn reorder_impl_trait_methods() {
165 check_assist(
166 reorder_impl,
167 r#"
168trait Bar {
169 fn a() {}
170 fn c() {}
171 fn b() {}
172 fn d() {}
173}
174
175struct Foo;
176$0impl Bar for Foo {
177 fn d() {}
178 fn b() {}
179 fn c() {}
180 fn a() {}
181}
182 "#,
183 r#"
184trait Bar {
185 fn a() {}
186 fn c() {}
187 fn b() {}
188 fn d() {}
189}
190
191struct Foo;
192impl Bar for Foo {
193 fn a() {}
194 fn c() {}
195 fn b() {}
196 fn d() {}
197}
198 "#,
199 )
200 }
201}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 90009c55a..1080294ab 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -146,6 +146,7 @@ mod handlers {
146 mod remove_mut; 146 mod remove_mut;
147 mod remove_unused_param; 147 mod remove_unused_param;
148 mod reorder_fields; 148 mod reorder_fields;
149 mod reorder_impl;
149 mod replace_derive_with_manual_impl; 150 mod replace_derive_with_manual_impl;
150 mod replace_if_let_with_match; 151 mod replace_if_let_with_match;
151 mod replace_impl_trait_with_generic; 152 mod replace_impl_trait_with_generic;
@@ -202,6 +203,7 @@ mod handlers {
202 remove_mut::remove_mut, 203 remove_mut::remove_mut,
203 remove_unused_param::remove_unused_param, 204 remove_unused_param::remove_unused_param,
204 reorder_fields::reorder_fields, 205 reorder_fields::reorder_fields,
206 reorder_impl::reorder_impl,
205 replace_derive_with_manual_impl::replace_derive_with_manual_impl, 207 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
206 replace_if_let_with_match::replace_if_let_with_match, 208 replace_if_let_with_match::replace_if_let_with_match,
207 replace_if_let_with_match::replace_match_with_if_let, 209 replace_if_let_with_match::replace_match_with_if_let,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index e28837b53..217f577eb 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -896,6 +896,41 @@ const test: Foo = Foo {foo: 1, bar: 0}
896} 896}
897 897
898#[test] 898#[test]
899fn doctest_reorder_impl() {
900 check_doc_test(
901 "reorder_impl",
902 r#####"
903trait Foo {
904 fn a() {}
905 fn b() {}
906 fn c() {}
907}
908
909struct Bar;
910$0impl Foo for Bar {
911 fn b() {}
912 fn c() {}
913 fn a() {}
914}
915"#####,
916 r#####"
917trait Foo {
918 fn a() {}
919 fn b() {}
920 fn c() {}
921}
922
923struct Bar;
924impl Foo for Bar {
925 fn a() {}
926 fn b() {}
927 fn c() {}
928}
929"#####,
930 )
931}
932
933#[test]
899fn doctest_replace_derive_with_manual_impl() { 934fn doctest_replace_derive_with_manual_impl() {
900 check_doc_test( 935 check_doc_test(
901 "replace_derive_with_manual_impl", 936 "replace_derive_with_manual_impl",
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index b29d1fef9..e8999a7f3 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -47,22 +47,23 @@ 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 reference in refs.info.references() { 50 for (&file_id, references) in refs.info.references().iter() {
51 let file_id = reference.file_range.file_id;
52 let file = sema.parse(file_id); 51 let file = sema.parse(file_id);
53 let file = file.syntax(); 52 let file = file.syntax();
54 let token = file.token_at_offset(reference.file_range.range.start()).next()?; 53 for reference in references {
55 let token = sema.descend_into_macros(token); 54 let token = file.token_at_offset(reference.range.start()).next()?;
56 let syntax = token.parent(); 55 let token = sema.descend_into_macros(token);
57 56 let syntax = token.parent();
58 // This target is the containing function 57
59 if let Some(nav) = syntax.ancestors().find_map(|node| { 58 // This target is the containing function
60 let fn_ = ast::Fn::cast(node)?; 59 if let Some(nav) = syntax.ancestors().find_map(|node| {
61 let def = sema.to_def(&fn_)?; 60 let fn_ = ast::Fn::cast(node)?;
62 def.try_to_nav(sema.db) 61 let def = sema.to_def(&fn_)?;
63 }) { 62 def.try_to_nav(sema.db)
64 let relative_range = reference.file_range.range; 63 }) {
65 calls.add(&nav, relative_range); 64 let relative_range = reference.range;
65 calls.add(&nav, relative_range);
66 }
66 } 67 }
67 } 68 }
68 69
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 1f368cbd0..1e03832ec 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -92,7 +92,7 @@ pub use ide_db::base_db::{
92}; 92};
93pub use ide_db::{ 93pub use ide_db::{
94 call_info::CallInfo, 94 call_info::CallInfo,
95 search::{Reference, ReferenceAccess, ReferenceKind}, 95 search::{FileReference, ReferenceAccess, ReferenceKind},
96}; 96};
97pub use ide_db::{ 97pub use ide_db::{
98 label::Label, 98 label::Label,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index b774a2be1..7d4757e02 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -13,9 +13,9 @@ pub(crate) mod rename;
13 13
14use hir::Semantics; 14use hir::Semantics;
15use ide_db::{ 15use ide_db::{
16 base_db::FileId,
16 defs::{Definition, NameClass, NameRefClass}, 17 defs::{Definition, NameClass, NameRefClass},
17 search::Reference, 18 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult},
18 search::{ReferenceAccess, ReferenceKind, SearchScope},
19 RootDatabase, 19 RootDatabase,
20}; 20};
21use syntax::{ 21use syntax::{
@@ -29,7 +29,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI
29#[derive(Debug, Clone)] 29#[derive(Debug, Clone)]
30pub struct ReferenceSearchResult { 30pub struct ReferenceSearchResult {
31 declaration: Declaration, 31 declaration: Declaration,
32 references: Vec<Reference>, 32 references: UsageSearchResult,
33} 33}
34 34
35#[derive(Debug, Clone)] 35#[derive(Debug, Clone)]
@@ -48,10 +48,21 @@ impl ReferenceSearchResult {
48 &self.declaration.nav 48 &self.declaration.nav
49 } 49 }
50 50
51 pub fn references(&self) -> &[Reference] { 51 pub fn references(&self) -> &UsageSearchResult {
52 &self.references 52 &self.references
53 } 53 }
54 54
55 pub fn references_with_declaration(mut self) -> UsageSearchResult {
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
55 /// Total number of references 66 /// Total number of references
56 /// At least 1 since all valid references should 67 /// At least 1 since all valid references should
57 /// Have a declaration 68 /// Have a declaration
@@ -63,21 +74,11 @@ impl ReferenceSearchResult {
63// allow turning ReferenceSearchResult into an iterator 74// allow turning ReferenceSearchResult into an iterator
64// over References 75// over References
65impl IntoIterator for ReferenceSearchResult { 76impl IntoIterator for ReferenceSearchResult {
66 type Item = Reference; 77 type Item = (FileId, Vec<FileReference>);
67 type IntoIter = std::vec::IntoIter<Reference>; 78 type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>;
68 79
69 fn into_iter(mut self) -> Self::IntoIter { 80 fn into_iter(self) -> Self::IntoIter {
70 let mut v = Vec::with_capacity(self.len()); 81 self.references_with_declaration().into_iter()
71 v.push(Reference {
72 file_range: FileRange {
73 file_id: self.declaration.nav.file_id,
74 range: self.declaration.nav.focus_or_full_range(),
75 },
76 kind: self.declaration.kind,
77 access: self.declaration.access,
78 });
79 v.append(&mut self.references);
80 v.into_iter()
81 } 82 }
82} 83}
83 84
@@ -109,13 +110,12 @@ pub(crate) fn find_all_refs(
109 110
110 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)?;
111 112
112 let references = def 113 let mut usages = def.usages(sema).set_scope(search_scope).all();
113 .usages(sema) 114 usages
114 .set_scope(search_scope) 115 .references
115 .all() 116 .values_mut()
116 .into_iter() 117 .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind));
117 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 118 usages.references.retain(|_, it| !it.is_empty());
118 .collect();
119 119
120 let nav = def.try_to_nav(sema.db)?; 120 let nav = def.try_to_nav(sema.db)?;
121 let decl_range = nav.focus_or_full_range(); 121 let decl_range = nav.focus_or_full_range();
@@ -139,7 +139,7 @@ pub(crate) fn find_all_refs(
139 139
140 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) };
141 141
142 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) 142 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages }))
143} 143}
144 144
145fn find_name( 145fn find_name(
@@ -255,7 +255,8 @@ fn try_find_self_references(
255 syntax: &SyntaxNode, 255 syntax: &SyntaxNode,
256 position: FilePosition, 256 position: FilePosition,
257) -> Option<RangeInfo<ReferenceSearchResult>> { 257) -> Option<RangeInfo<ReferenceSearchResult>> {
258 let self_token = syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])?; 258 let FilePosition { file_id, offset } = position;
259 let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?;
259 let parent = self_token.parent(); 260 let parent = self_token.parent();
260 match_ast! { 261 match_ast! {
261 match parent { 262 match parent {
@@ -276,7 +277,7 @@ fn try_find_self_references(
276 277
277 let declaration = Declaration { 278 let declaration = Declaration {
278 nav: NavigationTarget { 279 nav: NavigationTarget {
279 file_id: position.file_id, 280 file_id,
280 full_range: self_param.syntax().text_range(), 281 full_range: self_param.syntax().text_range(),
281 focus_range: Some(param_self_token.text_range()), 282 focus_range: Some(param_self_token.text_range()),
282 name: param_self_token.text().clone(), 283 name: param_self_token.text().clone(),
@@ -292,7 +293,7 @@ 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 body.syntax() 299 body.syntax()
@@ -306,14 +307,16 @@ fn try_find_self_references(
306 None 307 None
307 } 308 }
308 }) 309 })
309 .map(|token| Reference { 310 .map(|token| FileReference {
310 file_range: FileRange { file_id: position.file_id, range: token.text_range() }, 311 range: token.text_range(),
311 kind: ReferenceKind::SelfKw, 312 kind: ReferenceKind::SelfKw,
312 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration 313 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
313 }) 314 })
314 .collect() 315 .collect()
315 }) 316 })
316 .unwrap_or_default(); 317 .unwrap_or_default();
318 let mut references = UsageSearchResult::default();
319 references.references.insert(file_id, refs);
317 320
318 Some(RangeInfo::new( 321 Some(RangeInfo::new(
319 param_self_token.text_range(), 322 param_self_token.text_range(),
@@ -1018,12 +1021,14 @@ impl Foo {
1018 actual += "\n\n"; 1021 actual += "\n\n";
1019 } 1022 }
1020 1023
1021 for r in &refs.references { 1024 for (file_id, references) in refs.references {
1022 format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); 1025 for r in references {
1023 if let Some(access) = r.access { 1026 format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind);
1024 format_to!(actual, " {:?}", access); 1027 if let Some(access) = r.access {
1028 format_to!(actual, " {:?}", access);
1029 }
1030 actual += "\n";
1025 } 1031 }
1026 actual += "\n";
1027 } 1032 }
1028 expect.assert_eq(&actual) 1033 expect.assert_eq(&actual)
1029 } 1034 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 3edc43e08..c3ae568c2 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,14 +1,14 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::{ 2use std::{
3 convert::TryInto, 3 convert::TryInto,
4 error::Error,
5 fmt::{self, Display}, 4 fmt::{self, Display},
6}; 5};
7 6
8use hir::{Module, ModuleDef, ModuleSource, Semantics}; 7use hir::{Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
10use ide_db::{ 8use ide_db::{
9 base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt},
11 defs::{Definition, NameClass, NameRefClass}, 10 defs::{Definition, NameClass, NameRefClass},
11 search::FileReference,
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use syntax::{ 14use syntax::{
@@ -20,8 +20,8 @@ use test_utils::mark;
20use text_edit::TextEdit; 20use text_edit::TextEdit;
21 21
22use crate::{ 22use crate::{
23 FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, ReferenceSearchResult, 23 FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange,
24 SourceChange, SourceFileEdit, TextRange, TextSize, 24 SourceFileEdit, TextRange, TextSize,
25}; 25};
26 26
27type RenameResult<T> = Result<T, RenameError>; 27type RenameResult<T> = Result<T, RenameError>;
@@ -34,8 +34,6 @@ impl fmt::Display for RenameError {
34 } 34 }
35} 35}
36 36
37impl Error for RenameError {}
38
39macro_rules! format_err { 37macro_rules! format_err {
40 ($fmt:expr) => {RenameError(format!($fmt))}; 38 ($fmt:expr) => {RenameError(format!($fmt))};
41 ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))} 39 ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))}
@@ -173,39 +171,46 @@ fn find_all_refs(
173 .ok_or_else(|| format_err!("No references found at position")) 171 .ok_or_else(|| format_err!("No references found at position"))
174} 172}
175 173
176fn source_edit_from_reference( 174fn source_edit_from_references(
177 sema: &Semantics<RootDatabase>, 175 sema: &Semantics<RootDatabase>,
178 reference: Reference, 176 file_id: FileId,
177 references: &[FileReference],
179 new_name: &str, 178 new_name: &str,
180) -> SourceFileEdit { 179) -> SourceFileEdit {
181 let mut replacement_text = String::new(); 180 let mut edit = TextEdit::builder();
182 let range = match reference.kind { 181 for reference in references {
183 ReferenceKind::FieldShorthandForField => { 182 let mut replacement_text = String::new();
184 mark::hit!(test_rename_struct_field_for_shorthand); 183 let range = match reference.kind {
185 replacement_text.push_str(new_name); 184 ReferenceKind::FieldShorthandForField => {
186 replacement_text.push_str(": "); 185 mark::hit!(test_rename_struct_field_for_shorthand);
187 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) 186 replacement_text.push_str(new_name);
188 } 187 replacement_text.push_str(": ");
189 ReferenceKind::FieldShorthandForLocal => { 188 TextRange::new(reference.range.start(), reference.range.start())
190 mark::hit!(test_rename_local_for_field_shorthand); 189 }
191 replacement_text.push_str(": "); 190 ReferenceKind::FieldShorthandForLocal => {
192 replacement_text.push_str(new_name); 191 mark::hit!(test_rename_local_for_field_shorthand);
193 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 192 replacement_text.push_str(": ");
194 } 193 replacement_text.push_str(new_name);
195 ReferenceKind::RecordFieldExprOrPat => { 194 TextRange::new(reference.range.end(), reference.range.end())
196 mark::hit!(test_rename_field_expr_pat); 195 }
197 replacement_text.push_str(new_name); 196 ReferenceKind::RecordFieldExprOrPat => {
198 edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name) 197 mark::hit!(test_rename_field_expr_pat);
199 } 198 replacement_text.push_str(new_name);
200 _ => { 199 edit_text_range_for_record_field_expr_or_pat(
201 replacement_text.push_str(new_name); 200 sema,
202 reference.file_range.range 201 FileRange { file_id, range: reference.range },
203 } 202 new_name,
204 }; 203 )
205 SourceFileEdit { 204 }
206 file_id: reference.file_range.file_id, 205 _ => {
207 edit: TextEdit::replace(range, replacement_text), 206 replacement_text.push_str(new_name);
207 reference.range
208 }
209 };
210 edit.replace(range, replacement_text);
208 } 211 }
212
213 SourceFileEdit { file_id, edit: edit.finish() }
209} 214}
210 215
211fn edit_text_range_for_record_field_expr_or_pat( 216fn edit_text_range_for_record_field_expr_or_pat(
@@ -276,10 +281,9 @@ fn rename_mod(
276 } 281 }
277 282
278 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; 283 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
279 let ref_edits = refs 284 let ref_edits = refs.references().iter().map(|(&file_id, references)| {
280 .references 285 source_edit_from_references(sema, file_id, references, new_name)
281 .into_iter() 286 });
282 .map(|reference| source_edit_from_reference(sema, reference, new_name));
283 source_file_edits.extend(ref_edits); 287 source_file_edits.extend(ref_edits);
284 288
285 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 289 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@@ -331,17 +335,12 @@ fn rename_to_self(
331 335
332 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; 336 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
333 337
334 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 338 let mut edits = refs
335 .into_iter() 339 .references()
336 .partition(|reference| param_range.intersect(reference.file_range.range).is_some()); 340 .iter()
337 341 .map(|(&file_id, references)| {
338 if param_ref.is_empty() { 342 source_edit_from_references(sema, file_id, references, "self")
339 bail!("Parameter to rename not found"); 343 })
340 }
341
342 let mut edits = usages
343 .into_iter()
344 .map(|reference| source_edit_from_reference(sema, reference, "self"))
345 .collect::<Vec<_>>(); 344 .collect::<Vec<_>>();
346 345
347 edits.push(SourceFileEdit { 346 edits.push(SourceFileEdit {
@@ -467,7 +466,9 @@ fn rename_reference(
467 466
468 let edit = refs 467 let edit = refs
469 .into_iter() 468 .into_iter()
470 .map(|reference| source_edit_from_reference(sema, reference, new_name)) 469 .map(|(file_id, references)| {
470 source_edit_from_references(sema, file_id, &references, new_name)
471 })
471 .collect::<Vec<_>>(); 472 .collect::<Vec<_>>();
472 473
473 Ok(RangeInfo::new(range, SourceChange::from(edit))) 474 Ok(RangeInfo::new(range, SourceChange::from(edit)))
diff --git a/crates/ide/src/syntax_highlighting/highlights.rs b/crates/ide/src/syntax_highlighting/highlights.rs
index c6f0417ec..882a685a5 100644
--- a/crates/ide/src/syntax_highlighting/highlights.rs
+++ b/crates/ide/src/syntax_highlighting/highlights.rs
@@ -1,5 +1,5 @@
1//! Collects a tree of highlighted ranges and flattens it. 1//! Collects a tree of highlighted ranges and flattens it.
2use std::{cmp::Ordering, iter}; 2use std::iter;
3 3
4use stdx::equal_range_by; 4use stdx::equal_range_by;
5use syntax::TextRange; 5use syntax::TextRange;
@@ -52,7 +52,7 @@ impl Node {
52 } 52 }
53 53
54 let overlapping = 54 let overlapping =
55 equal_range_by(&self.nested, |n| ordering(n.hl_range.range, hl_range.range)); 55 equal_range_by(&self.nested, |n| TextRange::ordering(n.hl_range.range, hl_range.range));
56 56
57 if overlapping.len() == 1 57 if overlapping.len() == 1
58 && self.nested[overlapping.start].hl_range.range.contains_range(hl_range.range) 58 && self.nested[overlapping.start].hl_range.range.contains_range(hl_range.range)
@@ -90,13 +90,3 @@ impl Node {
90 } 90 }
91 } 91 }
92} 92}
93
94pub(super) fn ordering(r1: TextRange, r2: TextRange) -> Ordering {
95 if r1.end() <= r2.start() {
96 Ordering::Less
97 } else if r2.end() <= r1.start() {
98 Ordering::Greater
99 } else {
100 Ordering::Equal
101 }
102}
diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs
index fd4025694..24ff473ec 100644
--- a/crates/ide/src/syntax_highlighting/injector.rs
+++ b/crates/ide/src/syntax_highlighting/injector.rs
@@ -5,8 +5,6 @@ use std::ops::{self, Sub};
5use stdx::equal_range_by; 5use stdx::equal_range_by;
6use syntax::{TextRange, TextSize}; 6use syntax::{TextRange, TextSize};
7 7
8use super::highlights::ordering;
9
10#[derive(Default)] 8#[derive(Default)]
11pub(super) struct Injector { 9pub(super) struct Injector {
12 buf: String, 10 buf: String,
@@ -33,7 +31,7 @@ impl Injector {
33 &self.buf 31 &self.buf
34 } 32 }
35 pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ { 33 pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
36 equal_range_by(&self.ranges, |&(r, _)| ordering(r, range)).filter_map(move |i| { 34 equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| {
37 let (target_range, delta) = self.ranges[i]; 35 let (target_range, delta) = self.ranges[i];
38 let intersection = target_range.intersect(range).unwrap(); 36 let intersection = target_range.intersect(range).unwrap();
39 Some(intersection + delta?) 37 Some(intersection + delta?)
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 773bfbc2c..b5fa46642 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}
@@ -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/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 3db0d55c5..f148521a2 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -75,14 +75,24 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
75 will_create: None, 75 will_create: None,
76 did_rename: None, 76 did_rename: None,
77 will_rename: Some(FileOperationRegistrationOptions { 77 will_rename: Some(FileOperationRegistrationOptions {
78 filters: vec![FileOperationFilter { 78 filters: vec![
79 scheme: Some(String::from("file")), 79 FileOperationFilter {
80 pattern: FileOperationPattern { 80 scheme: Some(String::from("file")),
81 glob: String::from("**/*.rs"), 81 pattern: FileOperationPattern {
82 matches: Some(FileOperationPatternKind::File), 82 glob: String::from("**/*.rs"),
83 options: None, 83 matches: Some(FileOperationPatternKind::File),
84 options: None,
85 },
84 }, 86 },
85 }], 87 FileOperationFilter {
88 scheme: Some(String::from("file")),
89 pattern: FileOperationPattern {
90 glob: String::from("**"),
91 matches: Some(FileOperationPatternKind::Folder),
92 options: None,
93 },
94 },
95 ],
86 }), 96 }),
87 did_delete: None, 97 did_delete: None,
88 will_delete: None, 98 will_delete: None,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 29cc9051e..dc81f55d6 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -432,9 +432,27 @@ pub(crate) fn handle_will_rename_files(
432 // Limit to single-level moves for now. 432 // Limit to single-level moves for now.
433 match (from_path.parent(), to_path.parent()) { 433 match (from_path.parent(), to_path.parent()) {
434 (Some(p1), Some(p2)) if p1 == p2 => { 434 (Some(p1), Some(p2)) if p1 == p2 => {
435 let new_name = to_path.file_stem()?; 435 if from_path.is_dir() {
436 let new_name = new_name.to_str()?; 436 // add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/`
437 Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())) 437 let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string();
438 old_folder_name.push('/');
439 let from_with_trailing_slash = from.join(&old_folder_name).ok()?;
440
441 let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?;
442 let new_file_name = to_path.file_name()?.to_str()?;
443 Some((
444 snap.url_to_file_id(&imitate_from_url).ok()?,
445 new_file_name.to_string(),
446 ))
447 } else {
448 let old_name = from_path.file_stem()?.to_str()?;
449 let new_name = to_path.file_stem()?.to_str()?;
450 match (old_name, new_name) {
451 ("mod", _) => None,
452 (_, "mod") => None,
453 _ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())),
454 }
455 }
438 } 456 }
439 _ => None, 457 _ => None,
440 } 458 }
@@ -773,7 +791,8 @@ pub(crate) fn handle_prepare_rename(
773 let _p = profile::span("handle_prepare_rename"); 791 let _p = profile::span("handle_prepare_rename");
774 let position = from_proto::file_position(&snap, params)?; 792 let position = from_proto::file_position(&snap, params)?;
775 793
776 let change = snap.analysis.prepare_rename(position)??; 794 let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
795
777 let line_index = snap.analysis.file_line_index(position.file_id)?; 796 let line_index = snap.analysis.file_line_index(position.file_id)?;
778 let range = to_proto::range(&line_index, change.range); 797 let range = to_proto::range(&line_index, change.range);
779 Ok(Some(PrepareRenameResponse::Range(range))) 798 Ok(Some(PrepareRenameResponse::Range(range)))
@@ -786,15 +805,8 @@ pub(crate) fn handle_rename(
786 let _p = profile::span("handle_rename"); 805 let _p = profile::span("handle_rename");
787 let position = from_proto::file_position(&snap, params.text_document_position)?; 806 let position = from_proto::file_position(&snap, params.text_document_position)?;
788 807
789 if params.new_name.is_empty() { 808 let change =
790 return Err(LspError::new( 809 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
791 ErrorCode::InvalidParams as i32,
792 "New Name cannot be empty".into(),
793 )
794 .into());
795 }
796
797 let change = snap.analysis.rename(position, &*params.new_name)??;
798 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; 810 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?;
799 Ok(Some(workspace_edit)) 811 Ok(Some(workspace_edit))
800} 812}
@@ -812,14 +824,15 @@ pub(crate) fn handle_references(
812 }; 824 };
813 825
814 let locations = if params.context.include_declaration { 826 let locations = if params.context.include_declaration {
815 refs.into_iter() 827 refs.references_with_declaration()
816 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) 828 .file_ranges()
829 .filter_map(|frange| to_proto::location(&snap, frange).ok())
817 .collect() 830 .collect()
818 } else { 831 } else {
819 // Only iterate over the references if include_declaration was false 832 // Only iterate over the references if include_declaration was false
820 refs.references() 833 refs.references()
821 .iter() 834 .file_ranges()
822 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) 835 .filter_map(|frange| to_proto::location(&snap, frange).ok())
823 .collect() 836 .collect()
824 }; 837 };
825 838
@@ -1175,8 +1188,8 @@ pub(crate) fn handle_code_lens_resolve(
1175 .unwrap_or(None) 1188 .unwrap_or(None)
1176 .map(|r| { 1189 .map(|r| {
1177 r.references() 1190 r.references()
1178 .iter() 1191 .file_ranges()
1179 .filter_map(|it| to_proto::location(&snap, it.file_range).ok()) 1192 .filter_map(|frange| to_proto::location(&snap, frange).ok())
1180 .collect_vec() 1193 .collect_vec()
1181 }) 1194 })
1182 .unwrap_or_default(); 1195 .unwrap_or_default();
@@ -1220,13 +1233,19 @@ pub(crate) fn handle_document_highlight(
1220 }; 1233 };
1221 1234
1222 let res = refs 1235 let res = refs
1223 .into_iter() 1236 .references_with_declaration()
1224 .filter(|reference| reference.file_range.file_id == position.file_id) 1237 .references
1225 .map(|reference| DocumentHighlight { 1238 .get(&position.file_id)
1226 range: to_proto::range(&line_index, reference.file_range.range), 1239 .map(|file_refs| {
1227 kind: reference.access.map(to_proto::document_highlight_kind), 1240 file_refs
1241 .into_iter()
1242 .map(|r| DocumentHighlight {
1243 range: to_proto::range(&line_index, r.range),
1244 kind: r.access.map(to_proto::document_highlight_kind),
1245 })
1246 .collect()
1228 }) 1247 })
1229 .collect(); 1248 .unwrap_or_default();
1230 Ok(Some(res)) 1249 Ok(Some(res))
1231} 1250}
1232 1251
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index bdddca9da..a7ff8975a 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -8,7 +8,8 @@ use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, 9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel,
10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, 10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess,
11 Runnable, Severity, SourceChange, SourceFileEdit, SymbolKind, TextEdit, TextRange, TextSize, 11 RenameError, Runnable, Severity, SourceChange, SourceFileEdit, SymbolKind, TextEdit, TextRange,
12 TextSize,
12}; 13};
13use itertools::Itertools; 14use itertools::Itertools;
14 15
@@ -855,6 +856,10 @@ pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
855 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } 856 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }
856} 857}
857 858
859pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
860 crate::LspError { code: lsp_server::ErrorCode::InvalidParams as i32, message: err.to_string() }
861}
862
858#[cfg(test)] 863#[cfg(test)]
859mod tests { 864mod tests {
860 use ide::Analysis; 865 use ide::Analysis;
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index f5c5c63e5..80bde29b9 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -16,11 +16,14 @@ use std::{collections::HashMap, path::PathBuf, time::Instant};
16use expect_test::expect; 16use expect_test::expect;
17use lsp_types::{ 17use lsp_types::{
18 notification::DidOpenTextDocument, 18 notification::DidOpenTextDocument,
19 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest}, 19 request::{
20 CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
21 WillRenameFiles,
22 },
20 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, 23 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
21 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, 24 DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
22 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, 25 PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
23 WorkDoneProgressParams, 26 TextDocumentPositionParams, WorkDoneProgressParams,
24}; 27};
25use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; 28use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
26use serde_json::json; 29use serde_json::json;
@@ -745,3 +748,136 @@ pub fn foo(_input: TokenStream) -> TokenStream {
745 ```"#]] 748 ```"#]]
746 .assert_eq(&value); 749 .assert_eq(&value);
747} 750}
751
752#[test]
753fn test_will_rename_files_same_level() {
754 if skip_slow_tests() {
755 return;
756 }
757
758 let tmp_dir = TestDir::new();
759 let tmp_dir_path = tmp_dir.path().to_owned();
760 let tmp_dir_str = tmp_dir_path.to_str().unwrap();
761 let base_path = PathBuf::from(format!("file://{}", tmp_dir_str));
762
763 let code = r#"
764//- /Cargo.toml
765[package]
766name = "foo"
767version = "0.0.0"
768
769//- /src/lib.rs
770mod old_file;
771mod from_mod;
772mod to_mod;
773mod old_folder;
774fn main() {}
775
776//- /src/old_file.rs
777
778//- /src/old_folder/mod.rs
779
780//- /src/from_mod/mod.rs
781
782//- /src/to_mod/foo.rs
783
784"#;
785 let server =
786 Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
787
788 //rename same level file
789 server.request::<WillRenameFiles>(
790 RenameFilesParams {
791 files: vec![FileRename {
792 old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_string(),
793 new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_string(),
794 }],
795 },
796 json!({
797 "documentChanges": [
798 {
799 "textDocument": {
800 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")),
801 "version": null
802 },
803 "edits": [
804 {
805 "range": {
806 "start": {
807 "line": 0,
808 "character": 4
809 },
810 "end": {
811 "line": 0,
812 "character": 12
813 }
814 },
815 "newText": "new_file"
816 }
817 ]
818 }
819 ]
820 }),
821 );
822
823 //rename file from mod.rs to foo.rs
824 server.request::<WillRenameFiles>(
825 RenameFilesParams {
826 files: vec![FileRename {
827 old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_string(),
828 new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_string(),
829 }],
830 },
831 json!({
832 "documentChanges": []
833 }),
834 );
835
836 //rename file from foo.rs to mod.rs
837 server.request::<WillRenameFiles>(
838 RenameFilesParams {
839 files: vec![FileRename {
840 old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_string(),
841 new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_string(),
842 }],
843 },
844 json!({
845 "documentChanges": []
846 }),
847 );
848
849 //rename same level file
850 server.request::<WillRenameFiles>(
851 RenameFilesParams {
852 files: vec![FileRename {
853 old_uri: base_path.join("src/old_folder").to_str().unwrap().to_string(),
854 new_uri: base_path.join("src/new_folder").to_str().unwrap().to_string(),
855 }],
856 },
857 json!({
858 "documentChanges": [
859 {
860 "textDocument": {
861 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")),
862 "version": null
863 },
864 "edits": [
865 {
866 "range": {
867 "start": {
868 "line": 3,
869 "character": 4
870 },
871 "end": {
872 "line": 3,
873 "character": 14
874 }
875 },
876 "newText": "new_folder"
877 }
878 ]
879 }
880 ]
881 }),
882 );
883}
diff --git a/crates/ssr/src/search.rs b/crates/ssr/src/search.rs
index 44b5db029..836eb94b2 100644
--- a/crates/ssr/src/search.rs
+++ b/crates/ssr/src/search.rs
@@ -5,10 +5,10 @@ use crate::{
5 resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, 5 resolving::{ResolvedPath, ResolvedPattern, ResolvedRule},
6 Match, MatchFinder, 6 Match, MatchFinder,
7}; 7};
8use ide_db::base_db::{FileId, FileRange};
9use ide_db::{ 8use ide_db::{
9 base_db::{FileId, FileRange},
10 defs::Definition, 10 defs::Definition,
11 search::{Reference, SearchScope}, 11 search::{SearchScope, UsageSearchResult},
12}; 12};
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; 14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode};
@@ -20,7 +20,7 @@ use test_utils::mark;
20/// them more than once. 20/// them more than once.
21#[derive(Default)] 21#[derive(Default)]
22pub(crate) struct UsageCache { 22pub(crate) struct UsageCache {
23 usages: Vec<(Definition, Vec<Reference>)>, 23 usages: Vec<(Definition, UsageSearchResult)>,
24} 24}
25 25
26impl<'db> MatchFinder<'db> { 26impl<'db> MatchFinder<'db> {
@@ -58,8 +58,8 @@ 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 reference in self.find_usages(usage_cache, definition) { 61 for file_range in self.find_usages(usage_cache, definition).file_ranges() {
62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, reference) { 62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) {
63 if !is_search_permitted_ancestors(&node_to_match) { 63 if !is_search_permitted_ancestors(&node_to_match) {
64 mark::hit!(use_declaration_with_braces); 64 mark::hit!(use_declaration_with_braces);
65 continue; 65 continue;
@@ -73,11 +73,11 @@ impl<'db> MatchFinder<'db> {
73 fn find_node_to_match( 73 fn find_node_to_match(
74 &self, 74 &self,
75 resolved_path: &ResolvedPath, 75 resolved_path: &ResolvedPath,
76 reference: &Reference, 76 file_range: FileRange,
77 ) -> Option<SyntaxNode> { 77 ) -> Option<SyntaxNode> {
78 let file = self.sema.parse(reference.file_range.file_id); 78 let file = self.sema.parse(file_range.file_id);
79 let depth = resolved_path.depth as usize; 79 let depth = resolved_path.depth as usize;
80 let offset = reference.file_range.range.start(); 80 let offset = file_range.range.start();
81 if let Some(path) = 81 if let Some(path) =
82 self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset) 82 self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset)
83 { 83 {
@@ -108,7 +108,7 @@ impl<'db> MatchFinder<'db> {
108 &self, 108 &self,
109 usage_cache: &'a mut UsageCache, 109 usage_cache: &'a mut UsageCache,
110 definition: Definition, 110 definition: Definition,
111 ) -> &'a [Reference] { 111 ) -> &'a UsageSearchResult {
112 // 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
113 // 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
114 // 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
@@ -250,7 +250,7 @@ fn is_search_permitted(node: &SyntaxNode) -> bool {
250} 250}
251 251
252impl UsageCache { 252impl UsageCache {
253 fn find(&mut self, definition: &Definition) -> Option<&[Reference]> { 253 fn find(&mut self, definition: &Definition) -> Option<&UsageSearchResult> {
254 // 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
255 // fast enough and avoids the need to implement Hash for Definition. 255 // fast enough and avoids the need to implement Hash for Definition.
256 for (d, refs) in &self.usages { 256 for (d, refs) in &self.usages {