aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/references
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-12-17 11:00:39 +0000
committerGitHub <[email protected]>2020-12-17 11:00:39 +0000
commitd641bccb0ffec543fa444ba523e8d4b63078fa53 (patch)
tree6695e5ebfeb9563bf5358a0cb89abe8456ac9172 /crates/ide/src/references
parentd21f5f7d6e13b93d64235f13ac18e447af8d59e4 (diff)
parent55faa2daa3fc8bd213038a012b1c5e9ad5fd3736 (diff)
Merge #6907
6907: Lifetime reference search r=matklad a=Veykril PR #6787 but rewritten to make use of the HIR now. This only applies to Lifetimes, not labels. Also Higher-Ranked Trait Bounds aren't supported yet, but I feel like this PR is big enough as is which is why I left them out after noticing I forgot about them. Supporting renaming required slight changes in the renaming module as lifetime names aren't allowed for anything but lifetimes(and labels) and vice versa for normal names. Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide/src/references')
-rw-r--r--crates/ide/src/references/rename.rs104
1 files changed, 98 insertions, 6 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 44081f210..56e923841 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -35,6 +35,29 @@ impl fmt::Display for RenameError {
35 35
36impl Error for RenameError {} 36impl Error for RenameError {}
37 37
38pub(crate) fn prepare_rename(
39 db: &RootDatabase,
40 position: FilePosition,
41) -> Result<RangeInfo<()>, RenameError> {
42 let sema = Semantics::new(db);
43 let source_file = sema.parse(position.file_id);
44 let syntax = source_file.syntax();
45 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
46 rename_mod(&sema, position, module, "dummy")
47 } else if let Some(self_token) =
48 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
49 {
50 rename_self_to_param(&sema, position, self_token, "dummy")
51 } else {
52 let range = match find_all_refs(&sema, position, None) {
53 Some(RangeInfo { range, .. }) => range,
54 None => return Err(RenameError("No references found at position".to_string())),
55 };
56 Ok(RangeInfo::new(range, SourceChange::from(vec![])))
57 }
58 .map(|info| RangeInfo::new(info.range, ()))
59}
60
38pub(crate) fn rename( 61pub(crate) fn rename(
39 db: &RootDatabase, 62 db: &RootDatabase,
40 position: FilePosition, 63 position: FilePosition,
@@ -49,11 +72,18 @@ pub(crate) fn rename_with_semantics(
49 position: FilePosition, 72 position: FilePosition,
50 new_name: &str, 73 new_name: &str,
51) -> Result<RangeInfo<SourceChange>, RenameError> { 74) -> Result<RangeInfo<SourceChange>, RenameError> {
52 match lex_single_syntax_kind(new_name) { 75 let is_lifetime_name = match lex_single_syntax_kind(new_name) {
53 Some(res) => match res { 76 Some(res) => match res {
54 (SyntaxKind::IDENT, _) => (), 77 (SyntaxKind::IDENT, _) => false,
55 (SyntaxKind::UNDERSCORE, _) => (), 78 (SyntaxKind::UNDERSCORE, _) => false,
56 (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position), 79 (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
80 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true,
81 (SyntaxKind::LIFETIME_IDENT, _) => {
82 return Err(RenameError(format!(
83 "Invalid name `{0}`: Cannot rename lifetime to {0}",
84 new_name
85 )))
86 }
57 (_, Some(syntax_error)) => { 87 (_, Some(syntax_error)) => {
58 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error))) 88 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
59 } 89 }
@@ -62,18 +92,21 @@ pub(crate) fn rename_with_semantics(
62 } 92 }
63 }, 93 },
64 None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))), 94 None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
65 } 95 };
66 96
67 let source_file = sema.parse(position.file_id); 97 let source_file = sema.parse(position.file_id);
68 let syntax = source_file.syntax(); 98 let syntax = source_file.syntax();
69 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 99 // this is here to prevent lifetime renames from happening on modules and self
100 if is_lifetime_name {
101 rename_reference(&sema, position, new_name, is_lifetime_name)
102 } else if let Some(module) = find_module_at_offset(&sema, position, syntax) {
70 rename_mod(&sema, position, module, new_name) 103 rename_mod(&sema, position, module, new_name)
71 } else if let Some(self_token) = 104 } else if let Some(self_token) =
72 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 105 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
73 { 106 {
74 rename_self_to_param(&sema, position, self_token, new_name) 107 rename_self_to_param(&sema, position, self_token, new_name)
75 } else { 108 } else {
76 rename_reference(&sema, position, new_name) 109 rename_reference(&sema, position, new_name, is_lifetime_name)
77 } 110 }
78} 111}
79 112
@@ -355,12 +388,26 @@ fn rename_reference(
355 sema: &Semantics<RootDatabase>, 388 sema: &Semantics<RootDatabase>,
356 position: FilePosition, 389 position: FilePosition,
357 new_name: &str, 390 new_name: &str,
391 is_lifetime_name: bool,
358) -> Result<RangeInfo<SourceChange>, RenameError> { 392) -> Result<RangeInfo<SourceChange>, RenameError> {
359 let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { 393 let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) {
360 Some(range_info) => range_info, 394 Some(range_info) => range_info,
361 None => return Err(RenameError("No references found at position".to_string())), 395 None => return Err(RenameError("No references found at position".to_string())),
362 }; 396 };
363 397
398 match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) {
399 (true, false) => {
400 return Err(RenameError(format!(
401 "Invalid name `{}`: not a lifetime identifier",
402 new_name
403 )))
404 }
405 (false, true) => {
406 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
407 }
408 _ => (),
409 }
410
364 let edit = refs 411 let edit = refs
365 .into_iter() 412 .into_iter()
366 .map(|reference| source_edit_from_reference(sema, reference, new_name)) 413 .map(|reference| source_edit_from_reference(sema, reference, new_name))
@@ -465,6 +512,24 @@ mod tests {
465 } 512 }
466 513
467 #[test] 514 #[test]
515 fn test_rename_to_invalid_identifier_lifetime() {
516 check(
517 "'foo",
518 r#"fn main() { let i<|> = 1; }"#,
519 "error: Invalid name `'foo`: not an identifier",
520 );
521 }
522
523 #[test]
524 fn test_rename_to_invalid_identifier_lifetime2() {
525 check(
526 "foo",
527 r#"fn main<'a>(_: &'a<|> ()) {}"#,
528 "error: Invalid name `foo`: not a lifetime identifier",
529 );
530 }
531
532 #[test]
468 fn test_rename_for_local() { 533 fn test_rename_for_local() {
469 check( 534 check(
470 "k", 535 "k",
@@ -1396,4 +1461,31 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1396"#, 1461"#,
1397 ) 1462 )
1398 } 1463 }
1464
1465 #[test]
1466 fn test_rename_lifetimes() {
1467 check(
1468 "'yeeee",
1469 r#"
1470trait Foo<'a> {
1471 fn foo() -> &'a ();
1472}
1473impl<'a> Foo<'a> for &'a () {
1474 fn foo() -> &'a<|> () {
1475 unimplemented!()
1476 }
1477}
1478"#,
1479 r#"
1480trait Foo<'a> {
1481 fn foo() -> &'a ();
1482}
1483impl<'yeeee> Foo<'yeeee> for &'yeeee () {
1484 fn foo() -> &'yeeee () {
1485 unimplemented!()
1486 }
1487}
1488"#,
1489 )
1490 }
1399} 1491}