diff options
-rw-r--r-- | crates/ra_ide/src/references.rs | 101 |
1 files changed, 53 insertions, 48 deletions
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 7d31ef6bd..5b8ed370c 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -55,7 +55,7 @@ pub enum ReferenceKind { | |||
55 | Other, | 55 | Other, |
56 | } | 56 | } |
57 | 57 | ||
58 | #[derive(Debug, Clone, PartialEq)] | 58 | #[derive(Debug, Copy, Clone, PartialEq)] |
59 | pub enum ReferenceAccess { | 59 | pub enum ReferenceAccess { |
60 | Read, | 60 | Read, |
61 | Write, | 61 | Write, |
@@ -225,49 +225,41 @@ fn process_definition( | |||
225 | } | 225 | } |
226 | 226 | ||
227 | fn access_mode(kind: NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | 227 | fn access_mode(kind: NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { |
228 | // Only Locals and Fields have accesses for now. | ||
228 | match kind { | 229 | match kind { |
229 | NameKind::Local(_) | NameKind::Field(_) => { | 230 | NameKind::Local(_) | NameKind::Field(_) => {} |
230 | //LetExpr or BinExpr | 231 | _ => return None, |
231 | name_ref.syntax().ancestors().find_map(|node| { | 232 | }; |
232 | match_ast! { | ||
233 | match (node) { | ||
234 | ast::BinExpr(expr) => { | ||
235 | if expr.op_kind()?.is_assignment() { | ||
236 | // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals). | ||
237 | // FIXME: This is not terribly accurate. | ||
238 | if let Some(lhs) = expr.lhs() { | ||
239 | if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() { | ||
240 | return Some(ReferenceAccess::Write); | ||
241 | } else if name_ref.syntax().text_range().is_subrange(&lhs.syntax().text_range()) { | ||
242 | return Some(ReferenceAccess::Read); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | // If the variable is on the RHS then it's a Read. | ||
247 | if let Some(rhs) = expr.rhs() { | ||
248 | if name_ref.syntax().text_range().is_subrange(&rhs.syntax().text_range()) { | ||
249 | return Some(ReferenceAccess::Read); | ||
250 | } | ||
251 | } | ||
252 | } | ||
253 | 233 | ||
254 | // Cannot determine access | 234 | let mode = name_ref.syntax().ancestors().find_map(|node| { |
255 | None | 235 | match_ast! { |
256 | }, | 236 | match (node) { |
257 | _ => {None} | 237 | ast::BinExpr(expr) => { |
238 | if expr.op_kind()?.is_assignment() { | ||
239 | // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals). | ||
240 | // FIXME: This is not terribly accurate. | ||
241 | if let Some(lhs) = expr.lhs() { | ||
242 | if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() { | ||
243 | return Some(ReferenceAccess::Write); | ||
244 | } | ||
245 | } | ||
258 | } | 246 | } |
259 | } | 247 | return Some(ReferenceAccess::Read); |
260 | }) | 248 | }, |
249 | _ => {None} | ||
250 | } | ||
261 | } | 251 | } |
262 | _ => None, | 252 | }); |
263 | } | 253 | |
254 | // Default Locals and Fields to read | ||
255 | mode.or(Some(ReferenceAccess::Read)) | ||
264 | } | 256 | } |
265 | 257 | ||
266 | #[cfg(test)] | 258 | #[cfg(test)] |
267 | mod tests { | 259 | mod tests { |
268 | use crate::{ | 260 | use crate::{ |
269 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, | 261 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, |
270 | Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, | 262 | Reference, ReferenceKind, ReferenceSearchResult, SearchScope, |
271 | }; | 263 | }; |
272 | 264 | ||
273 | #[test] | 265 | #[test] |
@@ -314,10 +306,10 @@ mod tests { | |||
314 | "i BIND_PAT FileId(1) [33; 34)", | 306 | "i BIND_PAT FileId(1) [33; 34)", |
315 | ReferenceKind::Other, | 307 | ReferenceKind::Other, |
316 | &[ | 308 | &[ |
317 | "FileId(1) [67; 68) Other", | 309 | "FileId(1) [67; 68) Other Write", |
318 | "FileId(1) [71; 72) Other", | 310 | "FileId(1) [71; 72) Other Read", |
319 | "FileId(1) [101; 102) Other", | 311 | "FileId(1) [101; 102) Other Write", |
320 | "FileId(1) [127; 128) Other", | 312 | "FileId(1) [127; 128) Other Write", |
321 | ], | 313 | ], |
322 | ); | 314 | ); |
323 | } | 315 | } |
@@ -334,7 +326,7 @@ mod tests { | |||
334 | refs, | 326 | refs, |
335 | "i BIND_PAT FileId(1) [12; 13)", | 327 | "i BIND_PAT FileId(1) [12; 13)", |
336 | ReferenceKind::Other, | 328 | ReferenceKind::Other, |
337 | &["FileId(1) [38; 39) Other"], | 329 | &["FileId(1) [38; 39) Other Read"], |
338 | ); | 330 | ); |
339 | } | 331 | } |
340 | 332 | ||
@@ -350,7 +342,7 @@ mod tests { | |||
350 | refs, | 342 | refs, |
351 | "i BIND_PAT FileId(1) [12; 13)", | 343 | "i BIND_PAT FileId(1) [12; 13)", |
352 | ReferenceKind::Other, | 344 | ReferenceKind::Other, |
353 | &["FileId(1) [38; 39) Other"], | 345 | &["FileId(1) [38; 39) Other Read"], |
354 | ); | 346 | ); |
355 | } | 347 | } |
356 | 348 | ||
@@ -372,7 +364,7 @@ mod tests { | |||
372 | refs, | 364 | refs, |
373 | "spam RECORD_FIELD_DEF FileId(1) [66; 79) [70; 74)", | 365 | "spam RECORD_FIELD_DEF FileId(1) [66; 79) [70; 74)", |
374 | ReferenceKind::Other, | 366 | ReferenceKind::Other, |
375 | &["FileId(1) [152; 156) Other"], | 367 | &["FileId(1) [152; 156) Other Read"], |
376 | ); | 368 | ); |
377 | } | 369 | } |
378 | 370 | ||
@@ -577,9 +569,12 @@ mod tests { | |||
577 | }"#; | 569 | }"#; |
578 | 570 | ||
579 | let refs = get_all_refs(code); | 571 | let refs = get_all_refs(code); |
580 | assert_eq!(refs.len(), 3); | 572 | check_result( |
581 | assert_eq!(refs.references[0].access, Some(ReferenceAccess::Write)); | 573 | refs, |
582 | assert_eq!(refs.references[1].access, Some(ReferenceAccess::Read)); | 574 | "i BIND_PAT FileId(1) [36; 37)", |
575 | ReferenceKind::Other, | ||
576 | &["FileId(1) [55; 56) Other Write", "FileId(1) [59; 60) Other Read"], | ||
577 | ); | ||
583 | } | 578 | } |
584 | 579 | ||
585 | #[test] | 580 | #[test] |
@@ -595,9 +590,12 @@ mod tests { | |||
595 | }"#; | 590 | }"#; |
596 | 591 | ||
597 | let refs = get_all_refs(code); | 592 | let refs = get_all_refs(code); |
598 | assert_eq!(refs.len(), 3); | 593 | check_result( |
599 | //assert_eq!(refs.references[0].access, Some(ReferenceAccess::Write)); | 594 | refs, |
600 | assert_eq!(refs.references[1].access, Some(ReferenceAccess::Write)); | 595 | "f RECORD_FIELD_DEF FileId(1) [32; 38) [32; 33)", |
596 | ReferenceKind::Other, | ||
597 | &["FileId(1) [96; 97) Other Read", "FileId(1) [117; 118) Other Write"], | ||
598 | ); | ||
601 | } | 599 | } |
602 | 600 | ||
603 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | 601 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
@@ -620,7 +618,14 @@ mod tests { | |||
620 | 618 | ||
621 | impl Reference { | 619 | impl Reference { |
622 | fn debug_render(&self) -> String { | 620 | fn debug_render(&self) -> String { |
623 | format!("{:?} {:?} {:?}", self.file_range.file_id, self.file_range.range, self.kind) | 621 | let mut s = format!( |
622 | "{:?} {:?} {:?}", | ||
623 | self.file_range.file_id, self.file_range.range, self.kind | ||
624 | ); | ||
625 | if let Some(access) = self.access { | ||
626 | s.push_str(&format!(" {:?}", access)); | ||
627 | } | ||
628 | s | ||
624 | } | 629 | } |
625 | 630 | ||
626 | fn assert_match(&self, expected: &str) { | 631 | fn assert_match(&self, expected: &str) { |