diff options
-rw-r--r-- | crates/hir/src/lib.rs | 14 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 165 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 47 |
3 files changed, 215 insertions, 11 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index debc3ee62..68ac8400c 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -1450,6 +1450,20 @@ impl AssocItem { | |||
1450 | _ => None, | 1450 | _ => None, |
1451 | } | 1451 | } |
1452 | } | 1452 | } |
1453 | |||
1454 | pub fn containing_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1455 | match self.container(db) { | ||
1456 | AssocItemContainer::Impl(i) => i.trait_(db), | ||
1457 | _ => None, | ||
1458 | } | ||
1459 | } | ||
1460 | |||
1461 | pub fn containing_trait_or_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1462 | match self.container(db) { | ||
1463 | AssocItemContainer::Trait(t) => Some(t), | ||
1464 | AssocItemContainer::Impl(i) => i.trait_(db), | ||
1465 | } | ||
1466 | } | ||
1453 | } | 1467 | } |
1454 | 1468 | ||
1455 | impl HasVisibility for AssocItem { | 1469 | impl HasVisibility for AssocItem { |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 2a4a1c3c8..7dfc5043e 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -239,7 +239,7 @@ fn rename_mod( | |||
239 | 239 | ||
240 | fn rename_reference( | 240 | fn rename_reference( |
241 | sema: &Semantics<RootDatabase>, | 241 | sema: &Semantics<RootDatabase>, |
242 | def: Definition, | 242 | mut def: Definition, |
243 | new_name: &str, | 243 | new_name: &str, |
244 | ) -> RenameResult<SourceChange> { | 244 | ) -> RenameResult<SourceChange> { |
245 | let ident_kind = check_identifier(new_name)?; | 245 | let ident_kind = check_identifier(new_name)?; |
@@ -285,7 +285,38 @@ fn rename_reference( | |||
285 | } | 285 | } |
286 | } | 286 | } |
287 | 287 | ||
288 | def = match def { | ||
289 | // HACK: resolve trait impl items to the item def of the trait definition | ||
290 | // so that we properly resolve all trait item references | ||
291 | Definition::ModuleDef(mod_def) => mod_def | ||
292 | .as_assoc_item(sema.db) | ||
293 | .and_then(|it| it.containing_trait_impl(sema.db)) | ||
294 | .and_then(|it| { | ||
295 | it.items(sema.db).into_iter().find_map(|it| match (it, mod_def) { | ||
296 | (hir::AssocItem::Function(trait_func), ModuleDef::Function(func)) | ||
297 | if trait_func.name(sema.db) == func.name(sema.db) => | ||
298 | { | ||
299 | Some(Definition::ModuleDef(ModuleDef::Function(trait_func))) | ||
300 | } | ||
301 | (hir::AssocItem::Const(trait_konst), ModuleDef::Const(konst)) | ||
302 | if trait_konst.name(sema.db) == konst.name(sema.db) => | ||
303 | { | ||
304 | Some(Definition::ModuleDef(ModuleDef::Const(trait_konst))) | ||
305 | } | ||
306 | ( | ||
307 | hir::AssocItem::TypeAlias(trait_type_alias), | ||
308 | ModuleDef::TypeAlias(type_alias), | ||
309 | ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => { | ||
310 | Some(Definition::ModuleDef(ModuleDef::TypeAlias(trait_type_alias))) | ||
311 | } | ||
312 | _ => None, | ||
313 | }) | ||
314 | }) | ||
315 | .unwrap_or(def), | ||
316 | _ => def, | ||
317 | }; | ||
288 | let usages = def.usages(sema).all(); | 318 | let usages = def.usages(sema).all(); |
319 | |||
289 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | 320 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { |
290 | cov_mark::hit!(rename_underscore_multiple); | 321 | cov_mark::hit!(rename_underscore_multiple); |
291 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | 322 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); |
@@ -1938,4 +1969,136 @@ use Bar$0; | |||
1938 | "error: Renaming aliases is currently unsupported", | 1969 | "error: Renaming aliases is currently unsupported", |
1939 | ); | 1970 | ); |
1940 | } | 1971 | } |
1972 | |||
1973 | #[test] | ||
1974 | fn test_rename_trait_method() { | ||
1975 | let res = r" | ||
1976 | trait Foo { | ||
1977 | fn foo(&self) { | ||
1978 | self.foo(); | ||
1979 | } | ||
1980 | } | ||
1981 | |||
1982 | impl Foo for () { | ||
1983 | fn foo(&self) { | ||
1984 | self.foo(); | ||
1985 | } | ||
1986 | }"; | ||
1987 | check( | ||
1988 | "foo", | ||
1989 | r#" | ||
1990 | trait Foo { | ||
1991 | fn bar$0(&self) { | ||
1992 | self.bar(); | ||
1993 | } | ||
1994 | } | ||
1995 | |||
1996 | impl Foo for () { | ||
1997 | fn bar(&self) { | ||
1998 | self.bar(); | ||
1999 | } | ||
2000 | }"#, | ||
2001 | res, | ||
2002 | ); | ||
2003 | check( | ||
2004 | "foo", | ||
2005 | r#" | ||
2006 | trait Foo { | ||
2007 | fn bar(&self) { | ||
2008 | self.bar$0(); | ||
2009 | } | ||
2010 | } | ||
2011 | |||
2012 | impl Foo for () { | ||
2013 | fn bar(&self) { | ||
2014 | self.bar(); | ||
2015 | } | ||
2016 | }"#, | ||
2017 | res, | ||
2018 | ); | ||
2019 | check( | ||
2020 | "foo", | ||
2021 | r#" | ||
2022 | trait Foo { | ||
2023 | fn bar(&self) { | ||
2024 | self.bar(); | ||
2025 | } | ||
2026 | } | ||
2027 | |||
2028 | impl Foo for () { | ||
2029 | fn bar$0(&self) { | ||
2030 | self.bar(); | ||
2031 | } | ||
2032 | }"#, | ||
2033 | res, | ||
2034 | ); | ||
2035 | check( | ||
2036 | "foo", | ||
2037 | r#" | ||
2038 | trait Foo { | ||
2039 | fn bar(&self) { | ||
2040 | self.bar(); | ||
2041 | } | ||
2042 | } | ||
2043 | |||
2044 | impl Foo for () { | ||
2045 | fn bar(&self) { | ||
2046 | self.bar$0(); | ||
2047 | } | ||
2048 | }"#, | ||
2049 | res, | ||
2050 | ); | ||
2051 | } | ||
2052 | |||
2053 | #[test] | ||
2054 | fn test_rename_trait_const() { | ||
2055 | let res = r" | ||
2056 | trait Foo { | ||
2057 | const FOO: (); | ||
2058 | } | ||
2059 | |||
2060 | impl Foo for () { | ||
2061 | const FOO: (); | ||
2062 | } | ||
2063 | fn f() { <()>::FOO; }"; | ||
2064 | check( | ||
2065 | "FOO", | ||
2066 | r#" | ||
2067 | trait Foo { | ||
2068 | const BAR$0: (); | ||
2069 | } | ||
2070 | |||
2071 | impl Foo for () { | ||
2072 | const BAR: (); | ||
2073 | } | ||
2074 | fn f() { <()>::BAR; }"#, | ||
2075 | res, | ||
2076 | ); | ||
2077 | check( | ||
2078 | "FOO", | ||
2079 | r#" | ||
2080 | trait Foo { | ||
2081 | const BAR: (); | ||
2082 | } | ||
2083 | |||
2084 | impl Foo for () { | ||
2085 | const BAR$0: (); | ||
2086 | } | ||
2087 | fn f() { <()>::BAR; }"#, | ||
2088 | res, | ||
2089 | ); | ||
2090 | check( | ||
2091 | "FOO", | ||
2092 | r#" | ||
2093 | trait Foo { | ||
2094 | const BAR: (); | ||
2095 | } | ||
2096 | |||
2097 | impl Foo for () { | ||
2098 | const BAR: (); | ||
2099 | } | ||
2100 | fn f() { <()>::BAR$0; }"#, | ||
2101 | res, | ||
2102 | ); | ||
2103 | } | ||
1941 | } | 2104 | } |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 67840602b..8152630f5 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -8,7 +8,8 @@ use std::{convert::TryInto, mem}; | |||
8 | 8 | ||
9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; | 9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; |
10 | use hir::{ | 10 | use hir::{ |
11 | DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, Visibility, | 11 | AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, |
12 | Visibility, | ||
12 | }; | 13 | }; |
13 | use once_cell::unsync::Lazy; | 14 | use once_cell::unsync::Lazy; |
14 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
@@ -303,13 +304,13 @@ impl Definition { | |||
303 | } | 304 | } |
304 | } | 305 | } |
305 | 306 | ||
306 | pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { | 307 | pub fn usages<'a>(self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { |
307 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: None } | 308 | FindUsages { def: self, sema, scope: None, include_self_kw_refs: None } |
308 | } | 309 | } |
309 | } | 310 | } |
310 | 311 | ||
311 | pub struct FindUsages<'a> { | 312 | pub struct FindUsages<'a> { |
312 | def: &'a Definition, | 313 | def: Definition, |
313 | sema: &'a Semantics<'a, RootDatabase>, | 314 | sema: &'a Semantics<'a, RootDatabase>, |
314 | scope: Option<SearchScope>, | 315 | scope: Option<SearchScope>, |
315 | include_self_kw_refs: Option<hir::Type>, | 316 | include_self_kw_refs: Option<hir::Type>, |
@@ -318,7 +319,7 @@ pub struct FindUsages<'a> { | |||
318 | impl<'a> FindUsages<'a> { | 319 | impl<'a> FindUsages<'a> { |
319 | /// Enable searching for `Self` when the definition is a type. | 320 | /// Enable searching for `Self` when the definition is a type. |
320 | pub fn include_self_refs(mut self) -> FindUsages<'a> { | 321 | pub fn include_self_refs(mut self) -> FindUsages<'a> { |
321 | self.include_self_kw_refs = def_to_ty(self.sema, self.def); | 322 | self.include_self_kw_refs = def_to_ty(self.sema, &self.def); |
322 | self | 323 | self |
323 | } | 324 | } |
324 | 325 | ||
@@ -445,7 +446,7 @@ impl<'a> FindUsages<'a> { | |||
445 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | 446 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
446 | ) -> bool { | 447 | ) -> bool { |
447 | match NameRefClass::classify_lifetime(self.sema, lifetime) { | 448 | match NameRefClass::classify_lifetime(self.sema, lifetime) { |
448 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 449 | Some(NameRefClass::Definition(def)) if def == self.def => { |
449 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); | 450 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); |
450 | let reference = FileReference { | 451 | let reference = FileReference { |
451 | range, | 452 | range, |
@@ -464,7 +465,7 @@ impl<'a> FindUsages<'a> { | |||
464 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | 465 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
465 | ) -> bool { | 466 | ) -> bool { |
466 | match NameRefClass::classify(self.sema, &name_ref) { | 467 | match NameRefClass::classify(self.sema, &name_ref) { |
467 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 468 | Some(NameRefClass::Definition(def)) if def == self.def => { |
468 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 469 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
469 | let reference = FileReference { | 470 | let reference = FileReference { |
470 | range, | 471 | range, |
@@ -489,10 +490,10 @@ impl<'a> FindUsages<'a> { | |||
489 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 490 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
490 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 491 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
491 | let access = match self.def { | 492 | let access = match self.def { |
492 | Definition::Field(_) if &field == self.def => { | 493 | Definition::Field(_) if field == self.def => { |
493 | reference_access(&field, &name_ref) | 494 | reference_access(&field, &name_ref) |
494 | } | 495 | } |
495 | Definition::Local(l) if &local == l => { | 496 | Definition::Local(l) if local == l => { |
496 | reference_access(&Definition::Local(local), &name_ref) | 497 | reference_access(&Definition::Local(local), &name_ref) |
497 | } | 498 | } |
498 | _ => return false, | 499 | _ => return false, |
@@ -513,7 +514,7 @@ impl<'a> FindUsages<'a> { | |||
513 | match NameClass::classify(self.sema, name) { | 514 | match NameClass::classify(self.sema, name) { |
514 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) | 515 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) |
515 | if matches!( | 516 | if matches!( |
516 | self.def, Definition::Field(_) if &field_ref == self.def | 517 | self.def, Definition::Field(_) if field_ref == self.def |
517 | ) => | 518 | ) => |
518 | { | 519 | { |
519 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | 520 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
@@ -525,12 +526,38 @@ impl<'a> FindUsages<'a> { | |||
525 | }; | 526 | }; |
526 | sink(file_id, reference) | 527 | sink(file_id, reference) |
527 | } | 528 | } |
528 | Some(NameClass::ConstReference(def)) if *self.def == def => { | 529 | Some(NameClass::ConstReference(def)) if self.def == def => { |
529 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | 530 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
530 | let reference = | 531 | let reference = |
531 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; | 532 | FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; |
532 | sink(file_id, reference) | 533 | sink(file_id, reference) |
533 | } | 534 | } |
535 | // Resolve trait impl function definitions to the trait definition's version if self.def is the trait definition's | ||
536 | Some(NameClass::Definition(Definition::ModuleDef(mod_def))) => { | ||
537 | /* poor man's try block */ | ||
538 | (|| { | ||
539 | let this = match self.def { | ||
540 | Definition::ModuleDef(this) if this != mod_def => this, | ||
541 | _ => return None, | ||
542 | }; | ||
543 | let this_trait = this | ||
544 | .as_assoc_item(self.sema.db)? | ||
545 | .containing_trait_or_trait_impl(self.sema.db)?; | ||
546 | let trait_ = mod_def | ||
547 | .as_assoc_item(self.sema.db)? | ||
548 | .containing_trait_or_trait_impl(self.sema.db)?; | ||
549 | (trait_ == this_trait).then(|| { | ||
550 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | ||
551 | let reference = FileReference { | ||
552 | range, | ||
553 | name: ast::NameLike::Name(name.clone()), | ||
554 | access: None, | ||
555 | }; | ||
556 | sink(file_id, reference) | ||
557 | }) | ||
558 | })() | ||
559 | .unwrap_or(false) | ||
560 | } | ||
534 | _ => false, | 561 | _ => false, |
535 | } | 562 | } |
536 | } | 563 | } |