@@ -5,7 +5,7 @@ use ide_db::RootDatabase;
5
5
use syntax:: {
6
6
ast,
7
7
ast:: { make, ArgListOwner } ,
8
- AstNode , TextRange ,
8
+ AstNode ,
9
9
} ;
10
10
use test_utils:: mark;
11
11
@@ -16,8 +16,6 @@ use crate::{
16
16
AssistId , AssistKind , GroupLabel ,
17
17
} ;
18
18
19
- const ASSIST_ID : AssistId = AssistId ( "qualify_path" , AssistKind :: QuickFix ) ;
20
-
21
19
// Assist: qualify_path
22
20
//
23
21
// If the name is unresolved, provides all possible qualified paths for it.
@@ -51,162 +49,151 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
51
49
return None ;
52
50
}
53
51
52
+ let candidate = import_assets. import_candidate ( ) ;
54
53
let range = ctx. sema . original_range ( import_assets. syntax_under_caret ( ) ) . range ;
55
- match import_assets. import_candidate ( ) {
56
- ImportCandidate :: QualifierStart ( candidate) => {
54
+
55
+ let qualify_candidate = match candidate {
56
+ ImportCandidate :: QualifierStart ( _) => {
57
+ mark:: hit!( qualify_path_qualifier_start) ;
57
58
let path = ast:: Path :: cast ( import_assets. syntax_under_caret ( ) . clone ( ) ) ?;
58
59
let segment = path. segment ( ) ?;
59
- qualify_path_qualifier_start ( acc , proposed_imports , range , segment, & candidate . name )
60
+ QualifyCandidate :: QualifierStart ( segment)
60
61
}
61
- ImportCandidate :: UnqualifiedName ( candidate) => {
62
- qualify_path_unqualified_name ( acc, proposed_imports, range, & candidate. name )
62
+ ImportCandidate :: UnqualifiedName ( _) => {
63
+ mark:: hit!( qualify_path_unqualified_name) ;
64
+ QualifyCandidate :: UnqualifiedName
63
65
}
64
66
ImportCandidate :: TraitAssocItem ( _) => {
67
+ mark:: hit!( qualify_path_trait_assoc_item) ;
65
68
let path = ast:: Path :: cast ( import_assets. syntax_under_caret ( ) . clone ( ) ) ?;
66
69
let ( qualifier, segment) = ( path. qualifier ( ) ?, path. segment ( ) ?) ;
67
- qualify_path_trait_assoc_item ( acc , proposed_imports , range , qualifier, segment)
70
+ QualifyCandidate :: TraitAssocItem ( qualifier, segment)
68
71
}
69
72
ImportCandidate :: TraitMethod ( _) => {
73
+ mark:: hit!( qualify_path_trait_method) ;
70
74
let mcall_expr = ast:: MethodCallExpr :: cast ( import_assets. syntax_under_caret ( ) . clone ( ) ) ?;
71
- qualify_path_trait_method ( acc , ctx. sema . db , proposed_imports , range , mcall_expr) ? ;
75
+ QualifyCandidate :: TraitMethod ( ctx. sema . db , mcall_expr)
72
76
}
73
77
} ;
74
- Some ( ( ) )
75
- }
76
78
77
- // a test that covers this -> `associated_struct_const`
78
- fn qualify_path_qualifier_start (
79
- acc : & mut Assists ,
80
- proposed_imports : Vec < ( hir:: ModPath , hir:: ItemInNs ) > ,
81
- range : TextRange ,
82
- segment : ast:: PathSegment ,
83
- qualifier_start : & ast:: NameRef ,
84
- ) {
85
- mark:: hit!( qualify_path_qualifier_start) ;
86
- let group_label = GroupLabel ( format ! ( "Qualify {}" , qualifier_start) ) ;
87
- for ( import, _) in proposed_imports {
79
+ let group_label = group_label ( candidate) ;
80
+ for ( import, item) in proposed_imports {
88
81
acc. add_group (
89
82
& group_label,
90
- ASSIST_ID ,
91
- format ! ( "Qualify with `{}`" , & import) ,
83
+ AssistId ( "qualify_path" , AssistKind :: QuickFix ) ,
84
+ label ( candidate , & import) ,
92
85
range,
93
86
|builder| {
94
- let import = mod_path_to_ast ( & import) ;
95
- builder. replace ( range, format ! ( "{}::{}" , import, segment) ) ;
87
+ qualify_candidate. qualify (
88
+ |replace_with : String | builder. replace ( range, replace_with) ,
89
+ import,
90
+ item,
91
+ )
96
92
} ,
97
93
) ;
98
94
}
95
+ Some ( ( ) )
99
96
}
100
97
101
- // a test that covers this -> `applicable_when_found_an_import_partial`
102
- fn qualify_path_unqualified_name (
103
- acc : & mut Assists ,
104
- proposed_imports : Vec < ( hir:: ModPath , hir:: ItemInNs ) > ,
105
- range : TextRange ,
106
- name : & ast:: NameRef ,
107
- ) {
108
- mark:: hit!( qualify_path_unqualified_name) ;
109
- let group_label = GroupLabel ( format ! ( "Qualify {}" , name) ) ;
110
- for ( import, _) in proposed_imports {
111
- acc. add_group (
112
- & group_label,
113
- ASSIST_ID ,
114
- format ! ( "Qualify as `{}`" , & import) ,
115
- range,
116
- |builder| builder. replace ( range, mod_path_to_ast ( & import) . to_string ( ) ) ,
117
- ) ;
118
- }
98
+ enum QualifyCandidate < ' db > {
99
+ QualifierStart ( ast:: PathSegment ) ,
100
+ UnqualifiedName ,
101
+ TraitAssocItem ( ast:: Path , ast:: PathSegment ) ,
102
+ TraitMethod ( & ' db RootDatabase , ast:: MethodCallExpr ) ,
119
103
}
120
104
121
- // a test that covers this -> `associated_trait_const`
122
- fn qualify_path_trait_assoc_item (
123
- acc : & mut Assists ,
124
- proposed_imports : Vec < ( hir:: ModPath , hir:: ItemInNs ) > ,
125
- range : TextRange ,
126
- qualifier : ast:: Path ,
127
- segment : ast:: PathSegment ,
128
- ) {
129
- mark:: hit!( qualify_path_trait_assoc_item) ;
130
- let group_label = GroupLabel ( format ! ( "Qualify {}" , & segment) ) ;
131
- for ( import, _) in proposed_imports {
132
- acc. add_group (
133
- & group_label,
134
- ASSIST_ID ,
135
- format ! ( "Qualify with cast as `{}`" , & import) ,
136
- range,
137
- |builder| {
105
+ impl QualifyCandidate < ' _ > {
106
+ fn qualify ( & self , mut replacer : impl FnMut ( String ) , import : hir:: ModPath , item : hir:: ItemInNs ) {
107
+ match self {
108
+ QualifyCandidate :: QualifierStart ( segment) => {
138
109
let import = mod_path_to_ast ( & import) ;
139
- builder. replace ( range, format ! ( "<{} as {}>::{}" , qualifier, import, segment) ) ;
140
- } ,
141
- ) ;
110
+ replacer ( format ! ( "{}::{}" , import, segment) ) ;
111
+ }
112
+ QualifyCandidate :: UnqualifiedName => replacer ( mod_path_to_ast ( & import) . to_string ( ) ) ,
113
+ QualifyCandidate :: TraitAssocItem ( qualifier, segment) => {
114
+ let import = mod_path_to_ast ( & import) ;
115
+ replacer ( format ! ( "<{} as {}>::{}" , qualifier, import, segment) ) ;
116
+ }
117
+ & QualifyCandidate :: TraitMethod ( db, ref mcall_expr) => {
118
+ Self :: qualify_trait_method ( db, mcall_expr, replacer, import, item) ;
119
+ }
120
+ }
121
+ }
122
+
123
+ fn qualify_trait_method (
124
+ db : & RootDatabase ,
125
+ mcall_expr : & ast:: MethodCallExpr ,
126
+ mut replacer : impl FnMut ( String ) ,
127
+ import : hir:: ModPath ,
128
+ item : hir:: ItemInNs ,
129
+ ) -> Option < ( ) > {
130
+ let receiver = mcall_expr. receiver ( ) ?;
131
+ let trait_method_name = mcall_expr. name_ref ( ) ?;
132
+ let arg_list = mcall_expr. arg_list ( ) . map ( |arg_list| arg_list. args ( ) ) ;
133
+ let trait_ = item_as_trait ( item) ?;
134
+ let method = find_trait_method ( db, trait_, & trait_method_name) ?;
135
+ if let Some ( self_access) = method. self_param ( db) . map ( |sp| sp. access ( db) ) {
136
+ let import = mod_path_to_ast ( & import) ;
137
+ let receiver = match self_access {
138
+ hir:: Access :: Shared => make:: expr_ref ( receiver, false ) ,
139
+ hir:: Access :: Exclusive => make:: expr_ref ( receiver, true ) ,
140
+ hir:: Access :: Owned => receiver,
141
+ } ;
142
+ replacer ( format ! (
143
+ "{}::{}{}" ,
144
+ import,
145
+ trait_method_name,
146
+ match arg_list. clone( ) {
147
+ Some ( args) => make:: arg_list( iter:: once( receiver) . chain( args) ) ,
148
+ None => make:: arg_list( iter:: once( receiver) ) ,
149
+ }
150
+ ) ) ;
151
+ }
152
+ Some ( ( ) )
142
153
}
143
154
}
144
155
145
- // a test that covers this -> `trait_method`
146
- fn qualify_path_trait_method (
147
- acc : & mut Assists ,
156
+ fn find_trait_method (
148
157
db : & RootDatabase ,
149
- proposed_imports : Vec < ( hir:: ModPath , hir:: ItemInNs ) > ,
150
- range : TextRange ,
151
- mcall_expr : ast:: MethodCallExpr ,
152
- ) -> Option < ( ) > {
153
- mark:: hit!( qualify_path_trait_method) ;
154
-
155
- let receiver = mcall_expr. receiver ( ) ?;
156
- let trait_method_name = mcall_expr. name_ref ( ) ?;
157
- let arg_list = mcall_expr. arg_list ( ) . map ( |arg_list| arg_list. args ( ) ) ;
158
- let group_label = GroupLabel ( format ! ( "Qualify {}" , trait_method_name) ) ;
159
- let find_method = |item : & hir:: AssocItem | {
160
- item. name ( db) . map ( |name| name == trait_method_name. as_name ( ) ) . unwrap_or ( false )
161
- } ;
162
- for ( import, trait_) in proposed_imports. into_iter ( ) . filter_map ( filter_trait) {
163
- acc. add_group (
164
- & group_label,
165
- ASSIST_ID ,
166
- format ! ( "Qualify `{}`" , & import) ,
167
- range,
168
- |builder| {
169
- let import = mod_path_to_ast ( & import) ;
170
- if let Some ( hir:: AssocItem :: Function ( method) ) =
171
- trait_. items ( db) . into_iter ( ) . find ( find_method)
172
- {
173
- if let Some ( self_access) = method. self_param ( db) . map ( |sp| sp. access ( db) ) {
174
- let receiver = receiver. clone ( ) ;
175
- let receiver = match self_access {
176
- hir:: Access :: Shared => make:: expr_ref ( receiver, false ) ,
177
- hir:: Access :: Exclusive => make:: expr_ref ( receiver, true ) ,
178
- hir:: Access :: Owned => receiver,
179
- } ;
180
- builder. replace (
181
- range,
182
- format ! (
183
- "{}::{}{}" ,
184
- import,
185
- trait_method_name,
186
- match arg_list. clone( ) {
187
- Some ( args) => make:: arg_list( iter:: once( receiver) . chain( args) ) ,
188
- None => make:: arg_list( iter:: once( receiver) ) ,
189
- }
190
- ) ,
191
- ) ;
192
- }
193
- }
194
- } ,
195
- ) ;
158
+ trait_ : hir:: Trait ,
159
+ trait_method_name : & ast:: NameRef ,
160
+ ) -> Option < hir:: Function > {
161
+ if let Some ( hir:: AssocItem :: Function ( method) ) =
162
+ trait_. items ( db) . into_iter ( ) . find ( |item : & hir:: AssocItem | {
163
+ item. name ( db) . map ( |name| name == trait_method_name. as_name ( ) ) . unwrap_or ( false )
164
+ } )
165
+ {
166
+ Some ( method)
167
+ } else {
168
+ None
196
169
}
197
- Some ( ( ) )
198
170
}
199
171
200
- fn filter_trait (
201
- ( import, trait_) : ( hir:: ModPath , hir:: ItemInNs ) ,
202
- ) -> Option < ( hir:: ModPath , hir:: Trait ) > {
203
- if let hir:: ModuleDef :: Trait ( trait_) = hir:: ModuleDef :: from ( trait_. as_module_def_id ( ) ?) {
204
- Some ( ( import, trait_) )
172
+ fn item_as_trait ( item : hir:: ItemInNs ) -> Option < hir:: Trait > {
173
+ if let hir:: ModuleDef :: Trait ( trait_) = hir:: ModuleDef :: from ( item. as_module_def_id ( ) ?) {
174
+ Some ( trait_)
205
175
} else {
206
176
None
207
177
}
208
178
}
209
179
180
+ fn group_label ( candidate : & ImportCandidate ) -> GroupLabel {
181
+ let name = match candidate {
182
+ ImportCandidate :: UnqualifiedName ( it) | ImportCandidate :: QualifierStart ( it) => & it. name ,
183
+ ImportCandidate :: TraitAssocItem ( it) | ImportCandidate :: TraitMethod ( it) => & it. name ,
184
+ } ;
185
+ GroupLabel ( format ! ( "Qualify {}" , name) )
186
+ }
187
+
188
+ fn label ( candidate : & ImportCandidate , import : & hir:: ModPath ) -> String {
189
+ match candidate {
190
+ ImportCandidate :: UnqualifiedName ( _) => format ! ( "Qualify as `{}`" , & import) ,
191
+ ImportCandidate :: QualifierStart ( _) => format ! ( "Qualify with `{}`" , & import) ,
192
+ ImportCandidate :: TraitAssocItem ( _) => format ! ( "Qualify `{}`" , & import) ,
193
+ ImportCandidate :: TraitMethod ( _) => format ! ( "Qualify with cast as `{}`" , & import) ,
194
+ }
195
+ }
196
+
210
197
#[ cfg( test) ]
211
198
mod tests {
212
199
use crate :: tests:: { check_assist, check_assist_not_applicable, check_assist_target} ;
0 commit comments