diff options
| author | Terry Sun <terrynsun@gmail.com> | 2021-10-24 13:34:02 -0700 |
|---|---|---|
| committer | Terry Sun <terrynsun@gmail.com> | 2021-10-24 17:38:45 -0700 |
| commit | 324d7d33e829cf8fd73ce27aebe48f64876fadda (patch) | |
| tree | 5f3b3381e39a05ac12f2cb9f02e16c76eecb4a4f | |
| parent | 1f47693e02809c97db61b51247ae4e4d46744c61 (diff) | |
| download | rust-324d7d33e829cf8fd73ce27aebe48f64876fadda.tar.gz rust-324d7d33e829cf8fd73ce27aebe48f64876fadda.zip | |
Add assist for replacing turbofish with explicit type.
Converts `::<_>` to an explicit type assignment. ``` let args = args.collect::<Vec<String>>(); ``` -> ``` let args: Vec<String> = args.collect(); ``` Closes #10285
| -rw-r--r-- | crates/ide_assists/src/handlers/replace_turbofish_with_explicit_type.rs | 184 | ||||
| -rw-r--r-- | crates/ide_assists/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/ide_assists/src/tests/generated.rs | 19 |
3 files changed, 205 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide_assists/src/handlers/replace_turbofish_with_explicit_type.rs new file mode 100644 index 00000000000..b02596daede --- /dev/null +++ b/crates/ide_assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -0,0 +1,184 @@ +use syntax::{ + ast::Expr, + ast::{LetStmt, Type::InferType}, + AstNode, TextRange, +}; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, AssistKind, +}; + +// Assist: replace_turbofish_with_explicit_type +// +// Converts `::<_>` to an explicit type assignment. +// +// ``` +// fn make<T>() -> T { ) } +// fn main() { +// let a = make$0::<i32>(); +// } +// ``` +// -> +// ``` +// fn make<T>() -> T { ) } +// fn main() { +// let a: i32 = make(); +// } +// ``` +pub(crate) fn replace_turbofish_with_explicit_type( + acc: &mut Assists, + ctx: &AssistContext, +) -> Option<()> { + let let_stmt = ctx.find_node_at_offset::<LetStmt>()?; + + let initializer = let_stmt.initializer()?; + + let (turbofish_start, turbofish_type, turbofish_end) = if let Expr::CallExpr(ce) = initializer { + if let Expr::PathExpr(pe) = ce.expr()? { + let path = pe.path()?; + + let generic_args = path.segment()?.generic_arg_list()?; + + let colon2 = generic_args.coloncolon_token()?; + let r_angle = generic_args.r_angle_token()?; + + let turbofish_args_as_string = generic_args + .generic_args() + .into_iter() + .map(|a| -> String { a.to_string() }) + .collect::<Vec<String>>() + .join(", "); + + (colon2.text_range().start(), turbofish_args_as_string, r_angle.text_range().end()) + } else { + cov_mark::hit!(not_applicable_if_non_path_function_call); + return None; + } + } else { + cov_mark::hit!(not_applicable_if_non_function_call_initializer); + return None; + }; + + let turbofish_range = TextRange::new(turbofish_start, turbofish_end); + + if let None = let_stmt.colon_token() { + // If there's no colon in a let statement, then there is no explicit type. + // let x = fn::<...>(); + let ident_range = let_stmt.pat()?.syntax().text_range(); + + return acc.add( + AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite), + format!("Replace turbofish with explicit type `: <{}>`", turbofish_type), + turbofish_range, + |builder| { + builder.insert(ident_range.end(), format!(": {}", turbofish_type)); + builder.delete(turbofish_range); + }, + ); + } else if let Some(InferType(t)) = let_stmt.ty() { + // If there's a type inferrence underscore, we can offer to replace it with the type in + // the turbofish. + // let x: _ = fn::<...>(); + let underscore_range = t.syntax().text_range(); + + return acc.add( + AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite), + format!("Replace `_` with turbofish type `{}`", turbofish_type), + turbofish_range, + |builder| { + builder.replace(underscore_range, turbofish_type); + builder.delete(turbofish_range); + }, + ); + } + + None +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; + + #[test] + fn replaces_turbofish_for_vec_string() { + check_assist( + replace_turbofish_with_explicit_type, + r#" +fn make<T>() -> T {} +fn main() { + let a = make$0::<Vec<String>>(); +} +"#, + r#" +fn make<T>() -> T {} +fn main() { + let a: Vec<String> = make(); +} +"#, + ); + } + + #[test] + fn replace_turbofish_target() { + check_assist_target( + replace_turbofish_with_explicit_type, + r#" +fn make<T>() -> T {} +fn main() { + let a = $0make::<Vec<String>>(); +} +"#, + r#"::<Vec<String>>"#, + ); + } + + #[test] + fn replace_inferred_type_placeholder() { + check_assist( + replace_turbofish_with_explicit_type, + r#" +fn make<T>() -> T {} +fn main() { + let a: _ = make$0::<Vec<String>>(); +} +"#, + r#" +fn make<T>() -> T {} +fn main() { + let a: Vec<String> = make(); +} +"#, + ); + } + + #[test] + fn not_applicable_constant_initializer() { + cov_mark::check!(not_applicable_if_non_function_call_initializer); + check_assist_not_applicable( + replace_turbofish_with_explicit_type, + r#" +fn make<T>() -> T {} +fn main() { + let a = "foo"$0; +} +"#, + ); + } + + #[test] + fn not_applicable_non_path_function_call() { + cov_mark::check!(not_applicable_if_non_path_function_call); + check_assist_not_applicable( + replace_turbofish_with_explicit_type, + r#" +fn make<T>() -> T {} +fn main() { + $0let a = (|| {})(); +} +"#, + ); + } +} diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 8f9ae6c1540..5d4c1532dbe 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs @@ -175,6 +175,7 @@ mod handlers { mod replace_let_with_if_let; mod replace_qualified_name_with_use; mod replace_string_with_char; + mod replace_turbofish_with_explicit_type; mod split_import; mod sort_items; mod toggle_ignore; @@ -257,6 +258,7 @@ mod handlers { replace_if_let_with_match::replace_if_let_with_match, replace_if_let_with_match::replace_match_with_if_let, replace_let_with_if_let::replace_let_with_if_let, + replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type, replace_qualified_name_with_use::replace_qualified_name_with_use, sort_items::sort_items, split_import::split_import, diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 43d30e84b3b..e30f98bcd13 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -1877,6 +1877,25 @@ fn handle() { } #[test] +fn doctest_replace_turbofish_with_explicit_type() { + check_doc_test( + "replace_turbofish_with_explicit_type", + r#####" +fn make<T>() -> T { ) } +fn main() { + let a = make$0::<i32>(); +} +"#####, + r#####" +fn make<T>() -> T { ) } +fn main() { + let a: i32 = make(); +} +"#####, + ) +} + +#[test] fn doctest_sort_items() { check_doc_test( "sort_items", |
