Skip to content

Commit 68910d2

Browse files
bors[bot]Veykril
andauthored
Merge #6670
6670: Allow renaming between self and first param with owned parameters r=matklad a=Veykril This fixes renaming owned SelfParams turning the parameter into a reference, as in, for a type `Foo`, `fn foo(self) {}` became `fn foo(renamed_name: &Foo) {}` prior to this. Similarly for the other way around, we now support renaming non-ref parameters to `self`. Additionally we do more checks now than before. We check: - that the function has an impl block - that we are renaming the first parameter(prior we ignored which parameter was renamed and always picked the first nevertheless) - that the parameter's type aligns with the impl block(minus one level of reference abstraction to account for `&self`/`&mut self`) Co-authored-by: Lukas Wirth <[email protected]>
2 parents 65a7893 + 4c33ae3 commit 68910d2

File tree

1 file changed

+152
-14
lines changed

1 file changed

+152
-14
lines changed

crates/ide/src/references/rename.rs

Lines changed: 152 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -221,24 +221,47 @@ fn rename_to_self(
221221
let source_file = sema.parse(position.file_id);
222222
let syn = source_file.syntax();
223223

224-
let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
224+
let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset)
225+
.and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast)))
225226
.ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?;
226-
let params =
227-
fn_def.param_list().ok_or_else(|| RenameError("Method has no parameters".to_string()))?;
228-
if params.self_param().is_some() {
227+
let param_range = fn_ast
228+
.param_list()
229+
.and_then(|p| p.params().next())
230+
.ok_or_else(|| RenameError("Method has no parameters".to_string()))?
231+
.syntax()
232+
.text_range();
233+
if !param_range.contains(position.offset) {
234+
return Err(RenameError("Only the first parameter can be self".to_string()));
235+
}
236+
237+
let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset)
238+
.and_then(|def| sema.to_def(&def))
239+
.ok_or_else(|| RenameError("No impl block found for function".to_string()))?;
240+
if fn_def.self_param(sema.db).is_some() {
229241
return Err(RenameError("Method already has a self parameter".to_string()));
230242
}
243+
244+
let params = fn_def.params(sema.db);
231245
let first_param =
232-
params.params().next().ok_or_else(|| RenameError("Method has no parameters".into()))?;
233-
let mutable = match first_param.ty() {
234-
Some(ast::Type::RefType(rt)) => rt.mut_token().is_some(),
235-
_ => return Err(RenameError("Not renaming other types".to_string())),
246+
params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?;
247+
let first_param_ty = first_param.ty();
248+
let impl_ty = impl_block.target_ty(sema.db);
249+
let (ty, self_param) = if impl_ty.remove_ref().is_some() {
250+
// if the impl is a ref to the type we can just match the `&T` with self directly
251+
(first_param_ty.clone(), "self")
252+
} else {
253+
first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| {
254+
(ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" })
255+
})
236256
};
237257

258+
if ty != impl_ty {
259+
return Err(RenameError("Parameter type differs from impl block type".to_string()));
260+
}
261+
238262
let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)
239263
.ok_or_else(|| RenameError("No reference found at position".to_string()))?;
240264

241-
let param_range = first_param.syntax().text_range();
242265
let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
243266
.into_iter()
244267
.partition(|reference| param_range.intersect(reference.file_range.range).is_some());
@@ -254,10 +277,7 @@ fn rename_to_self(
254277

255278
edits.push(SourceFileEdit {
256279
file_id: position.file_id,
257-
edit: TextEdit::replace(
258-
param_range,
259-
String::from(if mutable { "&mut self" } else { "&self" }),
260-
),
280+
edit: TextEdit::replace(param_range, String::from(self_param)),
261281
});
262282

263283
Ok(RangeInfo::new(range, SourceChange::from(edits)))
@@ -280,7 +300,11 @@ fn text_edit_from_self_param(
280300

281301
let mut replacement_text = String::from(new_name);
282302
replacement_text.push_str(": ");
283-
replacement_text.push_str(self_param.mut_token().map_or("&", |_| "&mut "));
303+
match (self_param.amp_token(), self_param.mut_token()) {
304+
(None, None) => (),
305+
(Some(_), None) => replacement_text.push('&'),
306+
(_, Some(_)) => replacement_text.push_str("&mut "),
307+
};
284308
replacement_text.push_str(type_name.as_str());
285309

286310
Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
@@ -1080,6 +1104,95 @@ impl Foo {
10801104
self.i
10811105
}
10821106
}
1107+
"#,
1108+
);
1109+
check(
1110+
"self",
1111+
r#"
1112+
struct Foo { i: i32 }
1113+
1114+
impl Foo {
1115+
fn f(foo<|>: Foo) -> i32 {
1116+
foo.i
1117+
}
1118+
}
1119+
"#,
1120+
r#"
1121+
struct Foo { i: i32 }
1122+
1123+
impl Foo {
1124+
fn f(self) -> i32 {
1125+
self.i
1126+
}
1127+
}
1128+
"#,
1129+
);
1130+
}
1131+
1132+
#[test]
1133+
fn test_parameter_to_self_error_no_impl() {
1134+
check(
1135+
"self",
1136+
r#"
1137+
struct Foo { i: i32 }
1138+
1139+
fn f(foo<|>: &mut Foo) -> i32 {
1140+
foo.i
1141+
}
1142+
"#,
1143+
"error: No impl block found for function",
1144+
);
1145+
check(
1146+
"self",
1147+
r#"
1148+
struct Foo { i: i32 }
1149+
struct Bar;
1150+
1151+
impl Bar {
1152+
fn f(foo<|>: &mut Foo) -> i32 {
1153+
foo.i
1154+
}
1155+
}
1156+
"#,
1157+
"error: Parameter type differs from impl block type",
1158+
);
1159+
}
1160+
1161+
#[test]
1162+
fn test_parameter_to_self_error_not_first() {
1163+
check(
1164+
"self",
1165+
r#"
1166+
struct Foo { i: i32 }
1167+
impl Foo {
1168+
fn f(x: (), foo<|>: &mut Foo) -> i32 {
1169+
foo.i
1170+
}
1171+
}
1172+
"#,
1173+
"error: Only the first parameter can be self",
1174+
);
1175+
}
1176+
1177+
#[test]
1178+
fn test_parameter_to_self_impl_ref() {
1179+
check(
1180+
"self",
1181+
r#"
1182+
struct Foo { i: i32 }
1183+
impl &Foo {
1184+
fn f(foo<|>: &Foo) -> i32 {
1185+
foo.i
1186+
}
1187+
}
1188+
"#,
1189+
r#"
1190+
struct Foo { i: i32 }
1191+
impl &Foo {
1192+
fn f(self) -> i32 {
1193+
self.i
1194+
}
1195+
}
10831196
"#,
10841197
);
10851198
}
@@ -1109,6 +1222,31 @@ impl Foo {
11091222
);
11101223
}
11111224

1225+
#[test]
1226+
fn test_owned_self_to_parameter() {
1227+
check(
1228+
"foo",
1229+
r#"
1230+
struct Foo { i: i32 }
1231+
1232+
impl Foo {
1233+
fn f(<|>self) -> i32 {
1234+
self.i
1235+
}
1236+
}
1237+
"#,
1238+
r#"
1239+
struct Foo { i: i32 }
1240+
1241+
impl Foo {
1242+
fn f(foo: Foo) -> i32 {
1243+
foo.i
1244+
}
1245+
}
1246+
"#,
1247+
);
1248+
}
1249+
11121250
#[test]
11131251
fn test_self_in_path_to_parameter() {
11141252
check(

0 commit comments

Comments
 (0)