about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-08-18 08:02:37 +0000
committerbors <bors@rust-lang.org>2022-08-18 08:02:37 +0000
commit5543dd88c98eca686d63fc032ae927a0565565b8 (patch)
treee491d3881f490fcec0cc705b29c03856614ea958
parent1d36aba57a279ef3a018626a41976ac831944c5e (diff)
parent313b004ef76d3352f36e5128b16ca5212de97d49 (diff)
downloadrust-5543dd88c98eca686d63fc032ae927a0565565b8.tar.gz
rust-5543dd88c98eca686d63fc032ae927a0565565b8.zip
Auto merge of #13036 - sancho20021:10881-inline_type_alias_uses, r=Veykril
feat: Add an assist for inlining all type alias uses

## Description
`inline_type_alias_uses` assist tries to inline all selected type alias occurrences.

### Currently
Type alias used in `PathType` position are inlined.

### Not supported
- Removing type alias declaration if all uses are inlined.
- Removing redundant imports after inlining all uses in the file.
- Type alias not in `PathType` position, such as:
  - `A::new()`
  - `let x = A {}`
  - `let bits = A::BITS`
  - etc.

## Demonstration

![example](https://user-images.githubusercontent.com/45790125/184905226-9cb8ac81-1439-4387-a13b-e18ad4ecf208.gif)

## Related Issues
Partially fixes #10881
-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 054663a06a1..9adf6381c1c 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 fe87aa15fcf..7fb35143fa2 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 6eaab48a32b..22319f36134 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",