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