Skip to content

Commit e5e9799

Browse files
Use type information to deduce the correct type for "Replace turbofish with explicit type", even when it is not exactly the same as the turbofish type
I implemented that by checking the expressions' type. This could probably be implemented better by taking the function's return type and substituting the generic parameter with the provided turbofish, but this is more complicated.
1 parent e8e598f commit e5e9799

File tree

1 file changed

+91
-3
lines changed

1 file changed

+91
-3
lines changed

crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use hir::HirDisplay;
12
use syntax::{
23
ast::{Expr, GenericArg},
34
ast::{LetStmt, Type::InferType},
@@ -65,7 +66,16 @@ pub(crate) fn replace_turbofish_with_explicit_type(
6566

6667
// An improvement would be to check that this is correctly part of the return value of the
6768
// function call, or sub in the actual return type.
68-
let turbofish_type = &turbofish_args[0];
69+
let returned_type = match ctx.sema.type_of_expr(&initializer) {
70+
Some(returned_type) if !returned_type.original.contains_unknown() => {
71+
let module = ctx.sema.scope(let_stmt.syntax())?.module();
72+
returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
73+
}
74+
_ => {
75+
cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
76+
turbofish_args[0].to_string()
77+
}
78+
};
6979

7080
let initializer_start = initializer.syntax().text_range().start();
7181
if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
@@ -83,7 +93,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
8393
"Replace turbofish with explicit type",
8494
TextRange::new(initializer_start, turbofish_range.end()),
8595
|builder| {
86-
builder.insert(ident_range.end(), format!(": {}", turbofish_type));
96+
builder.insert(ident_range.end(), format!(": {}", returned_type));
8797
builder.delete(turbofish_range);
8898
},
8999
);
@@ -98,7 +108,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
98108
"Replace `_` with turbofish type",
99109
turbofish_range,
100110
|builder| {
101-
builder.replace(underscore_range, turbofish_type.to_string());
111+
builder.replace(underscore_range, returned_type);
102112
builder.delete(turbofish_range);
103113
},
104114
);
@@ -115,6 +125,7 @@ mod tests {
115125

116126
#[test]
117127
fn replaces_turbofish_for_vec_string() {
128+
cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
118129
check_assist(
119130
replace_turbofish_with_explicit_type,
120131
r#"
@@ -135,6 +146,7 @@ fn main() {
135146
#[test]
136147
fn replaces_method_calls() {
137148
// foo.make() is a method call which uses a different expr in the let initializer
149+
cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
138150
check_assist(
139151
replace_turbofish_with_explicit_type,
140152
r#"
@@ -237,6 +249,82 @@ fn make<T>() -> T {}
237249
fn main() {
238250
let a = make$0::<Vec<String>, i32>();
239251
}
252+
"#,
253+
);
254+
}
255+
256+
#[test]
257+
fn replaces_turbofish_for_known_type() {
258+
check_assist(
259+
replace_turbofish_with_explicit_type,
260+
r#"
261+
fn make<T>() -> T {}
262+
fn main() {
263+
let a = make$0::<i32>();
264+
}
265+
"#,
266+
r#"
267+
fn make<T>() -> T {}
268+
fn main() {
269+
let a: i32 = make();
270+
}
271+
"#,
272+
);
273+
check_assist(
274+
replace_turbofish_with_explicit_type,
275+
r#"
276+
//- minicore: option
277+
fn make<T>() -> T {}
278+
fn main() {
279+
let a = make$0::<Option<bool>>();
280+
}
281+
"#,
282+
r#"
283+
fn make<T>() -> T {}
284+
fn main() {
285+
let a: Option<bool> = make();
286+
}
287+
"#,
288+
);
289+
}
290+
291+
#[test]
292+
fn replaces_turbofish_not_same_type() {
293+
check_assist(
294+
replace_turbofish_with_explicit_type,
295+
r#"
296+
//- minicore: option
297+
fn make<T>() -> Option<T> {}
298+
fn main() {
299+
let a = make$0::<u128>();
300+
}
301+
"#,
302+
r#"
303+
fn make<T>() -> Option<T> {}
304+
fn main() {
305+
let a: Option<u128> = make();
306+
}
307+
"#,
308+
);
309+
}
310+
311+
#[test]
312+
fn replaces_turbofish_for_type_with_defaulted_generic_param() {
313+
check_assist(
314+
replace_turbofish_with_explicit_type,
315+
r#"
316+
struct HasDefault<T, U = i32>(T, U);
317+
fn make<T>() -> HasDefault<T> {}
318+
fn main() {
319+
let a = make$0::<bool>();
320+
}
321+
"#,
322+
r#"
323+
struct HasDefault<T, U = i32>(T, U);
324+
fn make<T>() -> HasDefault<T> {}
325+
fn main() {
326+
let a: HasDefault<bool> = make();
327+
}
240328
"#,
241329
);
242330
}

0 commit comments

Comments
 (0)