1use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
2use syntax::{
3 AstNode, T,
4 ast::{self, edit_in_place::Indent, make},
5 ted,
6};
7
8use crate::{AssistContext, AssistId, Assists};
9
10pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
48 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
49 let indent = impl_def.indent_level();
50
51 let ast::Type::PathType(path) = impl_def.trait_()? else {
52 return None;
53 };
54 let trait_name = path.path()?.segment()?.name_ref()?;
55
56 let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?;
57 let famous = FamousDefs(&ctx.sema, scope.krate());
58
59 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
60 let trait_new = get_trait_mut(&trait_, famous)?;
61
62 ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax());
64
65 let (trait_method_name, new_trait_method_name) = impl_def
67 .syntax()
68 .descendants()
69 .filter_map(ast::Name::cast)
70 .find_map(process_method_name)?;
71 ted::replace(
72 trait_method_name.syntax(),
73 make::name(new_trait_method_name).clone_for_update().syntax(),
74 );
75
76 if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) {
77 ted::remove(type_alias.syntax());
78 }
79
80 let mut_self_param = make::mut_self_param();
82 let self_param: ast::SelfParam =
83 impl_def.syntax().descendants().find_map(ast::SelfParam::cast)?;
84 ted::replace(self_param.syntax(), mut_self_param.clone_for_update().syntax());
85
86 let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?;
88 let new_ret_type = process_ret_type(&ret_type)?;
89 ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax());
90
91 let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it {
92 ast::AssocItem::Fn(f) => Some(f),
93 _ => None,
94 })?;
95 let _ = process_ref_mut(&fn_);
96
97 let assoc_list = make::assoc_item_list(None).clone_for_update();
98 ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
99 impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_));
100
101 let target = impl_def.syntax().text_range();
102 acc.add(
103 AssistId::generate("generate_mut_trait_impl"),
104 format!("Generate `{trait_new}` impl from this `{trait_name}` trait"),
105 target,
106 |edit| {
107 edit.insert(
108 target.start(),
109 if ctx.config.snippet_cap.is_some() {
110 format!("$0{impl_def}\n\n{indent}")
111 } else {
112 format!("{impl_def}\n\n{indent}")
113 },
114 );
115 },
116 )
117}
118
119fn process_ref_mut(fn_: &ast::Fn) -> Option<()> {
120 let expr = fn_.body()?.tail_expr()?;
121 match &expr {
122 ast::Expr::RefExpr(ref_expr) if ref_expr.mut_token().is_none() => {
123 ted::insert_all_raw(
124 ted::Position::after(ref_expr.amp_token()?),
125 vec![make::token(T![mut]).into(), make::tokens::whitespace(" ").into()],
126 );
127 }
128 _ => {}
129 }
130 None
131}
132
133fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> {
134 let trait_ = Some(apply_trait);
135 if trait_ == famous.core_convert_Index().as_ref() {
136 return Some("IndexMut");
137 }
138 if trait_ == famous.core_convert_AsRef().as_ref() {
139 return Some("AsMut");
140 }
141 if trait_ == famous.core_borrow_Borrow().as_ref() {
142 return Some("BorrowMut");
143 }
144 if trait_ == famous.core_ops_Deref().as_ref() {
145 return Some("DerefMut");
146 }
147 None
148}
149
150fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> {
151 let new_name = match &*name.text() {
152 "index" => "index_mut",
153 "as_ref" => "as_mut",
154 "borrow" => "borrow_mut",
155 "deref" => "deref_mut",
156 _ => return None,
157 };
158 Some((name, new_name))
159}
160
161fn process_ret_type(ref_ty: &ast::RetType) -> Option<ast::Type> {
162 let ty = ref_ty.ty()?;
163 let ast::Type::RefType(ref_type) = ty else {
164 return None;
165 };
166 Some(make::ty_ref(ref_type.ty()?, true))
167}
168
169#[cfg(test)]
170mod tests {
171 use crate::{
172 AssistConfig,
173 tests::{TEST_CONFIG, check_assist, check_assist_not_applicable, check_assist_with_config},
174 };
175
176 use super::*;
177
178 #[test]
179 fn test_generate_mut_trait_impl() {
180 check_assist(
181 generate_mut_trait_impl,
182 r#"
183//- minicore: index
184pub enum Axis { X = 0, Y = 1, Z = 2 }
185
186impl<T> core::ops::Index$0<Axis> for [T; 3] {
187 type Output = T;
188
189 fn index(&self, index: Axis) -> &Self::Output {
190 &self[index as usize]
191 }
192}
193"#,
194 r#"
195pub enum Axis { X = 0, Y = 1, Z = 2 }
196
197$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
198 fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
199 &mut self[index as usize]
200 }
201}
202
203impl<T> core::ops::Index<Axis> for [T; 3] {
204 type Output = T;
205
206 fn index(&self, index: Axis) -> &Self::Output {
207 &self[index as usize]
208 }
209}
210"#,
211 );
212
213 check_assist(
214 generate_mut_trait_impl,
215 r#"
216//- minicore: index
217pub enum Axis { X = 0, Y = 1, Z = 2 }
218
219impl<T> core::ops::Index$0<Axis> for [T; 3] where T: Copy {
220 type Output = T;
221
222 fn index(&self, index: Axis) -> &Self::Output {
223 let var_name = &self[index as usize];
224 var_name
225 }
226}
227"#,
228 r#"
229pub enum Axis { X = 0, Y = 1, Z = 2 }
230
231$0impl<T> core::ops::IndexMut<Axis> for [T; 3] where T: Copy {
232 fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
233 let var_name = &self[index as usize];
234 var_name
235 }
236}
237
238impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
239 type Output = T;
240
241 fn index(&self, index: Axis) -> &Self::Output {
242 let var_name = &self[index as usize];
243 var_name
244 }
245}
246"#,
247 );
248
249 check_assist(
250 generate_mut_trait_impl,
251 r#"
252//- minicore: as_ref
253struct Foo(i32);
254
255impl core::convert::AsRef$0<i32> for Foo {
256 fn as_ref(&self) -> &i32 {
257 &self.0
258 }
259}
260"#,
261 r#"
262struct Foo(i32);
263
264$0impl core::convert::AsMut<i32> for Foo {
265 fn as_mut(&mut self) -> &mut i32 {
266 &mut self.0
267 }
268}
269
270impl core::convert::AsRef<i32> for Foo {
271 fn as_ref(&self) -> &i32 {
272 &self.0
273 }
274}
275"#,
276 );
277
278 check_assist(
279 generate_mut_trait_impl,
280 r#"
281//- minicore: deref
282struct Foo(i32);
283
284impl core::ops::Deref$0 for Foo {
285 type Target = i32;
286
287 fn deref(&self) -> &Self::Target {
288 &self.0
289 }
290}
291"#,
292 r#"
293struct Foo(i32);
294
295$0impl core::ops::DerefMut for Foo {
296 fn deref_mut(&mut self) -> &mut Self::Target {
297 &mut self.0
298 }
299}
300
301impl core::ops::Deref for Foo {
302 type Target = i32;
303
304 fn deref(&self) -> &Self::Target {
305 &self.0
306 }
307}
308"#,
309 );
310 }
311
312 #[test]
313 fn test_generate_mut_trait_impl_non_zero_indent() {
314 check_assist(
315 generate_mut_trait_impl,
316 r#"
317//- minicore: index
318mod foo {
319 pub enum Axis { X = 0, Y = 1, Z = 2 }
320
321 impl<T> core::ops::Index$0<Axis> for [T; 3] where T: Copy {
322 type Output = T;
323
324 fn index(&self, index: Axis) -> &Self::Output {
325 let var_name = &self[index as usize];
326 var_name
327 }
328 }
329}
330"#,
331 r#"
332mod foo {
333 pub enum Axis { X = 0, Y = 1, Z = 2 }
334
335 $0impl<T> core::ops::IndexMut<Axis> for [T; 3] where T: Copy {
336 fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
337 let var_name = &self[index as usize];
338 var_name
339 }
340 }
341
342 impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
343 type Output = T;
344
345 fn index(&self, index: Axis) -> &Self::Output {
346 let var_name = &self[index as usize];
347 var_name
348 }
349 }
350}
351"#,
352 );
353
354 check_assist(
355 generate_mut_trait_impl,
356 r#"
357//- minicore: index
358mod foo {
359 mod bar {
360 pub enum Axis { X = 0, Y = 1, Z = 2 }
361
362 impl<T> core::ops::Index$0<Axis> for [T; 3] where T: Copy {
363 type Output = T;
364
365 fn index(&self, index: Axis) -> &Self::Output {
366 let var_name = &self[index as usize];
367 var_name
368 }
369 }
370 }
371}
372"#,
373 r#"
374mod foo {
375 mod bar {
376 pub enum Axis { X = 0, Y = 1, Z = 2 }
377
378 $0impl<T> core::ops::IndexMut<Axis> for [T; 3] where T: Copy {
379 fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
380 let var_name = &self[index as usize];
381 var_name
382 }
383 }
384
385 impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
386 type Output = T;
387
388 fn index(&self, index: Axis) -> &Self::Output {
389 let var_name = &self[index as usize];
390 var_name
391 }
392 }
393 }
394}
395"#,
396 );
397 }
398
399 #[test]
400 fn test_generate_mut_trait_impl_not_applicable() {
401 check_assist_not_applicable(
402 generate_mut_trait_impl,
403 r#"
404pub trait Index<Idx: ?Sized> {}
405
406impl<T> Index$0<i32> for [T; 3] {}
407"#,
408 );
409 check_assist_not_applicable(
410 generate_mut_trait_impl,
411 r#"
412pub trait AsRef<T: ?Sized> {}
413
414impl AsRef$0<i32> for [T; 3] {}
415"#,
416 );
417 }
418
419 #[test]
420 fn no_snippets() {
421 check_assist_with_config(
422 generate_mut_trait_impl,
423 AssistConfig { snippet_cap: None, ..TEST_CONFIG },
424 r#"
425//- minicore: index
426pub enum Axis { X = 0, Y = 1, Z = 2 }
427
428impl<T> core::ops::Index$0<Axis> for [T; 3] {
429 type Output = T;
430
431 fn index(&self, index: Axis) -> &Self::Output {
432 &self[index as usize]
433 }
434}
435"#,
436 r#"
437pub enum Axis { X = 0, Y = 1, Z = 2 }
438
439impl<T> core::ops::IndexMut<Axis> for [T; 3] {
440 fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
441 &mut self[index as usize]
442 }
443}
444
445impl<T> core::ops::Index<Axis> for [T; 3] {
446 type Output = T;
447
448 fn index(&self, index: Axis) -> &Self::Output {
449 &self[index as usize]
450 }
451}
452"#,
453 );
454 }
455}