diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/macros.rs | 81 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide/src/references.rs | 253 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 14 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 34 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/expr_extensions.rs | 1 |
8 files changed, 330 insertions, 70 deletions
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 2c119269c..e9e275670 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -62,8 +62,13 @@ register_builtin! { | |||
62 | } | 62 | } |
63 | 63 | ||
64 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | 64 | fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { |
65 | // FIXME: Use expansion info | ||
66 | let file_id = file.original_file(db); | 65 | let file_id = file.original_file(db); |
66 | |||
67 | // FIXME: if the file is coming from macro, we return a dummy value for now. | ||
68 | if file.call_node(db).map(|it| it.file_id != file_id.into()).unwrap_or(true) { | ||
69 | return 0; | ||
70 | } | ||
71 | |||
67 | let text = db.file_text(file_id); | 72 | let text = db.file_text(file_id); |
68 | let mut line_num = 1; | 73 | let mut line_num = 1; |
69 | 74 | ||
@@ -150,8 +155,11 @@ fn option_env_expand( | |||
150 | } | 155 | } |
151 | 156 | ||
152 | fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { | 157 | fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { |
153 | // FIXME: Use expansion info | ||
154 | let file_id = file.original_file(db); | 158 | let file_id = file.original_file(db); |
159 | // FIXME: if the file is coming from macro, we return a dummy value for now. | ||
160 | if file.call_node(db).map(|it| it.file_id != file_id.into()).unwrap_or(true) { | ||
161 | return 0; | ||
162 | } | ||
155 | let text = db.file_text(file_id); | 163 | let text = db.file_text(file_id); |
156 | 164 | ||
157 | let pos = pos.to_usize(); | 165 | let pos = pos.to_usize(); |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 5d12c1584..af5b22d1c 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -190,7 +190,6 @@ fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> Fragmen | |||
190 | TUPLE_EXPR => FragmentKind::Expr, | 190 | TUPLE_EXPR => FragmentKind::Expr, |
191 | PAREN_EXPR => FragmentKind::Expr, | 191 | PAREN_EXPR => FragmentKind::Expr, |
192 | 192 | ||
193 | // FIXME: Add tests for following cases in hir_ty | ||
194 | FOR_EXPR => FragmentKind::Expr, | 193 | FOR_EXPR => FragmentKind::Expr, |
195 | PATH_EXPR => FragmentKind::Expr, | 194 | PATH_EXPR => FragmentKind::Expr, |
196 | LAMBDA_EXPR => FragmentKind::Expr, | 195 | LAMBDA_EXPR => FragmentKind::Expr, |
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 69c695cc8..9d09d93a7 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs | |||
@@ -136,6 +136,87 @@ fn main() { | |||
136 | } | 136 | } |
137 | 137 | ||
138 | #[test] | 138 | #[test] |
139 | fn expr_macro_expanded_in_various_places() { | ||
140 | assert_snapshot!( | ||
141 | infer(r#" | ||
142 | macro_rules! spam { | ||
143 | () => (1isize); | ||
144 | } | ||
145 | |||
146 | fn spam() { | ||
147 | spam!(); | ||
148 | (spam!()); | ||
149 | spam!().spam(spam!()); | ||
150 | for _ in spam!() {} | ||
151 | || spam!(); | ||
152 | while spam!() {} | ||
153 | break spam!(); | ||
154 | return spam!(); | ||
155 | match spam!() { | ||
156 | _ if spam!() => spam!(), | ||
157 | } | ||
158 | spam!()(spam!()); | ||
159 | Spam { spam: spam!() }; | ||
160 | spam!()[spam!()]; | ||
161 | await spam!(); | ||
162 | spam!() as usize; | ||
163 | &spam!(); | ||
164 | -spam!(); | ||
165 | spam!()..spam!(); | ||
166 | spam!() + spam!(); | ||
167 | } | ||
168 | "#), | ||
169 | @r###" | ||
170 | ![0; 6) '1isize': isize | ||
171 | ![0; 6) '1isize': isize | ||
172 | ![0; 6) '1isize': isize | ||
173 | ![0; 6) '1isize': isize | ||
174 | ![0; 6) '1isize': isize | ||
175 | ![0; 6) '1isize': isize | ||
176 | ![0; 6) '1isize': isize | ||
177 | ![0; 6) '1isize': isize | ||
178 | ![0; 6) '1isize': isize | ||
179 | ![0; 6) '1isize': isize | ||
180 | ![0; 6) '1isize': isize | ||
181 | ![0; 6) '1isize': isize | ||
182 | ![0; 6) '1isize': isize | ||
183 | ![0; 6) '1isize': isize | ||
184 | ![0; 6) '1isize': isize | ||
185 | ![0; 6) '1isize': isize | ||
186 | ![0; 6) '1isize': isize | ||
187 | ![0; 6) '1isize': isize | ||
188 | ![0; 6) '1isize': isize | ||
189 | ![0; 6) '1isize': isize | ||
190 | ![0; 6) '1isize': isize | ||
191 | ![0; 6) '1isize': isize | ||
192 | ![0; 6) '1isize': isize | ||
193 | ![0; 6) '1isize': isize | ||
194 | ![0; 6) '1isize': isize | ||
195 | [54; 457) '{ ...!(); }': ! | ||
196 | [88; 109) 'spam!(...am!())': {unknown} | ||
197 | [115; 134) 'for _ ...!() {}': () | ||
198 | [119; 120) '_': {unknown} | ||
199 | [132; 134) '{}': () | ||
200 | [139; 149) '|| spam!()': || -> isize | ||
201 | [155; 171) 'while ...!() {}': () | ||
202 | [169; 171) '{}': () | ||
203 | [176; 189) 'break spam!()': ! | ||
204 | [195; 209) 'return spam!()': ! | ||
205 | [215; 269) 'match ... }': isize | ||
206 | [239; 240) '_': isize | ||
207 | [274; 290) 'spam!(...am!())': {unknown} | ||
208 | [296; 318) 'Spam {...m!() }': {unknown} | ||
209 | [324; 340) 'spam!(...am!()]': {unknown} | ||
210 | [365; 381) 'spam!(... usize': usize | ||
211 | [387; 395) '&spam!()': &isize | ||
212 | [401; 409) '-spam!()': isize | ||
213 | [415; 431) 'spam!(...pam!()': {unknown} | ||
214 | [437; 454) 'spam!(...pam!()': isize | ||
215 | "### | ||
216 | ); | ||
217 | } | ||
218 | |||
219 | #[test] | ||
139 | fn infer_type_value_macro_having_same_name() { | 220 | fn infer_type_value_macro_having_same_name() { |
140 | assert_snapshot!( | 221 | assert_snapshot!( |
141 | infer(r#" | 222 | infer(r#" |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 7b187eba3..4d8deb21c 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -75,7 +75,9 @@ pub use crate::{ | |||
75 | inlay_hints::{InlayHint, InlayKind}, | 75 | inlay_hints::{InlayHint, InlayKind}, |
76 | line_index::{LineCol, LineIndex}, | 76 | line_index::{LineCol, LineIndex}, |
77 | line_index_utils::translate_offset_with_edit, | 77 | line_index_utils::translate_offset_with_edit, |
78 | references::{Reference, ReferenceKind, ReferenceSearchResult, SearchScope}, | 78 | references::{ |
79 | Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, | ||
80 | }, | ||
79 | runnables::{Runnable, RunnableKind}, | 81 | runnables::{Runnable, RunnableKind}, |
80 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 82 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
81 | syntax_highlighting::HighlightedRange, | 83 | syntax_highlighting::HighlightedRange, |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 5a3ec4eb9..4e52e0e7b 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -19,8 +19,9 @@ use once_cell::unsync::Lazy; | |||
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
20 | use ra_prof::profile; | 20 | use ra_prof::profile; |
21 | use ra_syntax::{ | 21 | use ra_syntax::{ |
22 | algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextUnit, | 22 | algo::find_node_at_offset, |
23 | TokenAtOffset, | 23 | ast::{self, NameOwner}, |
24 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, | ||
24 | }; | 25 | }; |
25 | 26 | ||
26 | use crate::{ | 27 | use crate::{ |
@@ -37,15 +38,22 @@ pub use self::search_scope::SearchScope; | |||
37 | 38 | ||
38 | #[derive(Debug, Clone)] | 39 | #[derive(Debug, Clone)] |
39 | pub struct ReferenceSearchResult { | 40 | pub struct ReferenceSearchResult { |
40 | declaration: NavigationTarget, | 41 | declaration: Declaration, |
41 | declaration_kind: ReferenceKind, | ||
42 | references: Vec<Reference>, | 42 | references: Vec<Reference>, |
43 | } | 43 | } |
44 | 44 | ||
45 | #[derive(Debug, Clone)] | 45 | #[derive(Debug, Clone)] |
46 | pub struct Declaration { | ||
47 | pub nav: NavigationTarget, | ||
48 | pub kind: ReferenceKind, | ||
49 | pub access: Option<ReferenceAccess>, | ||
50 | } | ||
51 | |||
52 | #[derive(Debug, Clone)] | ||
46 | pub struct Reference { | 53 | pub struct Reference { |
47 | pub file_range: FileRange, | 54 | pub file_range: FileRange, |
48 | pub kind: ReferenceKind, | 55 | pub kind: ReferenceKind, |
56 | pub access: Option<ReferenceAccess>, | ||
49 | } | 57 | } |
50 | 58 | ||
51 | #[derive(Debug, Clone, PartialEq)] | 59 | #[derive(Debug, Clone, PartialEq)] |
@@ -54,11 +62,21 @@ pub enum ReferenceKind { | |||
54 | Other, | 62 | Other, |
55 | } | 63 | } |
56 | 64 | ||
65 | #[derive(Debug, Copy, Clone, PartialEq)] | ||
66 | pub enum ReferenceAccess { | ||
67 | Read, | ||
68 | Write, | ||
69 | } | ||
70 | |||
57 | impl ReferenceSearchResult { | 71 | impl ReferenceSearchResult { |
58 | pub fn declaration(&self) -> &NavigationTarget { | 72 | pub fn declaration(&self) -> &Declaration { |
59 | &self.declaration | 73 | &self.declaration |
60 | } | 74 | } |
61 | 75 | ||
76 | pub fn decl_target(&self) -> &NavigationTarget { | ||
77 | &self.declaration.nav | ||
78 | } | ||
79 | |||
62 | pub fn references(&self) -> &[Reference] { | 80 | pub fn references(&self) -> &[Reference] { |
63 | &self.references | 81 | &self.references |
64 | } | 82 | } |
@@ -72,7 +90,7 @@ impl ReferenceSearchResult { | |||
72 | } | 90 | } |
73 | 91 | ||
74 | // allow turning ReferenceSearchResult into an iterator | 92 | // allow turning ReferenceSearchResult into an iterator |
75 | // over FileRanges | 93 | // over References |
76 | impl IntoIterator for ReferenceSearchResult { | 94 | impl IntoIterator for ReferenceSearchResult { |
77 | type Item = Reference; | 95 | type Item = Reference; |
78 | type IntoIter = std::vec::IntoIter<Reference>; | 96 | type IntoIter = std::vec::IntoIter<Reference>; |
@@ -81,10 +99,11 @@ impl IntoIterator for ReferenceSearchResult { | |||
81 | let mut v = Vec::with_capacity(self.len()); | 99 | let mut v = Vec::with_capacity(self.len()); |
82 | v.push(Reference { | 100 | v.push(Reference { |
83 | file_range: FileRange { | 101 | file_range: FileRange { |
84 | file_id: self.declaration.file_id(), | 102 | file_id: self.declaration.nav.file_id(), |
85 | range: self.declaration.range(), | 103 | range: self.declaration.nav.range(), |
86 | }, | 104 | }, |
87 | kind: self.declaration_kind, | 105 | kind: self.declaration.kind, |
106 | access: self.declaration.access, | ||
88 | }); | 107 | }); |
89 | v.append(&mut self.references); | 108 | v.append(&mut self.references); |
90 | v.into_iter() | 109 | v.into_iter() |
@@ -131,15 +150,20 @@ pub(crate) fn find_all_refs( | |||
131 | } | 150 | } |
132 | }; | 151 | }; |
133 | 152 | ||
153 | let decl_range = declaration.range(); | ||
154 | |||
155 | let declaration = Declaration { | ||
156 | nav: declaration, | ||
157 | kind: ReferenceKind::Other, | ||
158 | access: decl_access(&def.kind, &name, &syntax, decl_range), | ||
159 | }; | ||
160 | |||
134 | let references = process_definition(db, def, name, search_scope) | 161 | let references = process_definition(db, def, name, search_scope) |
135 | .into_iter() | 162 | .into_iter() |
136 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | 163 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) |
137 | .collect(); | 164 | .collect(); |
138 | 165 | ||
139 | Some(RangeInfo::new( | 166 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) |
140 | range, | ||
141 | ReferenceSearchResult { declaration, references, declaration_kind: ReferenceKind::Other }, | ||
142 | )) | ||
143 | } | 167 | } |
144 | 168 | ||
145 | fn find_name<'a>( | 169 | fn find_name<'a>( |
@@ -201,7 +225,12 @@ fn process_definition( | |||
201 | } else { | 225 | } else { |
202 | ReferenceKind::Other | 226 | ReferenceKind::Other |
203 | }; | 227 | }; |
204 | refs.push(Reference { file_range: FileRange { file_id, range }, kind }); | 228 | |
229 | refs.push(Reference { | ||
230 | file_range: FileRange { file_id, range }, | ||
231 | kind, | ||
232 | access: reference_access(&d.kind, &name_ref), | ||
233 | }); | ||
205 | } | 234 | } |
206 | } | 235 | } |
207 | } | 236 | } |
@@ -210,11 +239,69 @@ fn process_definition( | |||
210 | refs | 239 | refs |
211 | } | 240 | } |
212 | 241 | ||
242 | fn decl_access( | ||
243 | kind: &NameKind, | ||
244 | name: &str, | ||
245 | syntax: &SyntaxNode, | ||
246 | range: TextRange, | ||
247 | ) -> Option<ReferenceAccess> { | ||
248 | match kind { | ||
249 | NameKind::Local(_) | NameKind::Field(_) => {} | ||
250 | _ => return None, | ||
251 | }; | ||
252 | |||
253 | let stmt = find_node_at_offset::<ast::LetStmt>(syntax, range.start())?; | ||
254 | if let Some(_) = stmt.initializer() { | ||
255 | let pat = stmt.pat()?; | ||
256 | match pat { | ||
257 | ast::Pat::BindPat(it) => { | ||
258 | if it.name()?.text().as_str() == name { | ||
259 | return Some(ReferenceAccess::Write); | ||
260 | } | ||
261 | } | ||
262 | _ => {} | ||
263 | } | ||
264 | } | ||
265 | |||
266 | None | ||
267 | } | ||
268 | |||
269 | fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | ||
270 | // Only Locals and Fields have accesses for now. | ||
271 | match kind { | ||
272 | NameKind::Local(_) | NameKind::Field(_) => {} | ||
273 | _ => return None, | ||
274 | }; | ||
275 | |||
276 | let mode = name_ref.syntax().ancestors().find_map(|node| { | ||
277 | match_ast! { | ||
278 | match (node) { | ||
279 | ast::BinExpr(expr) => { | ||
280 | if expr.op_kind()?.is_assignment() { | ||
281 | // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals). | ||
282 | // FIXME: This is not terribly accurate. | ||
283 | if let Some(lhs) = expr.lhs() { | ||
284 | if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() { | ||
285 | return Some(ReferenceAccess::Write); | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | return Some(ReferenceAccess::Read); | ||
290 | }, | ||
291 | _ => {None} | ||
292 | } | ||
293 | } | ||
294 | }); | ||
295 | |||
296 | // Default Locals and Fields to read | ||
297 | mode.or(Some(ReferenceAccess::Read)) | ||
298 | } | ||
299 | |||
213 | #[cfg(test)] | 300 | #[cfg(test)] |
214 | mod tests { | 301 | mod tests { |
215 | use crate::{ | 302 | use crate::{ |
216 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, | 303 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, |
217 | Reference, ReferenceKind, ReferenceSearchResult, SearchScope, | 304 | Declaration, Reference, ReferenceSearchResult, SearchScope, |
218 | }; | 305 | }; |
219 | 306 | ||
220 | #[test] | 307 | #[test] |
@@ -234,8 +321,7 @@ mod tests { | |||
234 | let refs = get_all_refs(code); | 321 | let refs = get_all_refs(code); |
235 | check_result( | 322 | check_result( |
236 | refs, | 323 | refs, |
237 | "Foo STRUCT_DEF FileId(1) [5; 39) [12; 15)", | 324 | "Foo STRUCT_DEF FileId(1) [5; 39) [12; 15) Other", |
238 | ReferenceKind::Other, | ||
239 | &["FileId(1) [142; 145) StructLiteral"], | 325 | &["FileId(1) [142; 145) StructLiteral"], |
240 | ); | 326 | ); |
241 | } | 327 | } |
@@ -258,13 +344,12 @@ mod tests { | |||
258 | let refs = get_all_refs(code); | 344 | let refs = get_all_refs(code); |
259 | check_result( | 345 | check_result( |
260 | refs, | 346 | refs, |
261 | "i BIND_PAT FileId(1) [33; 34)", | 347 | "i BIND_PAT FileId(1) [33; 34) Other Write", |
262 | ReferenceKind::Other, | ||
263 | &[ | 348 | &[ |
264 | "FileId(1) [67; 68) Other", | 349 | "FileId(1) [67; 68) Other Write", |
265 | "FileId(1) [71; 72) Other", | 350 | "FileId(1) [71; 72) Other Read", |
266 | "FileId(1) [101; 102) Other", | 351 | "FileId(1) [101; 102) Other Write", |
267 | "FileId(1) [127; 128) Other", | 352 | "FileId(1) [127; 128) Other Write", |
268 | ], | 353 | ], |
269 | ); | 354 | ); |
270 | } | 355 | } |
@@ -279,9 +364,8 @@ mod tests { | |||
279 | let refs = get_all_refs(code); | 364 | let refs = get_all_refs(code); |
280 | check_result( | 365 | check_result( |
281 | refs, | 366 | refs, |
282 | "i BIND_PAT FileId(1) [12; 13)", | 367 | "i BIND_PAT FileId(1) [12; 13) Other", |
283 | ReferenceKind::Other, | 368 | &["FileId(1) [38; 39) Other Read"], |
284 | &["FileId(1) [38; 39) Other"], | ||
285 | ); | 369 | ); |
286 | } | 370 | } |
287 | 371 | ||
@@ -295,9 +379,8 @@ mod tests { | |||
295 | let refs = get_all_refs(code); | 379 | let refs = get_all_refs(code); |
296 | check_result( | 380 | check_result( |
297 | refs, | 381 | refs, |
298 | "i BIND_PAT FileId(1) [12; 13)", | 382 | "i BIND_PAT FileId(1) [12; 13) Other", |
299 | ReferenceKind::Other, | 383 | &["FileId(1) [38; 39) Other Read"], |
300 | &["FileId(1) [38; 39) Other"], | ||
301 | ); | 384 | ); |
302 | } | 385 | } |
303 | 386 | ||
@@ -317,9 +400,8 @@ mod tests { | |||
317 | let refs = get_all_refs(code); | 400 | let refs = get_all_refs(code); |
318 | check_result( | 401 | check_result( |
319 | refs, | 402 | refs, |
320 | "spam RECORD_FIELD_DEF FileId(1) [66; 79) [70; 74)", | 403 | "spam RECORD_FIELD_DEF FileId(1) [66; 79) [70; 74) Other", |
321 | ReferenceKind::Other, | 404 | &["FileId(1) [152; 156) Other Read"], |
322 | &["FileId(1) [152; 156) Other"], | ||
323 | ); | 405 | ); |
324 | } | 406 | } |
325 | 407 | ||
@@ -334,7 +416,7 @@ mod tests { | |||
334 | "#; | 416 | "#; |
335 | 417 | ||
336 | let refs = get_all_refs(code); | 418 | let refs = get_all_refs(code); |
337 | check_result(refs, "f FN_DEF FileId(1) [88; 104) [91; 92)", ReferenceKind::Other, &[]); | 419 | check_result(refs, "f FN_DEF FileId(1) [88; 104) [91; 92) Other", &[]); |
338 | } | 420 | } |
339 | 421 | ||
340 | #[test] | 422 | #[test] |
@@ -349,7 +431,7 @@ mod tests { | |||
349 | "#; | 431 | "#; |
350 | 432 | ||
351 | let refs = get_all_refs(code); | 433 | let refs = get_all_refs(code); |
352 | check_result(refs, "B ENUM_VARIANT FileId(1) [83; 84) [83; 84)", ReferenceKind::Other, &[]); | 434 | check_result(refs, "B ENUM_VARIANT FileId(1) [83; 84) [83; 84) Other", &[]); |
353 | } | 435 | } |
354 | 436 | ||
355 | #[test] | 437 | #[test] |
@@ -390,8 +472,7 @@ mod tests { | |||
390 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 472 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
391 | check_result( | 473 | check_result( |
392 | refs, | 474 | refs, |
393 | "Foo STRUCT_DEF FileId(2) [16; 50) [27; 30)", | 475 | "Foo STRUCT_DEF FileId(2) [16; 50) [27; 30) Other", |
394 | ReferenceKind::Other, | ||
395 | &["FileId(1) [52; 55) StructLiteral", "FileId(3) [77; 80) StructLiteral"], | 476 | &["FileId(1) [52; 55) StructLiteral", "FileId(3) [77; 80) StructLiteral"], |
396 | ); | 477 | ); |
397 | } | 478 | } |
@@ -421,8 +502,7 @@ mod tests { | |||
421 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 502 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
422 | check_result( | 503 | check_result( |
423 | refs, | 504 | refs, |
424 | "foo SOURCE_FILE FileId(2) [0; 35)", | 505 | "foo SOURCE_FILE FileId(2) [0; 35) Other", |
425 | ReferenceKind::Other, | ||
426 | &["FileId(1) [13; 16) Other"], | 506 | &["FileId(1) [13; 16) Other"], |
427 | ); | 507 | ); |
428 | } | 508 | } |
@@ -451,8 +531,7 @@ mod tests { | |||
451 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 531 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
452 | check_result( | 532 | check_result( |
453 | refs, | 533 | refs, |
454 | "Foo STRUCT_DEF FileId(3) [0; 41) [18; 21)", | 534 | "Foo STRUCT_DEF FileId(3) [0; 41) [18; 21) Other", |
455 | ReferenceKind::Other, | ||
456 | &["FileId(2) [20; 23) Other", "FileId(2) [46; 49) StructLiteral"], | 535 | &["FileId(2) [20; 23) Other", "FileId(2) [46; 49) StructLiteral"], |
457 | ); | 536 | ); |
458 | } | 537 | } |
@@ -480,8 +559,7 @@ mod tests { | |||
480 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 559 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
481 | check_result( | 560 | check_result( |
482 | refs, | 561 | refs, |
483 | "quux FN_DEF FileId(1) [18; 34) [25; 29)", | 562 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
484 | ReferenceKind::Other, | ||
485 | &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], | 563 | &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], |
486 | ); | 564 | ); |
487 | 565 | ||
@@ -489,8 +567,7 @@ mod tests { | |||
489 | analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); | 567 | analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); |
490 | check_result( | 568 | check_result( |
491 | refs, | 569 | refs, |
492 | "quux FN_DEF FileId(1) [18; 34) [25; 29)", | 570 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
493 | ReferenceKind::Other, | ||
494 | &["FileId(3) [16; 20) Other"], | 571 | &["FileId(3) [16; 20) Other"], |
495 | ); | 572 | ); |
496 | } | 573 | } |
@@ -509,33 +586,99 @@ mod tests { | |||
509 | let refs = get_all_refs(code); | 586 | let refs = get_all_refs(code); |
510 | check_result( | 587 | check_result( |
511 | refs, | 588 | refs, |
512 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48)", | 589 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", |
513 | ReferenceKind::Other, | ||
514 | &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], | 590 | &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], |
515 | ); | 591 | ); |
516 | } | 592 | } |
517 | 593 | ||
594 | #[test] | ||
595 | fn test_basic_highlight_read_write() { | ||
596 | let code = r#" | ||
597 | fn foo() { | ||
598 | let i<|> = 0; | ||
599 | i = i + 1; | ||
600 | }"#; | ||
601 | |||
602 | let refs = get_all_refs(code); | ||
603 | check_result( | ||
604 | refs, | ||
605 | "i BIND_PAT FileId(1) [36; 37) Other Write", | ||
606 | &["FileId(1) [55; 56) Other Write", "FileId(1) [59; 60) Other Read"], | ||
607 | ); | ||
608 | } | ||
609 | |||
610 | #[test] | ||
611 | fn test_basic_highlight_field_read_write() { | ||
612 | let code = r#" | ||
613 | struct S { | ||
614 | f: u32, | ||
615 | } | ||
616 | |||
617 | fn foo() { | ||
618 | let mut s = S{f: 0}; | ||
619 | s.f<|> = 0; | ||
620 | }"#; | ||
621 | |||
622 | let refs = get_all_refs(code); | ||
623 | check_result( | ||
624 | refs, | ||
625 | "f RECORD_FIELD_DEF FileId(1) [32; 38) [32; 33) Other", | ||
626 | &["FileId(1) [96; 97) Other Read", "FileId(1) [117; 118) Other Write"], | ||
627 | ); | ||
628 | } | ||
629 | |||
630 | #[test] | ||
631 | fn test_basic_highlight_decl_no_write() { | ||
632 | let code = r#" | ||
633 | fn foo() { | ||
634 | let i<|>; | ||
635 | i = 1; | ||
636 | }"#; | ||
637 | |||
638 | let refs = get_all_refs(code); | ||
639 | check_result( | ||
640 | refs, | ||
641 | "i BIND_PAT FileId(1) [36; 37) Other", | ||
642 | &["FileId(1) [51; 52) Other Write"], | ||
643 | ); | ||
644 | } | ||
645 | |||
518 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | 646 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
519 | let (analysis, position) = single_file_with_position(text); | 647 | let (analysis, position) = single_file_with_position(text); |
520 | analysis.find_all_refs(position, None).unwrap().unwrap() | 648 | analysis.find_all_refs(position, None).unwrap().unwrap() |
521 | } | 649 | } |
522 | 650 | ||
523 | fn check_result( | 651 | fn check_result(res: ReferenceSearchResult, expected_decl: &str, expected_refs: &[&str]) { |
524 | res: ReferenceSearchResult, | ||
525 | expected_decl: &str, | ||
526 | decl_kind: ReferenceKind, | ||
527 | expected_refs: &[&str], | ||
528 | ) { | ||
529 | res.declaration().assert_match(expected_decl); | 652 | res.declaration().assert_match(expected_decl); |
530 | assert_eq!(res.declaration_kind, decl_kind); | ||
531 | |||
532 | assert_eq!(res.references.len(), expected_refs.len()); | 653 | assert_eq!(res.references.len(), expected_refs.len()); |
533 | res.references().iter().enumerate().for_each(|(i, r)| r.assert_match(expected_refs[i])); | 654 | res.references().iter().enumerate().for_each(|(i, r)| r.assert_match(expected_refs[i])); |
534 | } | 655 | } |
535 | 656 | ||
657 | impl Declaration { | ||
658 | fn debug_render(&self) -> String { | ||
659 | let mut s = format!("{} {:?}", self.nav.debug_render(), self.kind); | ||
660 | if let Some(access) = self.access { | ||
661 | s.push_str(&format!(" {:?}", access)); | ||
662 | } | ||
663 | s | ||
664 | } | ||
665 | |||
666 | fn assert_match(&self, expected: &str) { | ||
667 | let actual = self.debug_render(); | ||
668 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | ||
669 | } | ||
670 | } | ||
671 | |||
536 | impl Reference { | 672 | impl Reference { |
537 | fn debug_render(&self) -> String { | 673 | fn debug_render(&self) -> String { |
538 | format!("{:?} {:?} {:?}", self.file_range.file_id, self.file_range.range, self.kind) | 674 | let mut s = format!( |
675 | "{:?} {:?} {:?}", | ||
676 | self.file_range.file_id, self.file_range.range, self.kind | ||
677 | ); | ||
678 | if let Some(access) = self.access { | ||
679 | s.push_str(&format!(" {:?}", access)); | ||
680 | } | ||
681 | s | ||
539 | } | 682 | } |
540 | 683 | ||
541 | fn assert_match(&self, expected: &str) { | 684 | fn assert_match(&self, expected: &str) { |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index c260b51c4..562699b7c 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -9,7 +9,7 @@ use lsp_types::{ | |||
9 | use ra_ide::{ | 9 | use ra_ide::{ |
10 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, | 10 | translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, |
11 | FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, | 11 | FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, |
12 | NavigationTarget, RangeInfo, Severity, SourceChange, SourceFileEdit, | 12 | NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, |
13 | }; | 13 | }; |
14 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 14 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
15 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 15 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
@@ -53,6 +53,18 @@ impl Conv for SyntaxKind { | |||
53 | } | 53 | } |
54 | } | 54 | } |
55 | 55 | ||
56 | impl Conv for ReferenceAccess { | ||
57 | type Output = ::lsp_types::DocumentHighlightKind; | ||
58 | |||
59 | fn conv(self) -> Self::Output { | ||
60 | use lsp_types::DocumentHighlightKind; | ||
61 | match self { | ||
62 | ReferenceAccess::Read => DocumentHighlightKind::Read, | ||
63 | ReferenceAccess::Write => DocumentHighlightKind::Write, | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
56 | impl Conv for CompletionItemKind { | 68 | impl Conv for CompletionItemKind { |
57 | type Output = ::lsp_types::CompletionItemKind; | 69 | type Output = ::lsp_types::CompletionItemKind; |
58 | 70 | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index a5b6f48af..a592f0a12 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -536,18 +536,32 @@ pub fn handle_references( | |||
536 | 536 | ||
537 | let locations = if params.context.include_declaration { | 537 | let locations = if params.context.include_declaration { |
538 | refs.into_iter() | 538 | refs.into_iter() |
539 | .filter_map(|r| { | 539 | .filter_map(|reference| { |
540 | let line_index = world.analysis().file_line_index(r.file_range.file_id).ok()?; | 540 | let line_index = |
541 | to_location(r.file_range.file_id, r.file_range.range, &world, &line_index).ok() | 541 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; |
542 | to_location( | ||
543 | reference.file_range.file_id, | ||
544 | reference.file_range.range, | ||
545 | &world, | ||
546 | &line_index, | ||
547 | ) | ||
548 | .ok() | ||
542 | }) | 549 | }) |
543 | .collect() | 550 | .collect() |
544 | } else { | 551 | } else { |
545 | // Only iterate over the references if include_declaration was false | 552 | // Only iterate over the references if include_declaration was false |
546 | refs.references() | 553 | refs.references() |
547 | .iter() | 554 | .iter() |
548 | .filter_map(|r| { | 555 | .filter_map(|reference| { |
549 | let line_index = world.analysis().file_line_index(r.file_range.file_id).ok()?; | 556 | let line_index = |
550 | to_location(r.file_range.file_id, r.file_range.range, &world, &line_index).ok() | 557 | world.analysis().file_line_index(reference.file_range.file_id).ok()?; |
558 | to_location( | ||
559 | reference.file_range.file_id, | ||
560 | reference.file_range.range, | ||
561 | &world, | ||
562 | &line_index, | ||
563 | ) | ||
564 | .ok() | ||
551 | }) | 565 | }) |
552 | .collect() | 566 | .collect() |
553 | }; | 567 | }; |
@@ -836,10 +850,10 @@ pub fn handle_document_highlight( | |||
836 | 850 | ||
837 | Ok(Some( | 851 | Ok(Some( |
838 | refs.into_iter() | 852 | refs.into_iter() |
839 | .filter(|r| r.file_range.file_id == file_id) | 853 | .filter(|reference| reference.file_range.file_id == file_id) |
840 | .map(|r| DocumentHighlight { | 854 | .map(|reference| DocumentHighlight { |
841 | range: r.file_range.range.conv_with(&line_index), | 855 | range: reference.file_range.range.conv_with(&line_index), |
842 | kind: None, | 856 | kind: reference.access.map(|it| it.conv()), |
843 | }) | 857 | }) |
844 | .collect(), | 858 | .collect(), |
845 | )) | 859 | )) |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 23b6aa901..3dfecfe76 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -144,6 +144,7 @@ impl BinOp { | |||
144 | } | 144 | } |
145 | } | 145 | } |
146 | } | 146 | } |
147 | |||
147 | impl ast::BinExpr { | 148 | impl ast::BinExpr { |
148 | pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> { | 149 | pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> { |
149 | self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| { | 150 | self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| { |