aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/lib.rs14
-rw-r--r--crates/ide/src/references/rename.rs165
-rw-r--r--crates/ide_db/src/search.rs47
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
1455impl HasVisibility for AssocItem { 1469impl 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
240fn rename_reference( 240fn 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"
1976trait Foo {
1977 fn foo(&self) {
1978 self.foo();
1979 }
1980}
1981
1982impl Foo for () {
1983 fn foo(&self) {
1984 self.foo();
1985 }
1986}";
1987 check(
1988 "foo",
1989 r#"
1990trait Foo {
1991 fn bar$0(&self) {
1992 self.bar();
1993 }
1994}
1995
1996impl Foo for () {
1997 fn bar(&self) {
1998 self.bar();
1999 }
2000}"#,
2001 res,
2002 );
2003 check(
2004 "foo",
2005 r#"
2006trait Foo {
2007 fn bar(&self) {
2008 self.bar$0();
2009 }
2010}
2011
2012impl Foo for () {
2013 fn bar(&self) {
2014 self.bar();
2015 }
2016}"#,
2017 res,
2018 );
2019 check(
2020 "foo",
2021 r#"
2022trait Foo {
2023 fn bar(&self) {
2024 self.bar();
2025 }
2026}
2027
2028impl Foo for () {
2029 fn bar$0(&self) {
2030 self.bar();
2031 }
2032}"#,
2033 res,
2034 );
2035 check(
2036 "foo",
2037 r#"
2038trait Foo {
2039 fn bar(&self) {
2040 self.bar();
2041 }
2042}
2043
2044impl 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"
2056trait Foo {
2057 const FOO: ();
2058}
2059
2060impl Foo for () {
2061 const FOO: ();
2062}
2063fn f() { <()>::FOO; }";
2064 check(
2065 "FOO",
2066 r#"
2067trait Foo {
2068 const BAR$0: ();
2069}
2070
2071impl Foo for () {
2072 const BAR: ();
2073}
2074fn f() { <()>::BAR; }"#,
2075 res,
2076 );
2077 check(
2078 "FOO",
2079 r#"
2080trait Foo {
2081 const BAR: ();
2082}
2083
2084impl Foo for () {
2085 const BAR$0: ();
2086}
2087fn f() { <()>::BAR; }"#,
2088 res,
2089 );
2090 check(
2091 "FOO",
2092 r#"
2093trait Foo {
2094 const BAR: ();
2095}
2096
2097impl Foo for () {
2098 const BAR: ();
2099}
2100fn 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
9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; 9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
10use hir::{ 10use hir::{
11 DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, Visibility, 11 AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics,
12 Visibility,
12}; 13};
13use once_cell::unsync::Lazy; 14use once_cell::unsync::Lazy;
14use rustc_hash::FxHashMap; 15use 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
311pub struct FindUsages<'a> { 312pub 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> {
318impl<'a> FindUsages<'a> { 319impl<'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 }