Skip to content

Commit 5543dd8

Browse files
committed
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
2 parents 1d36aba + 313b004 commit 5543dd8

File tree

3 files changed

+222
-33
lines changed

3 files changed

+222
-33
lines changed

crates/ide-assists/src/handlers/inline_type_alias.rs

+196-33
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Some ideas for future improvements:
22
// - Support replacing aliases which are used in expressions, e.g. `A::new()`.
3-
// - "inline_alias_to_users" assist #10881.
43
// - Remove unused aliases if there are no longer any users, see inline_call.rs.
54

65
use hir::{HasSource, PathResolution};
6+
use ide_db::{defs::Definition, search::FileReference};
77
use itertools::Itertools;
88
use std::collections::HashMap;
99
use syntax::{
@@ -16,6 +16,78 @@ use crate::{
1616
AssistId, AssistKind,
1717
};
1818

19+
// Assist: inline_type_alias_uses
20+
//
21+
// Inline a type alias into all of its uses where possible.
22+
//
23+
// ```
24+
// type $0A = i32;
25+
// fn id(x: A) -> A {
26+
// x
27+
// };
28+
// fn foo() {
29+
// let _: A = 3;
30+
// }
31+
// ```
32+
// ->
33+
// ```
34+
// type A = i32;
35+
// fn id(x: i32) -> i32 {
36+
// x
37+
// };
38+
// fn foo() {
39+
// let _: i32 = 3;
40+
// }
41+
pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
42+
let name = ctx.find_node_at_offset::<ast::Name>()?;
43+
let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?;
44+
45+
let hir_alias = ctx.sema.to_def(&ast_alias)?;
46+
let concrete_type = ast_alias.ty()?;
47+
48+
let usages = Definition::TypeAlias(hir_alias).usages(&ctx.sema);
49+
if !usages.at_least_one() {
50+
return None;
51+
}
52+
53+
// until this is ok
54+
55+
acc.add(
56+
AssistId("inline_type_alias_uses", AssistKind::RefactorInline),
57+
"Inline type alias into all uses",
58+
name.syntax().text_range(),
59+
|builder| {
60+
let usages = usages.all();
61+
62+
let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
63+
builder.edit_file(file_id);
64+
65+
let path_types: Vec<ast::PathType> = refs
66+
.into_iter()
67+
.filter_map(|file_ref| match file_ref.name {
68+
ast::NameLike::NameRef(path_type) => {
69+
path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
70+
}
71+
_ => None,
72+
})
73+
.collect();
74+
75+
for (target, replacement) in path_types.into_iter().filter_map(|path_type| {
76+
let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type);
77+
let target = path_type.syntax().text_range();
78+
Some((target, replacement))
79+
}) {
80+
builder.replace(target, replacement);
81+
}
82+
};
83+
84+
for (file_id, refs) in usages.into_iter() {
85+
inline_refs_for_file(file_id, refs);
86+
}
87+
},
88+
)
89+
}
90+
1991
// Assist: inline_type_alias
2092
//
2193
// Replace a type alias with its concrete type.
@@ -36,11 +108,6 @@ use crate::{
36108
// }
37109
// ```
38110
pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
39-
enum Replacement {
40-
Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap },
41-
Plain,
42-
}
43-
44111
let alias_instance = ctx.find_node_at_offset::<ast::PathType>()?;
45112
let concrete_type;
46113
let replacement;
@@ -59,23 +126,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
59126
_ => {
60127
let alias = get_type_alias(&ctx, &alias_instance)?;
61128
concrete_type = alias.ty()?;
62-
63-
replacement = if let Some(alias_generics) = alias.generic_param_list() {
64-
if alias_generics.generic_params().next().is_none() {
65-
cov_mark::hit!(no_generics_params);
66-
return None;
67-
}
68-
69-
let instance_args =
70-
alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast);
71-
72-
Replacement::Generic {
73-
lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?,
74-
const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?,
75-
}
76-
} else {
77-
Replacement::Plain
78-
};
129+
replacement = inline(&alias, &alias_instance)?;
79130
}
80131
}
81132

@@ -85,19 +136,45 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
85136
AssistId("inline_type_alias", AssistKind::RefactorInline),
86137
"Inline type alias",
87138
target,
88-
|builder| {
89-
let replacement_text = match replacement {
90-
Replacement::Generic { lifetime_map, const_and_type_map } => {
91-
create_replacement(&lifetime_map, &const_and_type_map, &concrete_type)
92-
}
93-
Replacement::Plain => concrete_type.to_string(),
94-
};
95-
96-
builder.replace(target, replacement_text);
97-
},
139+
|builder| builder.replace(target, replacement.to_text(&concrete_type)),
98140
)
99141
}
100142

143+
impl Replacement {
144+
fn to_text(&self, concrete_type: &ast::Type) -> String {
145+
match self {
146+
Replacement::Generic { lifetime_map, const_and_type_map } => {
147+
create_replacement(&lifetime_map, &const_and_type_map, &concrete_type)
148+
}
149+
Replacement::Plain => concrete_type.to_string(),
150+
}
151+
}
152+
}
153+
154+
enum Replacement {
155+
Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap },
156+
Plain,
157+
}
158+
159+
fn inline(alias_def: &ast::TypeAlias, alias_instance: &ast::PathType) -> Option<Replacement> {
160+
let repl = if let Some(alias_generics) = alias_def.generic_param_list() {
161+
if alias_generics.generic_params().next().is_none() {
162+
cov_mark::hit!(no_generics_params);
163+
return None;
164+
}
165+
let instance_args =
166+
alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast);
167+
168+
Replacement::Generic {
169+
lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?,
170+
const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?,
171+
}
172+
} else {
173+
Replacement::Plain
174+
};
175+
Some(repl)
176+
}
177+
101178
struct LifetimeMap(HashMap<String, ast::Lifetime>);
102179

103180
impl LifetimeMap {
@@ -835,4 +912,90 @@ trait Tr {
835912
"#,
836913
);
837914
}
915+
916+
mod inline_type_alias_uses {
917+
use crate::{handlers::inline_type_alias::inline_type_alias_uses, tests::check_assist};
918+
919+
#[test]
920+
fn inline_uses() {
921+
check_assist(
922+
inline_type_alias_uses,
923+
r#"
924+
type $0A = u32;
925+
926+
fn foo() {
927+
let _: A = 3;
928+
let _: A = 4;
929+
}
930+
"#,
931+
r#"
932+
type A = u32;
933+
934+
fn foo() {
935+
let _: u32 = 3;
936+
let _: u32 = 4;
937+
}
938+
"#,
939+
);
940+
}
941+
942+
#[test]
943+
fn inline_uses_across_files() {
944+
check_assist(
945+
inline_type_alias_uses,
946+
r#"
947+
//- /lib.rs
948+
mod foo;
949+
type $0T<E> = Vec<E>;
950+
fn f() -> T<&str> {
951+
vec!["hello"]
952+
}
953+
954+
//- /foo.rs
955+
use super::T;
956+
fn foo() {
957+
let _: T<i8> = Vec::new();
958+
}
959+
"#,
960+
r#"
961+
//- /lib.rs
962+
mod foo;
963+
type T<E> = Vec<E>;
964+
fn f() -> Vec<&str> {
965+
vec!["hello"]
966+
}
967+
968+
//- /foo.rs
969+
use super::T;
970+
fn foo() {
971+
let _: Vec<i8> = Vec::new();
972+
}
973+
"#,
974+
);
975+
}
976+
977+
#[test]
978+
fn inline_uses_across_files_2() {
979+
check_assist(
980+
inline_type_alias_uses,
981+
r#"
982+
//- /lib.rs
983+
mod foo;
984+
type $0I = i32;
985+
986+
//- /foo.rs
987+
use super::I;
988+
fn foo() {
989+
let _: I = 0;
990+
}
991+
"#,
992+
r#"
993+
use super::I;
994+
fn foo() {
995+
let _: i32 = 0;
996+
}
997+
"#,
998+
);
999+
}
1000+
}
8381001
}

crates/ide-assists/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ mod handlers {
243243
inline_call::inline_into_callers,
244244
inline_local_variable::inline_local_variable,
245245
inline_type_alias::inline_type_alias,
246+
inline_type_alias::inline_type_alias_uses,
246247
introduce_named_generic::introduce_named_generic,
247248
introduce_named_lifetime::introduce_named_lifetime,
248249
invert_if::invert_if,

crates/ide-assists/src/tests/generated.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,31 @@ fn main() {
13561356
)
13571357
}
13581358

1359+
#[test]
1360+
fn doctest_inline_type_alias_uses() {
1361+
check_doc_test(
1362+
"inline_type_alias_uses",
1363+
r#####"
1364+
type $0A = i32;
1365+
fn id(x: A) -> A {
1366+
x
1367+
};
1368+
fn foo() {
1369+
let _: A = 3;
1370+
}
1371+
"#####,
1372+
r#####"
1373+
type A = i32;
1374+
fn id(x: i32) -> i32 {
1375+
x
1376+
};
1377+
fn foo() {
1378+
let _: i32 = 3;
1379+
}
1380+
"#####,
1381+
)
1382+
}
1383+
13591384
#[test]
13601385
fn doctest_introduce_named_generic() {
13611386
check_doc_test(

0 commit comments

Comments
 (0)