3 ast::{Expr, GenericArg, GenericArgList},
4 ast::{LetStmt, Type::InferType},
9 assist_context::{AssistContext, Assists},
13 // Assist: replace_turbofish_with_explicit_type
15 // Converts `::<_>` to an explicit type assignment.
18 // fn make<T>() -> T { ) }
20 // let a = make$0::<i32>();
25 // fn make<T>() -> T { ) }
27 // let a: i32 = make();
30 pub(crate) fn replace_turbofish_with_explicit_type(
32 ctx: &AssistContext<'_>,
34 let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
36 let initializer = let_stmt.initializer()?;
38 let generic_args = generic_arg_list(&initializer)?;
40 // Find range of ::<_>
41 let colon2 = generic_args.coloncolon_token()?;
42 let r_angle = generic_args.r_angle_token()?;
43 let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());
45 let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();
48 if turbofish_args.len() != 1 {
49 cov_mark::hit!(not_applicable_if_not_single_arg);
53 // An improvement would be to check that this is correctly part of the return value of the
54 // function call, or sub in the actual return type.
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()?
61 cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
62 turbofish_args[0].to_string()
66 let initializer_start = initializer.syntax().text_range().start();
67 if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
68 cov_mark::hit!(not_applicable_outside_turbofish);
72 if let None = let_stmt.colon_token() {
73 // If there's no colon in a let statement, then there is no explicit type.
74 // let x = fn::<...>();
75 let ident_range = let_stmt.pat()?.syntax().text_range();
78 AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
79 "Replace turbofish with explicit type",
80 TextRange::new(initializer_start, turbofish_range.end()),
82 builder.insert(ident_range.end(), format!(": {}", returned_type));
83 builder.delete(turbofish_range);
86 } else if let Some(InferType(t)) = let_stmt.ty() {
87 // If there's a type inference underscore, we can offer to replace it with the type in
89 // let x: _ = fn::<...>();
90 let underscore_range = t.syntax().text_range();
93 AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
94 "Replace `_` with turbofish type",
97 builder.replace(underscore_range, returned_type);
98 builder.delete(turbofish_range);
106 fn generic_arg_list(expr: &Expr) -> Option<GenericArgList> {
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()
113 cov_mark::hit!(not_applicable_if_non_path_function_call);
117 Expr::AwaitExpr(expr) => generic_arg_list(&expr.expr()?),
118 Expr::TryExpr(expr) => generic_arg_list(&expr.expr()?),
120 cov_mark::hit!(not_applicable_if_non_function_call_initializer);
130 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
133 fn replaces_turbofish_for_vec_string() {
134 cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
136 replace_turbofish_with_explicit_type,
140 let a = make$0::<Vec<String>>();
146 let a: Vec<String> = make();
153 fn replaces_method_calls() {
154 // 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);
157 replace_turbofish_with_explicit_type,
161 let a = foo.make$0::<Vec<String>>();
167 let a: Vec<String> = foo.make();
174 fn replace_turbofish_target() {
176 replace_turbofish_with_explicit_type,
180 let a = $0make::<Vec<String>>();
183 r#"make::<Vec<String>>"#,
188 fn not_applicable_outside_turbofish() {
189 cov_mark::check!(not_applicable_outside_turbofish);
190 check_assist_not_applicable(
191 replace_turbofish_with_explicit_type,
195 let $0a = make::<Vec<String>>();
202 fn replace_inferred_type_placeholder() {
204 replace_turbofish_with_explicit_type,
208 let a: _ = make$0::<Vec<String>>();
214 let a: Vec<String> = make();
221 fn not_applicable_constant_initializer() {
222 cov_mark::check!(not_applicable_if_non_function_call_initializer);
223 check_assist_not_applicable(
224 replace_turbofish_with_explicit_type,
235 fn not_applicable_non_path_function_call() {
236 cov_mark::check!(not_applicable_if_non_path_function_call);
237 check_assist_not_applicable(
238 replace_turbofish_with_explicit_type,
249 fn non_applicable_multiple_generic_args() {
250 cov_mark::check!(not_applicable_if_not_single_arg);
251 check_assist_not_applicable(
252 replace_turbofish_with_explicit_type,
256 let a = make$0::<Vec<String>, i32>();
263 fn replaces_turbofish_for_known_type() {
265 replace_turbofish_with_explicit_type,
269 let a = make$0::<i32>();
280 replace_turbofish_with_explicit_type,
285 let a = make$0::<Option<bool>>();
291 let a: Option<bool> = make();
298 fn replaces_turbofish_not_same_type() {
300 replace_turbofish_with_explicit_type,
303 fn make<T>() -> Option<T> {}
305 let a = make$0::<u128>();
309 fn make<T>() -> Option<T> {}
311 let a: Option<u128> = make();
318 fn replaces_turbofish_for_type_with_defaulted_generic_param() {
320 replace_turbofish_with_explicit_type,
322 struct HasDefault<T, U = i32>(T, U);
323 fn make<T>() -> HasDefault<T> {}
325 let a = make$0::<bool>();
329 struct HasDefault<T, U = i32>(T, U);
330 fn make<T>() -> HasDefault<T> {}
332 let a: HasDefault<bool> = make();
339 fn replaces_turbofish_try_await() {
341 replace_turbofish_with_explicit_type,
343 //- minicore: option, future
345 impl<T> core::future::Future for Fut<T> {
346 type Output = Option<T>;
348 fn make<T>() -> Fut<T> {}
350 let a = make$0::<bool>().await?;
355 impl<T> core::future::Future for Fut<T> {
356 type Output = Option<T>;
358 fn make<T>() -> Fut<T> {}
360 let a: bool = make().await?;