ide_assists/handlers/
unqualify_method_call.rs1use hir::AsAssocItem;
2use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence};
3
4use crate::{AssistContext, AssistId, Assists};
5
6pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
26 let call = ctx.find_node_at_offset::<ast::CallExpr>()?;
27 let ast::Expr::PathExpr(path_expr) = call.expr()? else { return None };
28 let path = path_expr.path()?;
29
30 let cursor_in_range = path.syntax().text_range().contains_range(ctx.selection_trimmed());
31 if !cursor_in_range {
32 return None;
33 }
34
35 let args = call.arg_list()?;
36 let first_arg = args.args().next()?;
37
38 let qualifier = path.qualifier()?;
39 let method_name = path.segment()?.name_ref()?;
40
41 let scope = ctx.sema.scope(path.syntax())?;
42 let res = ctx.sema.resolve_path(&path)?;
43 let hir::PathResolution::Def(hir::ModuleDef::Function(fun)) = res else { return None };
44 if !fun.has_self_param(ctx.sema.db) {
45 return None;
46 }
47
48 acc.add(
49 AssistId::refactor_rewrite("unqualify_method_call"),
50 "Unqualify method call",
51 call.syntax().text_range(),
52 |builder| {
53 let editor = builder.make_editor(call.syntax());
54 let make = editor.make();
55
56 let new_arg_list = make.arg_list(args.args().skip(1));
57 let receiver = if first_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix) {
58 ast::Expr::from(make.expr_paren(first_arg.clone()))
59 } else {
60 first_arg.clone()
61 };
62 let method_call = make.expr_method_call(receiver, method_name, new_arg_list);
63
64 editor.replace(call.syntax(), method_call.syntax());
65
66 if let Some(fun) = fun.as_assoc_item(ctx.db())
67 && let Some(trait_) = fun.container_or_implemented_trait(ctx.db())
68 && !scope.can_use_trait_methods(trait_)
69 {
70 add_import(qualifier, ctx, &editor);
71 }
72
73 builder.add_file_edits(ctx.vfs_file_id(), editor);
74 },
75 )
76}
77
78fn add_import(
79 qualifier: ast::Path,
80 ctx: &AssistContext<'_, '_>,
81 editor: &syntax::syntax_editor::SyntaxEditor,
82) {
83 if let Some(path_segment) = qualifier.segment() {
84 let path_type = path_segment.qualifying_trait();
86 let import = match path_type {
87 Some(it) => {
88 if let Some(path) = it.path() {
89 path
90 } else {
91 return;
92 }
93 }
94 None => qualifier,
95 };
96
97 if import.coloncolon_token().is_none() {
99 return;
100 }
101
102 let scope = ide_db::imports::insert_use::ImportScope::find_insert_use_container(
103 import.syntax(),
104 &ctx.sema,
105 );
106
107 if let Some(scope) = scope {
108 ide_db::imports::insert_use::insert_use_with_editor(
109 &scope,
110 import,
111 &ctx.config.insert_use,
112 editor,
113 );
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use crate::tests::{check_assist, check_assist_not_applicable};
121
122 use super::*;
123
124 #[test]
125 fn unqualify_method_call_simple() {
126 check_assist(
127 unqualify_method_call,
128 r#"
129struct S;
130impl S { fn f(self, S: S) {} }
131fn f() { S::$0f(S, S); }"#,
132 r#"
133struct S;
134impl S { fn f(self, S: S) {} }
135fn f() { S.f(S); }"#,
136 );
137 }
138
139 #[test]
140 fn unqualify_method_call_trait() {
141 check_assist(
142 unqualify_method_call,
143 r#"
144//- minicore: add
145fn f() { <u32 as core::ops::Add>::$0add(2, 2); }"#,
146 r#"
147use core::ops::Add;
148
149fn f() { 2.add(2); }"#,
150 );
151
152 check_assist(
153 unqualify_method_call,
154 r#"
155//- minicore: add
156fn f() { core::ops::Add::$0add(2, 2); }"#,
157 r#"
158use core::ops::Add;
159
160fn f() { 2.add(2); }"#,
161 );
162
163 check_assist(
164 unqualify_method_call,
165 r#"
166//- minicore: add
167use core::ops::Add;
168fn f() { <_>::$0add(2, 2); }"#,
169 r#"
170use core::ops::Add;
171fn f() { 2.add(2); }"#,
172 );
173 }
174
175 #[test]
176 fn unqualify_method_call_single_arg() {
177 check_assist(
178 unqualify_method_call,
179 r#"
180 struct S;
181 impl S { fn f(self) {} }
182 fn f() { S::$0f(S); }"#,
183 r#"
184 struct S;
185 impl S { fn f(self) {} }
186 fn f() { S.f(); }"#,
187 );
188 }
189
190 #[test]
191 fn unqualify_method_call_parens() {
192 check_assist(
193 unqualify_method_call,
194 r#"
195//- minicore: deref
196struct S;
197impl core::ops::Deref for S {
198 type Target = S;
199 fn deref(&self) -> &S { self }
200}
201fn f() { core::ops::Deref::$0deref(&S); }"#,
202 r#"
203use core::ops::Deref;
204
205struct S;
206impl core::ops::Deref for S {
207 type Target = S;
208 fn deref(&self) -> &S { self }
209}
210fn f() { (&S).deref(); }"#,
211 );
212 }
213
214 #[test]
215 fn unqualify_method_call_doesnt_apply_with_cursor_not_on_path() {
216 check_assist_not_applicable(
217 unqualify_method_call,
218 r#"
219//- minicore: add
220fn f() { core::ops::Add::add(2,$0 2); }"#,
221 );
222 }
223
224 #[test]
225 fn unqualify_method_call_doesnt_apply_with_no_self() {
226 check_assist_not_applicable(
227 unqualify_method_call,
228 r#"
229struct S;
230impl S { fn assoc(S: S, S: S) {} }
231fn f() { S::assoc$0(S, S); }"#,
232 );
233 }
234
235 #[test]
236 fn inherent_method() {
237 check_assist(
238 unqualify_method_call,
239 r#"
240mod foo {
241 pub struct Bar;
242 impl Bar {
243 pub fn bar(self) {}
244 }
245}
246
247fn baz() {
248 foo::Bar::b$0ar(foo::Bar);
249}
250 "#,
251 r#"
252mod foo {
253 pub struct Bar;
254 impl Bar {
255 pub fn bar(self) {}
256 }
257}
258
259fn baz() {
260 foo::Bar.bar();
261}
262 "#,
263 );
264 }
265
266 #[test]
267 fn trait_method_in_impl() {
268 check_assist(
269 unqualify_method_call,
270 r#"
271mod foo {
272 pub trait Bar {
273 pub fn bar(self) {}
274 }
275}
276
277struct Baz;
278impl foo::Bar for Baz {
279 fn bar(self) {
280 foo::Bar::b$0ar(Baz);
281 }
282}
283 "#,
284 r#"
285mod foo {
286 pub trait Bar {
287 pub fn bar(self) {}
288 }
289}
290
291struct Baz;
292impl foo::Bar for Baz {
293 fn bar(self) {
294 Baz.bar();
295 }
296}
297 "#,
298 );
299 }
300
301 #[test]
302 fn trait_method_already_imported() {
303 check_assist(
304 unqualify_method_call,
305 r#"
306mod foo {
307 pub struct Foo;
308 pub trait Bar {
309 pub fn bar(self) {}
310 }
311 impl Bar for Foo {
312 pub fn bar(self) {}
313 }
314}
315
316use foo::Bar;
317
318fn baz() {
319 foo::Bar::b$0ar(foo::Foo);
320}
321 "#,
322 r#"
323mod foo {
324 pub struct Foo;
325 pub trait Bar {
326 pub fn bar(self) {}
327 }
328 impl Bar for Foo {
329 pub fn bar(self) {}
330 }
331}
332
333use foo::Bar;
334
335fn baz() {
336 foo::Foo.bar();
337}
338 "#,
339 );
340 }
341}