Skip to content

Commit c53dfb6

Browse files
committed
Preserve self, static, and parent keywords during class rename
1 parent af8ae58 commit c53dfb6

3 files changed

Lines changed: 91 additions & 0 deletions

File tree

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5555

5656
### Fixed
5757

58+
- **Rename class preserves `self`, `static`, and `parent` keywords.** Renaming a class no longer replaces occurrences of `self::`, `static::`, or `parent::` with the new class name.
5859
- **Rename propagates into closures and arrow functions.** Renaming a variable now follows explicit `use ($var)` captures into closure bodies and implicit captures into arrow function bodies, instead of leaving those occurrences unchanged.
5960
- **Spurious function auto-imports.** Import statements like `use function is_array;` were misidentified as function declarations, polluting the completion list with phantom entries that inserted incorrect imports.
6061
- **Duplicate `use function` insertion.** Accepting a function completion no longer inserts a `use function` statement when the exact import already exists in the file.

src/rename/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,12 @@ impl Backend {
444444
continue;
445445
}
446446

447+
// self, static, and parent are keywords that should not
448+
// be renamed when the class they resolve to is renamed.
449+
if matches!(source_text.as_str(), "self" | "static" | "parent") {
450+
continue;
451+
}
452+
447453
if source_text.contains('\\') {
448454
// This is an inline FQN reference (e.g. `\Ns\Foo`).
449455
// Replace only the last segment.

src/rename/tests.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,90 @@ async fn rename_class_with_collision_adds_alias() {
10631063
);
10641064
}
10651065

1066+
#[tokio::test]
1067+
async fn rename_class_does_not_rename_self_static_parent() {
1068+
let backend = Backend::new_test();
1069+
let uri = Url::parse("file:///test.php").unwrap();
1070+
let text = concat!(
1071+
"<?php\n",
1072+
"class Foo {\n",
1073+
" public const BAR = 1;\n",
1074+
" public static function create(): self {\n",
1075+
" return new self();\n",
1076+
" }\n",
1077+
" public function check(): bool {\n",
1078+
" return self::BAR === static::BAR;\n",
1079+
" }\n",
1080+
"}\n",
1081+
"class Bar extends Foo {\n",
1082+
" public function parentRef(): void {\n",
1083+
" parent::create();\n",
1084+
" }\n",
1085+
"}\n",
1086+
"function demo(Foo $f): void {\n",
1087+
" $obj = new Foo();\n",
1088+
"}\n",
1089+
);
1090+
1091+
open_file(&backend, &uri, text).await;
1092+
1093+
// Rename Foo -> Baz from the declaration.
1094+
let edit = rename(&backend, &uri, 1, 7, "Baz").await;
1095+
assert!(edit.is_some(), "Expected a workspace edit");
1096+
1097+
let file_edits = edits_for_uri(&edit.unwrap(), &uri);
1098+
let result = apply_edits(text, &file_edits);
1099+
1100+
// Class declaration and references should be renamed.
1101+
assert!(
1102+
result.contains("class Baz"),
1103+
"Declaration should be renamed; got:\n{}",
1104+
result
1105+
);
1106+
assert!(
1107+
result.contains("new Baz()"),
1108+
"new expression should be renamed; got:\n{}",
1109+
result
1110+
);
1111+
assert!(
1112+
result.contains("demo(Baz"),
1113+
"Type hint should be renamed; got:\n{}",
1114+
result
1115+
);
1116+
assert!(
1117+
result.contains("extends Baz"),
1118+
"extends should be renamed; got:\n{}",
1119+
result
1120+
);
1121+
1122+
// self, static, and parent keywords must NOT be renamed.
1123+
assert!(
1124+
result.contains("self::BAR"),
1125+
"self:: should not be renamed; got:\n{}",
1126+
result
1127+
);
1128+
assert!(
1129+
result.contains("static::BAR"),
1130+
"static:: should not be renamed; got:\n{}",
1131+
result
1132+
);
1133+
assert!(
1134+
result.contains("parent::create"),
1135+
"parent:: should not be renamed; got:\n{}",
1136+
result
1137+
);
1138+
assert!(
1139+
result.contains("new self()"),
1140+
"new self() should not be renamed; got:\n{}",
1141+
result
1142+
);
1143+
assert!(
1144+
result.contains("): self {"),
1145+
"return type self should not be renamed; got:\n{}",
1146+
result
1147+
);
1148+
}
1149+
10661150
#[tokio::test]
10671151
async fn rename_class_same_file_no_use_statement() {
10681152
// Renaming a class in the same file (no use statement) should still work.

0 commit comments

Comments
 (0)