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
65use hir:: { HasSource , PathResolution } ;
6+ use ide_db:: { defs:: Definition , search:: FileReference } ;
77use itertools:: Itertools ;
88use std:: collections:: HashMap ;
99use 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 ( ) . find_map ( 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// ```
38110pub ( 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+
101178struct LifetimeMap ( HashMap < String , ast:: Lifetime > ) ;
102179
103180impl LifetimeMap {
@@ -835,4 +912,95 @@ 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_fails ( ) {
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+ //- /lib.rs
994+ mod foo;
995+ type I = i32;
996+
997+ //- /foo.rs
998+ use super::I;
999+ fn foo() {
1000+ let _: i32 = 0;
1001+ }
1002+ "# ,
1003+ ) ;
1004+ }
1005+ }
8381006}
0 commit comments