Skip to content

Commit 4c33ae3

Browse files
committed
Reject more cases of invalid parameter to self renames
1 parent f4a77f3 commit 4c33ae3

File tree

1 file changed

+122
-13
lines changed

1 file changed

+122
-13
lines changed

crates/ide/src/references/rename.rs

Lines changed: 122 additions & 13 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)))
@@ -1084,6 +1104,95 @@ impl Foo {
10841104
self.i
10851105
}
10861106
}
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+
}
10871196
"#,
10881197
);
10891198
}

0 commit comments

Comments
 (0)