Skip to content

Commit ce9b174

Browse files
committed
Auto merge of #12276 - jonas-schievink:improve-generate-deref-impl, r=jonas-schievink
feat: Improve "Generate `Deref` impl" assist Fixes #12265 Fixes #12266 The assist will now generate a `DerefMut` impl if a `Deref` impl is already present.
2 parents 8bc6a8f + 1df6560 commit ce9b174

File tree

4 files changed

+171
-48
lines changed

4 files changed

+171
-48
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt::Display;
22

3+
use hir::{ModPath, ModuleDef};
34
use ide_db::{famous_defs::FamousDefs, RootDatabase};
45
use syntax::{
56
ast::{self, HasName},
@@ -17,6 +18,7 @@ use crate::{
1718
// Generate `Deref` impl using the given struct field.
1819
//
1920
// ```
21+
// # //- minicore: deref, deref_mut
2022
// struct A;
2123
// struct B {
2224
// $0a: A
@@ -29,7 +31,7 @@ use crate::{
2931
// a: A
3032
// }
3133
//
32-
// impl std::ops::Deref for B {
34+
// impl core::ops::Deref for B {
3335
// type Target = A;
3436
//
3537
// fn deref(&self) -> &Self::Target {
@@ -45,19 +47,36 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
4547
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
4648
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
4749

48-
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
49-
cov_mark::hit!(test_add_record_deref_impl_already_exists);
50-
return None;
51-
}
50+
let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
51+
None => DerefType::Deref,
52+
Some(DerefType::Deref) => DerefType::DerefMut,
53+
Some(DerefType::DerefMut) => {
54+
cov_mark::hit!(test_add_record_deref_impl_already_exists);
55+
return None;
56+
}
57+
};
58+
59+
let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
60+
let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
61+
let trait_path = module.find_use_path(ctx.db(), ModuleDef::Trait(trait_))?;
5262

5363
let field_type = field.ty()?;
5464
let field_name = field.name()?;
5565
let target = field.syntax().text_range();
5666
acc.add(
5767
AssistId("generate_deref", AssistKind::Generate),
58-
format!("Generate `Deref` impl using `{}`", field_name),
68+
format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field_name),
5969
target,
60-
|edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()),
70+
|edit| {
71+
generate_edit(
72+
edit,
73+
strukt,
74+
field_type.syntax(),
75+
field_name.syntax(),
76+
deref_type_to_generate,
77+
trait_path,
78+
)
79+
},
6180
)
6281
}
6382

@@ -68,18 +87,35 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
6887
let field_list_index =
6988
field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
7089

71-
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
72-
cov_mark::hit!(test_add_field_deref_impl_already_exists);
73-
return None;
74-
}
90+
let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
91+
None => DerefType::Deref,
92+
Some(DerefType::Deref) => DerefType::DerefMut,
93+
Some(DerefType::DerefMut) => {
94+
cov_mark::hit!(test_add_field_deref_impl_already_exists);
95+
return None;
96+
}
97+
};
98+
99+
let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
100+
let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
101+
let trait_path = module.find_use_path(ctx.db(), ModuleDef::Trait(trait_))?;
75102

76103
let field_type = field.ty()?;
77104
let target = field.syntax().text_range();
78105
acc.add(
79106
AssistId("generate_deref", AssistKind::Generate),
80-
format!("Generate `Deref` impl using `{}`", field.syntax()),
107+
format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field.syntax()),
81108
target,
82-
|edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index),
109+
|edit| {
110+
generate_edit(
111+
edit,
112+
strukt,
113+
field_type.syntax(),
114+
field_list_index,
115+
deref_type_to_generate,
116+
trait_path,
117+
)
118+
},
83119
)
84120
}
85121

@@ -88,38 +124,72 @@ fn generate_edit(
88124
strukt: ast::Struct,
89125
field_type_syntax: &SyntaxNode,
90126
field_name: impl Display,
127+
deref_type: DerefType,
128+
trait_path: ModPath,
91129
) {
92130
let start_offset = strukt.syntax().text_range().end();
93-
let impl_code = format!(
94-
r#" type Target = {0};
131+
let impl_code = match deref_type {
132+
DerefType::Deref => format!(
133+
r#" type Target = {0};
95134
96135
fn deref(&self) -> &Self::Target {{
97136
&self.{1}
98137
}}"#,
99-
field_type_syntax, field_name
100-
);
138+
field_type_syntax, field_name
139+
),
140+
DerefType::DerefMut => format!(
141+
r#" fn deref_mut(&mut self) -> &mut Self::Target {{
142+
&mut self.{}
143+
}}"#,
144+
field_name
145+
),
146+
};
101147
let strukt_adt = ast::Adt::Struct(strukt);
102-
let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code);
148+
let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code);
103149
edit.insert(start_offset, deref_impl);
104150
}
105151

106152
fn existing_deref_impl(
107-
sema: &'_ hir::Semantics<'_, RootDatabase>,
153+
sema: &hir::Semantics<'_, RootDatabase>,
108154
strukt: &ast::Struct,
109-
) -> Option<()> {
155+
) -> Option<DerefType> {
110156
let strukt = sema.to_def(strukt)?;
111157
let krate = strukt.module(sema.db).krate();
112158

113159
let deref_trait = FamousDefs(sema, krate).core_ops_Deref()?;
160+
let deref_mut_trait = FamousDefs(sema, krate).core_ops_DerefMut()?;
114161
let strukt_type = strukt.ty(sema.db);
115162

116163
if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
117-
Some(())
164+
if strukt_type.impls_trait(sema.db, deref_mut_trait, &[]) {
165+
Some(DerefType::DerefMut)
166+
} else {
167+
Some(DerefType::Deref)
168+
}
118169
} else {
119170
None
120171
}
121172
}
122173

174+
#[derive(Debug)]
175+
enum DerefType {
176+
Deref,
177+
DerefMut,
178+
}
179+
180+
impl DerefType {
181+
fn to_trait(
182+
&self,
183+
sema: &hir::Semantics<'_, RootDatabase>,
184+
krate: hir::Crate,
185+
) -> Option<hir::Trait> {
186+
match self {
187+
DerefType::Deref => FamousDefs(sema, krate).core_ops_Deref(),
188+
DerefType::DerefMut => FamousDefs(sema, krate).core_ops_DerefMut(),
189+
}
190+
}
191+
}
192+
123193
#[cfg(test)]
124194
mod tests {
125195
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -130,12 +200,39 @@ mod tests {
130200
fn test_generate_record_deref() {
131201
check_assist(
132202
generate_deref,
133-
r#"struct A { }
203+
r#"
204+
//- minicore: deref
205+
struct A { }
134206
struct B { $0a: A }"#,
135-
r#"struct A { }
207+
r#"
208+
struct A { }
136209
struct B { a: A }
137210
138-
impl std::ops::Deref for B {
211+
impl core::ops::Deref for B {
212+
type Target = A;
213+
214+
fn deref(&self) -> &Self::Target {
215+
&self.a
216+
}
217+
}"#,
218+
);
219+
}
220+
221+
#[test]
222+
fn test_generate_record_deref_short_path() {
223+
check_assist(
224+
generate_deref,
225+
r#"
226+
//- minicore: deref
227+
use core::ops::Deref;
228+
struct A { }
229+
struct B { $0a: A }"#,
230+
r#"
231+
use core::ops::Deref;
232+
struct A { }
233+
struct B { a: A }
234+
235+
impl Deref for B {
139236
type Target = A;
140237
141238
fn deref(&self) -> &Self::Target {
@@ -149,12 +246,15 @@ impl std::ops::Deref for B {
149246
fn test_generate_field_deref_idx_0() {
150247
check_assist(
151248
generate_deref,
152-
r#"struct A { }
249+
r#"
250+
//- minicore: deref
251+
struct A { }
153252
struct B($0A);"#,
154-
r#"struct A { }
253+
r#"
254+
struct A { }
155255
struct B(A);
156256
157-
impl std::ops::Deref for B {
257+
impl core::ops::Deref for B {
158258
type Target = A;
159259
160260
fn deref(&self) -> &Self::Target {
@@ -167,12 +267,15 @@ impl std::ops::Deref for B {
167267
fn test_generate_field_deref_idx_1() {
168268
check_assist(
169269
generate_deref,
170-
r#"struct A { }
270+
r#"
271+
//- minicore: deref
272+
struct A { }
171273
struct B(u8, $0A);"#,
172-
r#"struct A { }
274+
r#"
275+
struct A { }
173276
struct B(u8, A);
174277
175-
impl std::ops::Deref for B {
278+
impl core::ops::Deref for B {
176279
type Target = A;
177280
178281
fn deref(&self) -> &Self::Target {
@@ -182,23 +285,43 @@ impl std::ops::Deref for B {
182285
);
183286
}
184287

288+
#[test]
289+
fn test_generates_derefmut_when_deref_present() {
290+
check_assist(
291+
generate_deref,
292+
r#"
293+
//- minicore: deref, deref_mut
294+
struct B { $0a: u8 }
295+
296+
impl core::ops::Deref for B {}
297+
"#,
298+
r#"
299+
struct B { a: u8 }
300+
301+
impl core::ops::DerefMut for B {
302+
fn deref_mut(&mut self) -> &mut Self::Target {
303+
&mut self.a
304+
}
305+
}
306+
307+
impl core::ops::Deref for B {}
308+
"#,
309+
);
310+
}
311+
185312
#[test]
186313
fn test_generate_record_deref_not_applicable_if_already_impl() {
187314
cov_mark::check!(test_add_record_deref_impl_already_exists);
188315
check_assist_not_applicable(
189316
generate_deref,
190317
r#"
191-
//- minicore: deref
318+
//- minicore: deref, deref_mut
192319
struct A { }
193320
struct B { $0a: A }
194321
195-
impl core::ops::Deref for B {
196-
type Target = A;
197-
198-
fn deref(&self) -> &Self::Target {
199-
&self.a
200-
}
201-
}"#,
322+
impl core::ops::Deref for B {}
323+
impl core::ops::DerefMut for B {}
324+
"#,
202325
)
203326
}
204327

@@ -208,17 +331,13 @@ impl core::ops::Deref for B {
208331
check_assist_not_applicable(
209332
generate_deref,
210333
r#"
211-
//- minicore: deref
334+
//- minicore: deref, deref_mut
212335
struct A { }
213336
struct B($0A)
214337
215-
impl core::ops::Deref for B {
216-
type Target = A;
217-
218-
fn deref(&self) -> &Self::Target {
219-
&self.0
220-
}
221-
}"#,
338+
impl core::ops::Deref for B {}
339+
impl core::ops::DerefMut for B {}
340+
"#,
222341
)
223342
}
224343
}

crates/ide-assists/src/tests.rs

-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ fn assist_order_field_struct() {
226226
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
227227
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
228228
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
229-
assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
230229
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
231230
}
232231

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,7 @@ fn doctest_generate_deref() {
799799
check_doc_test(
800800
"generate_deref",
801801
r#####"
802+
//- minicore: deref, deref_mut
802803
struct A;
803804
struct B {
804805
$0a: A
@@ -810,7 +811,7 @@ struct B {
810811
a: A
811812
}
812813
813-
impl std::ops::Deref for B {
814+
impl core::ops::Deref for B {
814815
type Target = A;
815816
816817
fn deref(&self) -> &Self::Target {

crates/ide-db/src/famous_defs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ impl FamousDefs<'_, '_> {
8282
self.find_trait("core:ops:Deref")
8383
}
8484

85+
pub fn core_ops_DerefMut(&self) -> Option<Trait> {
86+
self.find_trait("core:ops:DerefMut")
87+
}
88+
8589
pub fn core_convert_AsRef(&self) -> Option<Trait> {
8690
self.find_trait("core:convert:AsRef")
8791
}

0 commit comments

Comments
 (0)