Skip to content

Commit e0e18cc

Browse files
committed
Auto merge of rust-lang#13151 - ChayimFriedman2:replace-turbofish-other-type, r=Veykril
Use correct type in "Replace turbofish with type" And support `?` and `.await` expressions. Fixes rust-lang#13148. The assist can still show up even if the turbofish's type is not used at all, e.g.: ```rust fn foo<T>() {} let v = foo::<i32>(); ```
2 parents 989b09d + bcdacfe commit e0e18cc

File tree

1 file changed

+141
-19
lines changed

1 file changed

+141
-19
lines changed

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

Lines changed: 141 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
use hir::HirDisplay;
12
use syntax::{
2-
ast::{Expr, GenericArg},
3+
ast::{Expr, GenericArg, GenericArgList},
34
ast::{LetStmt, Type::InferType},
45
AstNode, TextRange,
56
};
@@ -34,21 +35,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
3435

3536
let initializer = let_stmt.initializer()?;
3637

37-
let generic_args = match &initializer {
38-
Expr::MethodCallExpr(ce) => ce.generic_arg_list()?,
39-
Expr::CallExpr(ce) => {
40-
if let Expr::PathExpr(pe) = ce.expr()? {
41-
pe.path()?.segment()?.generic_arg_list()?
42-
} else {
43-
cov_mark::hit!(not_applicable_if_non_path_function_call);
44-
return None;
45-
}
46-
}
47-
_ => {
48-
cov_mark::hit!(not_applicable_if_non_function_call_initializer);
49-
return None;
50-
}
51-
};
38+
let generic_args = generic_arg_list(&initializer)?;
5239

5340
// Find range of ::<_>
5441
let colon2 = generic_args.coloncolon_token()?;
@@ -65,7 +52,16 @@ pub(crate) fn replace_turbofish_with_explicit_type(
6552

6653
// An improvement would be to check that this is correctly part of the return value of the
6754
// function call, or sub in the actual return type.
68-
let turbofish_type = &turbofish_args[0];
55+
let returned_type = match ctx.sema.type_of_expr(&initializer) {
56+
Some(returned_type) if !returned_type.original.contains_unknown() => {
57+
let module = ctx.sema.scope(let_stmt.syntax())?.module();
58+
returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
59+
}
60+
_ => {
61+
cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
62+
turbofish_args[0].to_string()
63+
}
64+
};
6965

7066
let initializer_start = initializer.syntax().text_range().start();
7167
if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
@@ -83,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
8379
"Replace turbofish with explicit type",
8480
TextRange::new(initializer_start, turbofish_range.end()),
8581
|builder| {
86-
builder.insert(ident_range.end(), format!(": {}", turbofish_type));
82+
builder.insert(ident_range.end(), format!(": {}", returned_type));
8783
builder.delete(turbofish_range);
8884
},
8985
);
@@ -98,7 +94,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
9894
"Replace `_` with turbofish type",
9995
turbofish_range,
10096
|builder| {
101-
builder.replace(underscore_range, turbofish_type.to_string());
97+
builder.replace(underscore_range, returned_type);
10298
builder.delete(turbofish_range);
10399
},
104100
);
@@ -107,6 +103,26 @@ pub(crate) fn replace_turbofish_with_explicit_type(
107103
None
108104
}
109105

106+
fn generic_arg_list(expr: &Expr) -> Option<GenericArgList> {
107+
match expr {
108+
Expr::MethodCallExpr(expr) => expr.generic_arg_list(),
109+
Expr::CallExpr(expr) => {
110+
if let Expr::PathExpr(pe) = expr.expr()? {
111+
pe.path()?.segment()?.generic_arg_list()
112+
} else {
113+
cov_mark::hit!(not_applicable_if_non_path_function_call);
114+
return None;
115+
}
116+
}
117+
Expr::AwaitExpr(expr) => generic_arg_list(&expr.expr()?),
118+
Expr::TryExpr(expr) => generic_arg_list(&expr.expr()?),
119+
_ => {
120+
cov_mark::hit!(not_applicable_if_non_function_call_initializer);
121+
None
122+
}
123+
}
124+
}
125+
110126
#[cfg(test)]
111127
mod tests {
112128
use super::*;
@@ -115,6 +131,7 @@ mod tests {
115131

116132
#[test]
117133
fn replaces_turbofish_for_vec_string() {
134+
cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
118135
check_assist(
119136
replace_turbofish_with_explicit_type,
120137
r#"
@@ -135,6 +152,7 @@ fn main() {
135152
#[test]
136153
fn replaces_method_calls() {
137154
// foo.make() is a method call which uses a different expr in the let initializer
155+
cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
138156
check_assist(
139157
replace_turbofish_with_explicit_type,
140158
r#"
@@ -237,6 +255,110 @@ fn make<T>() -> T {}
237255
fn main() {
238256
let a = make$0::<Vec<String>, i32>();
239257
}
258+
"#,
259+
);
260+
}
261+
262+
#[test]
263+
fn replaces_turbofish_for_known_type() {
264+
check_assist(
265+
replace_turbofish_with_explicit_type,
266+
r#"
267+
fn make<T>() -> T {}
268+
fn main() {
269+
let a = make$0::<i32>();
270+
}
271+
"#,
272+
r#"
273+
fn make<T>() -> T {}
274+
fn main() {
275+
let a: i32 = make();
276+
}
277+
"#,
278+
);
279+
check_assist(
280+
replace_turbofish_with_explicit_type,
281+
r#"
282+
//- minicore: option
283+
fn make<T>() -> T {}
284+
fn main() {
285+
let a = make$0::<Option<bool>>();
286+
}
287+
"#,
288+
r#"
289+
fn make<T>() -> T {}
290+
fn main() {
291+
let a: Option<bool> = make();
292+
}
293+
"#,
294+
);
295+
}
296+
297+
#[test]
298+
fn replaces_turbofish_not_same_type() {
299+
check_assist(
300+
replace_turbofish_with_explicit_type,
301+
r#"
302+
//- minicore: option
303+
fn make<T>() -> Option<T> {}
304+
fn main() {
305+
let a = make$0::<u128>();
306+
}
307+
"#,
308+
r#"
309+
fn make<T>() -> Option<T> {}
310+
fn main() {
311+
let a: Option<u128> = make();
312+
}
313+
"#,
314+
);
315+
}
316+
317+
#[test]
318+
fn replaces_turbofish_for_type_with_defaulted_generic_param() {
319+
check_assist(
320+
replace_turbofish_with_explicit_type,
321+
r#"
322+
struct HasDefault<T, U = i32>(T, U);
323+
fn make<T>() -> HasDefault<T> {}
324+
fn main() {
325+
let a = make$0::<bool>();
326+
}
327+
"#,
328+
r#"
329+
struct HasDefault<T, U = i32>(T, U);
330+
fn make<T>() -> HasDefault<T> {}
331+
fn main() {
332+
let a: HasDefault<bool> = make();
333+
}
334+
"#,
335+
);
336+
}
337+
338+
#[test]
339+
fn replaces_turbofish_try_await() {
340+
check_assist(
341+
replace_turbofish_with_explicit_type,
342+
r#"
343+
//- minicore: option, future
344+
struct Fut<T>(T);
345+
impl<T> core::future::Future for Fut<T> {
346+
type Output = Option<T>;
347+
}
348+
fn make<T>() -> Fut<T> {}
349+
fn main() {
350+
let a = make$0::<bool>().await?;
351+
}
352+
"#,
353+
r#"
354+
struct Fut<T>(T);
355+
impl<T> core::future::Future for Fut<T> {
356+
type Output = Option<T>;
357+
}
358+
fn make<T>() -> Fut<T> {}
359+
fn main() {
360+
let a: bool = make().await?;
361+
}
240362
"#,
241363
);
242364
}

0 commit comments

Comments
 (0)