1
- use std:: collections:: BTreeSet ;
2
-
3
- use syntax:: { ast, AstNode , TextRange } ;
1
+ use std:: iter;
2
+
3
+ use hir:: AsName ;
4
+ use ide_db:: RootDatabase ;
5
+ use syntax:: {
6
+ ast,
7
+ ast:: { make, ArgListOwner } ,
8
+ AstNode , TextRange ,
9
+ } ;
4
10
use test_utils:: mark;
5
11
6
12
use crate :: {
@@ -10,6 +16,8 @@ use crate::{
10
16
AssistId , AssistKind , GroupLabel ,
11
17
} ;
12
18
19
+ const ASSIST_ID : AssistId = AssistId ( "qualify_path" , AssistKind :: QuickFix ) ;
20
+
13
21
// Assist: qualify_path
14
22
//
15
23
// If the name is unresolved, provides all possible qualified paths for it.
@@ -53,30 +61,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
53
61
ImportCandidate :: UnqualifiedName ( candidate) => {
54
62
qualify_path_unqualified_name ( acc, proposed_imports, range, & candidate. name )
55
63
}
56
- ImportCandidate :: TraitAssocItem ( candidate ) => {
64
+ ImportCandidate :: TraitAssocItem ( _ ) => {
57
65
let path = ast:: Path :: cast ( import_assets. syntax_under_caret ( ) . clone ( ) ) ?;
58
66
let ( qualifier, segment) = ( path. qualifier ( ) ?, path. segment ( ) ?) ;
59
- qualify_path_trait_assoc_item (
60
- acc,
61
- proposed_imports,
62
- range,
63
- qualifier,
64
- segment,
65
- & candidate. name ,
66
- )
67
+ qualify_path_trait_assoc_item ( acc, proposed_imports, range, qualifier, segment)
67
68
}
68
- ImportCandidate :: TraitMethod ( candidate ) => {
69
+ ImportCandidate :: TraitMethod ( _ ) => {
69
70
let mcall_expr = ast:: MethodCallExpr :: cast ( import_assets. syntax_under_caret ( ) . clone ( ) ) ?;
70
- let receiver = mcall_expr. receiver ( ) ?;
71
- let name_ref = mcall_expr. name_ref ( ) ?;
72
- qualify_path_trait_method (
73
- acc,
74
- proposed_imports,
75
- range,
76
- receiver,
77
- name_ref,
78
- & candidate. name ,
79
- )
71
+ qualify_path_trait_method ( acc, ctx. sema . db , proposed_imports, range, mcall_expr) ?;
80
72
}
81
73
} ;
82
74
Some ( ( ) )
@@ -85,17 +77,17 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
85
77
// a test that covers this -> `associated_struct_const`
86
78
fn qualify_path_qualifier_start (
87
79
acc : & mut Assists ,
88
- proposed_imports : BTreeSet < hir:: ModPath > ,
80
+ proposed_imports : Vec < ( hir:: ModPath , hir :: ItemInNs ) > ,
89
81
range : TextRange ,
90
82
segment : ast:: PathSegment ,
91
- qualifier_start : & str ,
83
+ qualifier_start : & ast :: NameRef ,
92
84
) {
93
85
mark:: hit!( qualify_path_qualifier_start) ;
94
86
let group_label = GroupLabel ( format ! ( "Qualify {}" , qualifier_start) ) ;
95
- for import in proposed_imports {
87
+ for ( import, _ ) in proposed_imports {
96
88
acc. add_group (
97
89
& group_label,
98
- AssistId ( "qualify_path" , AssistKind :: QuickFix ) ,
90
+ ASSIST_ID ,
99
91
format ! ( "Qualify with `{}`" , & import) ,
100
92
range,
101
93
|builder| {
@@ -109,16 +101,16 @@ fn qualify_path_qualifier_start(
109
101
// a test that covers this -> `applicable_when_found_an_import_partial`
110
102
fn qualify_path_unqualified_name (
111
103
acc : & mut Assists ,
112
- proposed_imports : BTreeSet < hir:: ModPath > ,
104
+ proposed_imports : Vec < ( hir:: ModPath , hir :: ItemInNs ) > ,
113
105
range : TextRange ,
114
- name : & str ,
106
+ name : & ast :: NameRef ,
115
107
) {
116
108
mark:: hit!( qualify_path_unqualified_name) ;
117
109
let group_label = GroupLabel ( format ! ( "Qualify {}" , name) ) ;
118
- for import in proposed_imports {
110
+ for ( import, _ ) in proposed_imports {
119
111
acc. add_group (
120
112
& group_label,
121
- AssistId ( "qualify_path" , AssistKind :: QuickFix ) ,
113
+ ASSIST_ID ,
122
114
format ! ( "Qualify as `{}`" , & import) ,
123
115
range,
124
116
|builder| builder. replace ( range, mod_path_to_ast ( & import) . to_string ( ) ) ,
@@ -129,18 +121,17 @@ fn qualify_path_unqualified_name(
129
121
// a test that covers this -> `associated_trait_const`
130
122
fn qualify_path_trait_assoc_item (
131
123
acc : & mut Assists ,
132
- proposed_imports : BTreeSet < hir:: ModPath > ,
124
+ proposed_imports : Vec < ( hir:: ModPath , hir :: ItemInNs ) > ,
133
125
range : TextRange ,
134
126
qualifier : ast:: Path ,
135
127
segment : ast:: PathSegment ,
136
- trait_assoc_item_name : & str ,
137
128
) {
138
129
mark:: hit!( qualify_path_trait_assoc_item) ;
139
- let group_label = GroupLabel ( format ! ( "Qualify {}" , trait_assoc_item_name ) ) ;
140
- for import in proposed_imports {
130
+ let group_label = GroupLabel ( format ! ( "Qualify {}" , & segment ) ) ;
131
+ for ( import, _ ) in proposed_imports {
141
132
acc. add_group (
142
133
& group_label,
143
- AssistId ( "qualify_path" , AssistKind :: QuickFix ) ,
134
+ ASSIST_ID ,
144
135
format ! ( "Qualify with cast as `{}`" , & import) ,
145
136
range,
146
137
|builder| {
@@ -154,33 +145,74 @@ fn qualify_path_trait_assoc_item(
154
145
// a test that covers this -> `trait_method`
155
146
fn qualify_path_trait_method (
156
147
acc : & mut Assists ,
157
- proposed_imports : BTreeSet < hir:: ModPath > ,
148
+ db : & RootDatabase ,
149
+ proposed_imports : Vec < ( hir:: ModPath , hir:: ItemInNs ) > ,
158
150
range : TextRange ,
159
- receiver : ast:: Expr ,
160
- name_ref : ast:: NameRef ,
161
- trait_method_name : & str ,
162
- ) {
151
+ mcall_expr : ast:: MethodCallExpr ,
152
+ ) -> Option < ( ) > {
163
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 ( ) ) ;
164
158
let group_label = GroupLabel ( format ! ( "Qualify {}" , trait_method_name) ) ;
165
- for import in proposed_imports {
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) {
166
163
acc. add_group (
167
164
& group_label,
168
- AssistId ( "qualify_path" , AssistKind :: QuickFix ) , // < Does this still count as quickfix?
165
+ ASSIST_ID ,
169
166
format ! ( "Qualify `{}`" , & import) ,
170
167
range,
171
168
|builder| {
172
169
let import = mod_path_to_ast ( & import) ;
173
- // TODO: check the receiver self type and emit refs accordingly, don't discard other function parameters
174
- builder. replace ( range, format ! ( "{}::{}(&{})" , import, name_ref, receiver) ) ;
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
+ }
175
194
} ,
176
195
) ;
177
196
}
197
+ Some ( ( ) )
198
+ }
199
+
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_) )
205
+ } else {
206
+ None
207
+ }
178
208
}
179
209
180
210
#[ cfg( test) ]
181
211
mod tests {
182
- use super :: * ;
183
212
use crate :: tests:: { check_assist, check_assist_not_applicable, check_assist_target} ;
213
+
214
+ use super :: * ;
215
+
184
216
#[ test]
185
217
fn applicable_when_found_an_import_partial ( ) {
186
218
mark:: check!( qualify_path_unqualified_name) ;
0 commit comments