feat: add an assist to unwrap a type with a generic arg

This assist unwraps a type into its generic type argument, ignoring
const and lifetime arguments
This commit is contained in:
Vishruth-Thimmaiah 2025-05-04 23:39:00 +05:30
parent 3b57c00151
commit 4771b84a75
No known key found for this signature in database
GPG key ID: B89775C5BE008866
3 changed files with 175 additions and 0 deletions

View file

@ -0,0 +1,156 @@
use ide_db::assists::AssistId;
use syntax::{
AstNode,
ast::{self, GenericArg, HasGenericArgs},
};
use crate::{AssistContext, Assists};
// Assist: unwrap_type_to_generic_arg
//
// This assist unwraps a type into its generic type argument.
//
// ```
// fn foo() -> $0Option<i32> {
// todo!()
// }
// ```
// ->
// ```
// fn foo() -> i32 {
// todo!()
// }
// ```
pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let path_type = ctx.find_node_at_offset::<ast::PathType>()?;
let path = path_type.path()?;
let segment = path.segment()?;
let args_list = segment.generic_arg_list()?;
let mut generic_arg = None;
for arg in args_list.generic_args() {
match arg {
GenericArg::ConstArg(_) | GenericArg::LifetimeArg(_) => (),
GenericArg::TypeArg(arg) if generic_arg.is_none() => {
generic_arg = Some(arg);
}
_ => return None,
}
}
let generic_arg = generic_arg?;
acc.add(
AssistId::refactor_extract("unwrap_type_to_generic_arg"),
format!("Unwrap type to type argument {generic_arg}"),
path_type.syntax().text_range(),
|builder| {
let mut editor = builder.make_editor(path_type.syntax());
editor.replace(path_type.syntax(), generic_arg.syntax());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{check_assist, check_assist_not_applicable};
#[test]
fn test_unwrap_type_to_generic_arg() {
check_assist(
unwrap_type_to_generic_arg,
r#"
//- minicore: option
fn foo() -> $0Option<i32> {
todo!()
}
"#,
r#"
fn foo() -> i32 {
todo!()
}
"#,
);
}
#[test]
fn unwrap_type_to_generic_arg_not_applicable_for_non_generic_arg_list() {
check_assist_not_applicable(
unwrap_type_to_generic_arg,
r#"
fn foo() -> $0i32 {}
"#,
);
}
#[test]
fn unwrap_type_to_generic_arg_not_applicable_for_multiple_generic_args() {
check_assist_not_applicable(
unwrap_type_to_generic_arg,
r#"
//- minicore: result
fn foo() -> $0Result<i32, ()> {
todo!()
}
"#,
);
}
#[test]
fn unwrap_type_to_generic_arg_with_lifetime_and_const() {
check_assist(
unwrap_type_to_generic_arg,
r#"
enum Foo<'a, T, const N: usize> {
Bar(T),
Baz(&'a [T; N]),
}
fn test<'a>() -> $0Foo<'a, i32, 3> {
todo!()
}
"#,
r#"
enum Foo<'a, T, const N: usize> {
Bar(T),
Baz(&'a [T; N]),
}
fn test<'a>() -> i32 {
todo!()
}
"#,
);
}
#[test]
fn unwrap_type_to_generic_arg_in_let_stmt() {
check_assist(
unwrap_type_to_generic_arg,
r#"
enum Foo<T> {
Bar(T),
Baz,
}
fn test() {
let foo: $0Foo<i32> = todo!();
}
"#,
r#"
enum Foo<T> {
Bar(T),
Baz,
}
fn test() {
let foo: i32 = todo!();
}
"#,
);
}
}

View file

@ -229,6 +229,7 @@ mod handlers {
mod unwrap_block;
mod unwrap_return_type;
mod unwrap_tuple;
mod unwrap_type_to_generic_arg;
mod wrap_return_type;
mod wrap_unwrap_cfg_attr;
@ -369,6 +370,7 @@ mod handlers {
unwrap_block::unwrap_block,
unwrap_return_type::unwrap_return_type,
unwrap_tuple::unwrap_tuple,
unwrap_type_to_generic_arg::unwrap_type_to_generic_arg,
wrap_return_type::wrap_return_type,
wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr,

View file

@ -3481,6 +3481,23 @@ fn main() {
)
}
#[test]
fn doctest_unwrap_type_to_generic_arg() {
check_doc_test(
"unwrap_type_to_generic_arg",
r#####"
fn foo() -> $0Option<i32> {
todo!()
}
"#####,
r#####"
fn foo() -> i32 {
todo!()
}
"#####,
)
}
#[test]
fn doctest_wrap_return_type_in_option() {
check_doc_test(