Skip to content

Commit c1c9e10

Browse files
committed
Auto merge of #15832 - Young-Flash:generate_mut_trait, r=lnicola
feat: add generate_mut_trait_impl assist ![generate_mut_trait_impl](https://github.com/rust-lang/rust-analyzer/assets/71162630/362a5a93-e109-4ffc-996e-9b6e4f54fcfa) Generate proper `index_mut` method body refer to `index` method body may impossible due to the unpredicable case (#15581). Here just leave the `index_mut` method body be same as `index` method body, user can modify it manually to meet their need.
2 parents 6943228 + b84940b commit c1c9e10

File tree

5 files changed

+248
-0
lines changed

5 files changed

+248
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
use ide_db::famous_defs::FamousDefs;
2+
use syntax::{
3+
ast::{self, make},
4+
ted, AstNode,
5+
};
6+
7+
use crate::{AssistContext, AssistId, AssistKind, Assists};
8+
9+
// FIXME: Generate proper `index_mut` method body refer to `index` method body may impossible due to the unpredicable case [#15581].
10+
// Here just leave the `index_mut` method body be same as `index` method body, user can modify it manually to meet their need.
11+
12+
// Assist: generate_mut_trait_impl
13+
//
14+
// Adds a IndexMut impl from the `Index` trait.
15+
//
16+
// ```
17+
// # //- minicore: index
18+
// pub enum Axis { X = 0, Y = 1, Z = 2 }
19+
//
20+
// impl<T> core::ops::Index$0<Axis> for [T; 3] {
21+
// type Output = T;
22+
//
23+
// fn index(&self, index: Axis) -> &Self::Output {
24+
// &self[index as usize]
25+
// }
26+
// }
27+
// ```
28+
// ->
29+
// ```
30+
// pub enum Axis { X = 0, Y = 1, Z = 2 }
31+
//
32+
// $0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
33+
// fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
34+
// &self[index as usize]
35+
// }
36+
// }
37+
//
38+
// impl<T> core::ops::Index<Axis> for [T; 3] {
39+
// type Output = T;
40+
//
41+
// fn index(&self, index: Axis) -> &Self::Output {
42+
// &self[index as usize]
43+
// }
44+
// }
45+
// ```
46+
pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
47+
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
48+
49+
let trait_ = impl_def.trait_()?;
50+
if let ast::Type::PathType(trait_path) = trait_.clone() {
51+
let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?;
52+
let scope = ctx.sema.scope(trait_path.syntax())?;
53+
if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? {
54+
return None;
55+
}
56+
}
57+
58+
// Index -> IndexMut
59+
let index_trait = impl_def
60+
.syntax()
61+
.descendants()
62+
.filter_map(ast::NameRef::cast)
63+
.find(|it| it.text() == "Index")?;
64+
ted::replace(
65+
index_trait.syntax(),
66+
make::path_segment(make::name_ref("IndexMut")).clone_for_update().syntax(),
67+
);
68+
69+
// index -> index_mut
70+
let trait_method_name = impl_def
71+
.syntax()
72+
.descendants()
73+
.filter_map(ast::Name::cast)
74+
.find(|it| it.text() == "index")?;
75+
ted::replace(trait_method_name.syntax(), make::name("index_mut").clone_for_update().syntax());
76+
77+
let type_alias = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast)?;
78+
ted::remove(type_alias.syntax());
79+
80+
// &self -> &mut self
81+
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+
// &Self::Output -> &mut Self::Output
87+
let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?;
88+
ted::replace(
89+
ret_type.syntax(),
90+
make::ret_type(make::ty("&mut Self::Output")).clone_for_update().syntax(),
91+
);
92+
93+
let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it {
94+
ast::AssocItem::Fn(f) => Some(f),
95+
_ => None,
96+
})?;
97+
98+
let assoc_list = make::assoc_item_list().clone_for_update();
99+
assoc_list.add_item(syntax::ast::AssocItem::Fn(fn_));
100+
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
101+
102+
let target = impl_def.syntax().text_range();
103+
acc.add(
104+
AssistId("generate_mut_trait_impl", AssistKind::Generate),
105+
"Generate `IndexMut` impl from this `Index` trait",
106+
target,
107+
|edit| {
108+
edit.insert(target.start(), format!("$0{}\n\n", impl_def.to_string()));
109+
},
110+
)
111+
}
112+
113+
#[cfg(test)]
114+
mod tests {
115+
use crate::tests::{check_assist, check_assist_not_applicable};
116+
117+
use super::*;
118+
119+
#[test]
120+
fn test_generate_mut_trait_impl() {
121+
check_assist(
122+
generate_mut_trait_impl,
123+
r#"
124+
//- minicore: index
125+
pub enum Axis { X = 0, Y = 1, Z = 2 }
126+
127+
impl<T> core::ops::Index$0<Axis> for [T; 3] {
128+
type Output = T;
129+
130+
fn index(&self, index: Axis) -> &Self::Output {
131+
&self[index as usize]
132+
}
133+
}
134+
"#,
135+
r#"
136+
pub enum Axis { X = 0, Y = 1, Z = 2 }
137+
138+
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
139+
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
140+
&self[index as usize]
141+
}
142+
}
143+
144+
impl<T> core::ops::Index<Axis> for [T; 3] {
145+
type Output = T;
146+
147+
fn index(&self, index: Axis) -> &Self::Output {
148+
&self[index as usize]
149+
}
150+
}
151+
"#,
152+
);
153+
154+
check_assist(
155+
generate_mut_trait_impl,
156+
r#"
157+
//- minicore: index
158+
pub enum Axis { X = 0, Y = 1, Z = 2 }
159+
160+
impl<T> core::ops::Index$0<Axis> for [T; 3] where T: Copy {
161+
type Output = T;
162+
163+
fn index(&self, index: Axis) -> &Self::Output {
164+
let var_name = &self[index as usize];
165+
var_name
166+
}
167+
}
168+
"#,
169+
r#"
170+
pub enum Axis { X = 0, Y = 1, Z = 2 }
171+
172+
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] where T: Copy {
173+
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
174+
let var_name = &self[index as usize];
175+
var_name
176+
}
177+
}
178+
179+
impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
180+
type Output = T;
181+
182+
fn index(&self, index: Axis) -> &Self::Output {
183+
let var_name = &self[index as usize];
184+
var_name
185+
}
186+
}
187+
"#,
188+
);
189+
}
190+
191+
#[test]
192+
fn test_generate_mut_trait_impl_not_applicable() {
193+
check_assist_not_applicable(
194+
generate_mut_trait_impl,
195+
r#"
196+
pub trait Index<Idx: ?Sized> {}
197+
198+
impl<T> Index$0<i32> for [T; 3] {}
199+
"#,
200+
);
201+
}
202+
}

crates/ide-assists/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ mod handlers {
160160
mod generate_getter_or_setter;
161161
mod generate_impl;
162162
mod generate_is_empty_from_len;
163+
mod generate_mut_trait_impl;
163164
mod generate_new;
164165
mod generate_delegate_methods;
165166
mod generate_trait_from_impl;
@@ -274,6 +275,7 @@ mod handlers {
274275
generate_function::generate_function,
275276
generate_impl::generate_impl,
276277
generate_impl::generate_trait_impl,
278+
generate_mut_trait_impl::generate_mut_trait_impl,
277279
generate_is_empty_from_len::generate_is_empty_from_len,
278280
generate_new::generate_new,
279281
generate_trait_from_impl::generate_trait_from_impl,

crates/ide-assists/src/tests/generated.rs

+36
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,42 @@ impl MyStruct {
15381538
)
15391539
}
15401540

1541+
#[test]
1542+
fn doctest_generate_mut_trait_impl() {
1543+
check_doc_test(
1544+
"generate_mut_trait_impl",
1545+
r#####"
1546+
//- minicore: index
1547+
pub enum Axis { X = 0, Y = 1, Z = 2 }
1548+
1549+
impl<T> core::ops::Index$0<Axis> for [T; 3] {
1550+
type Output = T;
1551+
1552+
fn index(&self, index: Axis) -> &Self::Output {
1553+
&self[index as usize]
1554+
}
1555+
}
1556+
"#####,
1557+
r#####"
1558+
pub enum Axis { X = 0, Y = 1, Z = 2 }
1559+
1560+
$0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
1561+
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
1562+
&self[index as usize]
1563+
}
1564+
}
1565+
1566+
impl<T> core::ops::Index<Axis> for [T; 3] {
1567+
type Output = T;
1568+
1569+
fn index(&self, index: Axis) -> &Self::Output {
1570+
&self[index as usize]
1571+
}
1572+
}
1573+
"#####,
1574+
)
1575+
}
1576+
15411577
#[test]
15421578
fn doctest_generate_new() {
15431579
check_doc_test(

crates/ide-db/src/famous_defs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ impl FamousDefs<'_, '_> {
5454
self.find_trait("core:convert:Into")
5555
}
5656

57+
pub fn core_convert_Index(&self) -> Option<Trait> {
58+
self.find_trait("core:ops:Index")
59+
}
60+
5761
pub fn core_option_Option(&self) -> Option<Enum> {
5862
self.find_enum("core:option:Option")
5963
}

crates/syntax/src/ast/make.rs

+4
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,10 @@ pub fn self_param() -> ast::SelfParam {
852852
ast_from_text("fn f(&self) { }")
853853
}
854854

855+
pub fn mut_self_param() -> ast::SelfParam {
856+
ast_from_text("fn f(&mut self) { }")
857+
}
858+
855859
pub fn ret_type(ty: ast::Type) -> ast::RetType {
856860
ast_from_text(&format!("fn f() -> {ty} {{ }}"))
857861
}

0 commit comments

Comments
 (0)