Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-assists/src/handlers/inline_type_alias.rs229
-rw-r--r--crates/ide-assists/src/lib.rs1
-rw-r--r--crates/ide-assists/src/tests/generated.rs25
3 files changed, 222 insertions, 33 deletions
diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs
index 054663a06a..9adf6381c1 100644
--- a/crates/ide-assists/src/handlers/inline_type_alias.rs
+++ b/crates/ide-assists/src/handlers/inline_type_alias.rs
@@ -1,9 +1,9 @@
// Some ideas for future improvements:
// - Support replacing aliases which are used in expressions, e.g. `A::new()`.
-// - "inline_alias_to_users" assist #10881.
// - Remove unused aliases if there are no longer any users, see inline_call.rs.
use hir::{HasSource, PathResolution};
+use ide_db::{defs::Definition, search::FileReference};
use itertools::Itertools;
use std::collections::HashMap;
use syntax::{
@@ -16,6 +16,78 @@ use crate::{
AssistId, AssistKind,
};
+// Assist: inline_type_alias_uses
+//
+// Inline a type alias into all of its uses where possible.
+//
+// ```
+// type $0A = i32;
+// fn id(x: A) -> A {
+// x
+// };
+// fn foo() {
+// let _: A = 3;
+// }
+// ```
+// ->
+// ```
+// type A = i32;
+// fn id(x: i32) -> i32 {
+// x
+// };
+// fn foo() {
+// let _: i32 = 3;
+// }
+pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let name = ctx.find_node_at_offset::<ast::Name>()?;
+ let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?;
+
+ let hir_alias = ctx.sema.to_def(&ast_alias)?;
+ let concrete_type = ast_alias.ty()?;
+
+ let usages = Definition::TypeAlias(hir_alias).usages(&ctx.sema);
+ if !usages.at_least_one() {
+ return None;
+ }
+
+ // until this is ok
+
+ acc.add(
+ AssistId("inline_type_alias_uses", AssistKind::RefactorInline),
+ "Inline type alias into all uses",
+ name.syntax().text_range(),
+ |builder| {
+ let usages = usages.all();
+
+ let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
+ builder.edit_file(file_id);
+
+ let path_types: Vec<ast::PathType> = refs
+ .into_iter()
+ .filter_map(|file_ref| match file_ref.name {
+ ast::NameLike::NameRef(path_type) => {
+ path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
+ }
+ _ => None,
+ })
+ .collect();
+
+ for (target, replacement) in path_types.into_iter().filter_map(|path_type| {
+ let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type);
+ let target = path_type.syntax().text_range();
+ Some((target, replacement))
+ }) {
+ builder.replace(target, replacement);
+ }
+ };
+
+ for (file_id, refs) in usages.into_iter() {
+ inline_refs_for_file(file_id, refs);
+ }
+ },
+ )
+}
+
// Assist: inline_type_alias
//
// Replace a type alias with its concrete type.
@@ -36,11 +108,6 @@ use crate::{
// }
// ```
pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- enum Replacement {
- Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap },
- Plain,
- }
-
let alias_instance = ctx.find_node_at_offset::<ast::PathType>()?;
let concrete_type;
let replacement;
@@ -59,23 +126,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
_ => {
let alias = get_type_alias(&ctx, &alias_instance)?;
concrete_type = alias.ty()?;
-
- replacement = if let Some(alias_generics) = alias.generic_param_list() {
- if alias_generics.generic_params().next().is_none() {
- cov_mark::hit!(no_generics_params);
- return None;
- }
-
- let instance_args =
- alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast);
-
- Replacement::Generic {
- lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?,
- const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?,
- }
- } else {
- Replacement::Plain
- };
+ replacement = inline(&alias, &alias_instance)?;
}
}
@@ -85,19 +136,45 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
AssistId("inline_type_alias", AssistKind::RefactorInline),
"Inline type alias",
target,
- |builder| {
- let replacement_text = match replacement {
- Replacement::Generic { lifetime_map, const_and_type_map } => {
- create_replacement(&lifetime_map, &const_and_type_map, &concrete_type)
- }
- Replacement::Plain => concrete_type.to_string(),
- };
-
- builder.replace(target, replacement_text);
- },
+ |builder| builder.replace(target, replacement.to_text(&concrete_type)),
)
}
+impl Replacement {
+ fn to_text(&self, concrete_type: &ast::Type) -> String {
+ match self {
+ Replacement::Generic { lifetime_map, const_and_type_map } => {
+ create_replacement(&lifetime_map, &const_and_type_map, &concrete_type)
+ }
+ Replacement::Plain => concrete_type.to_string(),
+ }
+ }
+}
+
+enum Replacement {
+ Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap },
+ Plain,
+}
+
+fn inline(alias_def: &ast::TypeAlias, alias_instance: &ast::PathType) -> Option<Replacement> {
+ let repl = if let Some(alias_generics) = alias_def.generic_param_list() {
+ if alias_generics.generic_params().next().is_none() {
+ cov_mark::hit!(no_generics_params);
+ return None;
+ }
+ let instance_args =
+ alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast);
+
+ Replacement::Generic {
+ lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?,
+ const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?,
+ }
+ } else {
+ Replacement::Plain
+ };
+ Some(repl)
+}
+
struct LifetimeMap(HashMap<String, ast::Lifetime>);
impl LifetimeMap {
@@ -835,4 +912,90 @@ trait Tr {
"#,
);
}
+
+ mod inline_type_alias_uses {
+ use crate::{handlers::inline_type_alias::inline_type_alias_uses, tests::check_assist};
+
+ #[test]
+ fn inline_uses() {
+ check_assist(
+ inline_type_alias_uses,
+ r#"
+type $0A = u32;
+
+fn foo() {
+ let _: A = 3;
+ let _: A = 4;
+}
+"#,
+ r#"
+type A = u32;
+
+fn foo() {
+ let _: u32 = 3;
+ let _: u32 = 4;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn inline_uses_across_files() {
+ check_assist(
+ inline_type_alias_uses,
+ r#"
+//- /lib.rs
+mod foo;
+type $0T<E> = Vec<E>;
+fn f() -> T<&str> {
+ vec!["hello"]
+}
+
+//- /foo.rs
+use super::T;
+fn foo() {
+ let _: T<i8> = Vec::new();
+}
+"#,
+ r#"
+//- /lib.rs
+mod foo;
+type T<E> = Vec<E>;
+fn f() -> Vec<&str> {
+ vec!["hello"]
+}
+
+//- /foo.rs
+use super::T;
+fn foo() {
+ let _: Vec<i8> = Vec::new();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn inline_uses_across_files_2() {
+ check_assist(
+ inline_type_alias_uses,
+ r#"
+//- /lib.rs
+mod foo;
+type $0I = i32;
+
+//- /foo.rs
+use super::I;
+fn foo() {
+ let _: I = 0;
+}
+"#,
+ r#"
+use super::I;
+fn foo() {
+ let _: i32 = 0;
+}
+"#,
+ );
+ }
+ }
}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index fe87aa15fc..7fb35143fa 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -243,6 +243,7 @@ mod handlers {
inline_call::inline_into_callers,
inline_local_variable::inline_local_variable,
inline_type_alias::inline_type_alias,
+ inline_type_alias::inline_type_alias_uses,
introduce_named_generic::introduce_named_generic,
introduce_named_lifetime::introduce_named_lifetime,
invert_if::invert_if,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 6eaab48a32..22319f3613 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -1357,6 +1357,31 @@ fn main() {
}
#[test]
+fn doctest_inline_type_alias_uses() {
+ check_doc_test(
+ "inline_type_alias_uses",
+ r#####"
+type $0A = i32;
+fn id(x: A) -> A {
+ x
+};
+fn foo() {
+ let _: A = 3;
+}
+"#####,
+ r#####"
+type A = i32;
+fn id(x: i32) -> i32 {
+ x
+};
+fn foo() {
+ let _: i32 = 3;
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_introduce_named_generic() {
check_doc_test(
"introduce_named_generic",