3 use hir::{ModPath, ModuleDef};
4 use ide_db::{famous_defs::FamousDefs, RootDatabase};
11 assist_context::{AssistContext, Assists, SourceChangeBuilder},
12 utils::generate_trait_impl_text,
16 // Assist: generate_deref
18 // Generate `Deref` impl using the given struct field.
21 // # //- minicore: deref, deref_mut
34 // impl core::ops::Deref for B {
37 // fn deref(&self) -> &Self::Target {
42 pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
43 generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
46 fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
47 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
48 let field = ctx.find_node_at_offset::<ast::RecordField>()?;
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);
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_))?;
63 let field_type = field.ty()?;
64 let field_name = field.name()?;
65 let target = field.syntax().text_range();
67 AssistId("generate_deref", AssistKind::Generate),
68 format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field_name),
76 deref_type_to_generate,
83 fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
84 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
85 let field = ctx.find_node_at_offset::<ast::TupleField>()?;
86 let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
87 let field_list_index =
88 field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
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);
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_))?;
103 let field_type = field.ty()?;
104 let target = field.syntax().text_range();
106 AssistId("generate_deref", AssistKind::Generate),
107 format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field.syntax()),
115 deref_type_to_generate,
123 edit: &mut SourceChangeBuilder,
125 field_type_syntax: &SyntaxNode,
126 field_name: impl Display,
127 deref_type: DerefType,
130 let start_offset = strukt.syntax().text_range().end();
131 let impl_code = match deref_type {
132 DerefType::Deref => format!(
133 r#" type Target = {0};
135 fn deref(&self) -> &Self::Target {{
138 field_type_syntax, field_name
140 DerefType::DerefMut => format!(
141 r#" fn deref_mut(&mut self) -> &mut Self::Target {{
147 let strukt_adt = ast::Adt::Struct(strukt);
148 let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code);
149 edit.insert(start_offset, deref_impl);
152 fn existing_deref_impl(
153 sema: &hir::Semantics<'_, RootDatabase>,
154 strukt: &ast::Struct,
155 ) -> Option<DerefType> {
156 let strukt = sema.to_def(strukt)?;
157 let krate = strukt.module(sema.db).krate();
159 let deref_trait = FamousDefs(sema, krate).core_ops_Deref()?;
160 let deref_mut_trait = FamousDefs(sema, krate).core_ops_DerefMut()?;
161 let strukt_type = strukt.ty(sema.db);
163 if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
164 if strukt_type.impls_trait(sema.db, deref_mut_trait, &[]) {
165 Some(DerefType::DerefMut)
167 Some(DerefType::Deref)
183 sema: &hir::Semantics<'_, RootDatabase>,
185 ) -> Option<hir::Trait> {
187 DerefType::Deref => FamousDefs(sema, krate).core_ops_Deref(),
188 DerefType::DerefMut => FamousDefs(sema, krate).core_ops_DerefMut(),
195 use crate::tests::{check_assist, check_assist_not_applicable};
200 fn test_generate_record_deref() {
206 struct B { $0a: A }"#,
211 impl core::ops::Deref for B {
214 fn deref(&self) -> &Self::Target {
222 fn test_generate_record_deref_short_path() {
227 use core::ops::Deref;
229 struct B { $0a: A }"#,
231 use core::ops::Deref;
238 fn deref(&self) -> &Self::Target {
246 fn test_generate_field_deref_idx_0() {
257 impl core::ops::Deref for B {
260 fn deref(&self) -> &Self::Target {
267 fn test_generate_field_deref_idx_1() {
273 struct B(u8, $0A);"#,
278 impl core::ops::Deref for B {
281 fn deref(&self) -> &Self::Target {
289 fn test_generates_derefmut_when_deref_present() {
293 //- minicore: deref, deref_mut
296 impl core::ops::Deref for B {}
301 impl core::ops::DerefMut for B {
302 fn deref_mut(&mut self) -> &mut Self::Target {
307 impl core::ops::Deref for B {}
313 fn test_generate_record_deref_not_applicable_if_already_impl() {
314 cov_mark::check!(test_add_record_deref_impl_already_exists);
315 check_assist_not_applicable(
318 //- minicore: deref, deref_mut
322 impl core::ops::Deref for B {}
323 impl core::ops::DerefMut for B {}
329 fn test_generate_field_deref_not_applicable_if_already_impl() {
330 cov_mark::check!(test_add_field_deref_impl_already_exists);
331 check_assist_not_applicable(
334 //- minicore: deref, deref_mut
338 impl core::ops::Deref for B {}
339 impl core::ops::DerefMut for B {}