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+ } ;
410use test_utils:: mark;
511
612use crate :: {
@@ -10,6 +16,8 @@ use crate::{
1016 AssistId , AssistKind , GroupLabel ,
1117} ;
1218
19+ const ASSIST_ID : AssistId = AssistId ( "qualify_path" , AssistKind :: QuickFix ) ;
20+
1321// Assist: qualify_path
1422//
1523// 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<()>
5361 ImportCandidate :: UnqualifiedName ( candidate) => {
5462 qualify_path_unqualified_name ( acc, proposed_imports, range, & candidate. name )
5563 }
56- ImportCandidate :: TraitAssocItem ( candidate ) => {
64+ ImportCandidate :: TraitAssocItem ( _ ) => {
5765 let path = ast:: Path :: cast ( import_assets. syntax_under_caret ( ) . clone ( ) ) ?;
5866 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)
6768 }
68- ImportCandidate :: TraitMethod ( candidate ) => {
69+ ImportCandidate :: TraitMethod ( _ ) => {
6970 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) ?;
8072 }
8173 } ;
8274 Some ( ( ) )
@@ -85,17 +77,17 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
8577// a test that covers this -> `associated_struct_const`
8678fn qualify_path_qualifier_start (
8779 acc : & mut Assists ,
88- proposed_imports : BTreeSet < hir:: ModPath > ,
80+ proposed_imports : Vec < ( hir:: ModPath , hir :: ItemInNs ) > ,
8981 range : TextRange ,
9082 segment : ast:: PathSegment ,
91- qualifier_start : & str ,
83+ qualifier_start : & ast :: NameRef ,
9284) {
9385 mark:: hit!( qualify_path_qualifier_start) ;
9486 let group_label = GroupLabel ( format ! ( "Qualify {}" , qualifier_start) ) ;
95- for import in proposed_imports {
87+ for ( import, _ ) in proposed_imports {
9688 acc. add_group (
9789 & group_label,
98- AssistId ( "qualify_path" , AssistKind :: QuickFix ) ,
90+ ASSIST_ID ,
9991 format ! ( "Qualify with `{}`" , & import) ,
10092 range,
10193 |builder| {
@@ -109,16 +101,16 @@ fn qualify_path_qualifier_start(
109101// a test that covers this -> `applicable_when_found_an_import_partial`
110102fn qualify_path_unqualified_name (
111103 acc : & mut Assists ,
112- proposed_imports : BTreeSet < hir:: ModPath > ,
104+ proposed_imports : Vec < ( hir:: ModPath , hir :: ItemInNs ) > ,
113105 range : TextRange ,
114- name : & str ,
106+ name : & ast :: NameRef ,
115107) {
116108 mark:: hit!( qualify_path_unqualified_name) ;
117109 let group_label = GroupLabel ( format ! ( "Qualify {}" , name) ) ;
118- for import in proposed_imports {
110+ for ( import, _ ) in proposed_imports {
119111 acc. add_group (
120112 & group_label,
121- AssistId ( "qualify_path" , AssistKind :: QuickFix ) ,
113+ ASSIST_ID ,
122114 format ! ( "Qualify as `{}`" , & import) ,
123115 range,
124116 |builder| builder. replace ( range, mod_path_to_ast ( & import) . to_string ( ) ) ,
@@ -129,18 +121,17 @@ fn qualify_path_unqualified_name(
129121// a test that covers this -> `associated_trait_const`
130122fn qualify_path_trait_assoc_item (
131123 acc : & mut Assists ,
132- proposed_imports : BTreeSet < hir:: ModPath > ,
124+ proposed_imports : Vec < ( hir:: ModPath , hir :: ItemInNs ) > ,
133125 range : TextRange ,
134126 qualifier : ast:: Path ,
135127 segment : ast:: PathSegment ,
136- trait_assoc_item_name : & str ,
137128) {
138129 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 {
141132 acc. add_group (
142133 & group_label,
143- AssistId ( "qualify_path" , AssistKind :: QuickFix ) ,
134+ ASSIST_ID ,
144135 format ! ( "Qualify with cast as `{}`" , & import) ,
145136 range,
146137 |builder| {
@@ -154,33 +145,74 @@ fn qualify_path_trait_assoc_item(
154145// a test that covers this -> `trait_method`
155146fn qualify_path_trait_method (
156147 acc : & mut Assists ,
157- proposed_imports : BTreeSet < hir:: ModPath > ,
148+ db : & RootDatabase ,
149+ proposed_imports : Vec < ( hir:: ModPath , hir:: ItemInNs ) > ,
158150 range : TextRange ,
159- receiver : ast:: Expr ,
160- name_ref : ast:: NameRef ,
161- trait_method_name : & str ,
162- ) {
151+ mcall_expr : ast:: MethodCallExpr ,
152+ ) -> Option < ( ) > {
163153 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 ( ) ) ;
164158 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) {
166163 acc. add_group (
167164 & group_label,
168- AssistId ( "qualify_path" , AssistKind :: QuickFix ) , // < Does this still count as quickfix?
165+ ASSIST_ID ,
169166 format ! ( "Qualify `{}`" , & import) ,
170167 range,
171168 |builder| {
172169 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+ }
175194 } ,
176195 ) ;
177196 }
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+ }
178208}
179209
180210#[ cfg( test) ]
181211mod tests {
182- use super :: * ;
183212 use crate :: tests:: { check_assist, check_assist_not_applicable, check_assist_target} ;
213+
214+ use super :: * ;
215+
184216 #[ test]
185217 fn applicable_when_found_an_import_partial ( ) {
186218 mark:: check!( qualify_path_unqualified_name) ;
0 commit comments