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())?;
62 module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?;
64 let field_type = field.ty()?;
65 let field_name = field.name()?;
66 let target = field.syntax().text_range();
68 AssistId("generate_deref", AssistKind::Generate),
69 format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field_name),
77 deref_type_to_generate,
84 fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
85 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
86 let field = ctx.find_node_at_offset::<ast::TupleField>()?;
87 let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
88 let field_list_index =
89 field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
91 let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
92 None => DerefType::Deref,
93 Some(DerefType::Deref) => DerefType::DerefMut,
94 Some(DerefType::DerefMut) => {
95 cov_mark::hit!(test_add_field_deref_impl_already_exists);
100 let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
101 let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
103 module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?;
105 let field_type = field.ty()?;
106 let target = field.syntax().text_range();
108 AssistId("generate_deref", AssistKind::Generate),
109 format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field.syntax()),
117 deref_type_to_generate,
125 edit: &mut SourceChangeBuilder,
127 field_type_syntax: &SyntaxNode,
128 field_name: impl Display,
129 deref_type: DerefType,
132 let start_offset = strukt.syntax().text_range().end();
133 let impl_code = match deref_type {
134 DerefType::Deref => format!(
135 r#" type Target = {0};
137 fn deref(&self) -> &Self::Target {{
140 field_type_syntax, field_name
142 DerefType::DerefMut => format!(
143 r#" fn deref_mut(&mut self) -> &mut Self::Target {{
149 let strukt_adt = ast::Adt::Struct(strukt);
150 let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code);
151 edit.insert(start_offset, deref_impl);
154 fn existing_deref_impl(
155 sema: &hir::Semantics<'_, RootDatabase>,
156 strukt: &ast::Struct,
157 ) -> Option<DerefType> {
158 let strukt = sema.to_def(strukt)?;
159 let krate = strukt.module(sema.db).krate();
161 let deref_trait = FamousDefs(sema, krate).core_ops_Deref()?;
162 let deref_mut_trait = FamousDefs(sema, krate).core_ops_DerefMut()?;
163 let strukt_type = strukt.ty(sema.db);
165 if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
166 if strukt_type.impls_trait(sema.db, deref_mut_trait, &[]) {
167 Some(DerefType::DerefMut)
169 Some(DerefType::Deref)
185 sema: &hir::Semantics<'_, RootDatabase>,
187 ) -> Option<hir::Trait> {
189 DerefType::Deref => FamousDefs(sema, krate).core_ops_Deref(),
190 DerefType::DerefMut => FamousDefs(sema, krate).core_ops_DerefMut(),
197 use crate::tests::{check_assist, check_assist_not_applicable};
202 fn test_generate_record_deref() {
208 struct B { $0a: A }"#,
213 impl core::ops::Deref for B {
216 fn deref(&self) -> &Self::Target {
224 fn test_generate_record_deref_short_path() {
229 use core::ops::Deref;
231 struct B { $0a: A }"#,
233 use core::ops::Deref;
240 fn deref(&self) -> &Self::Target {
248 fn test_generate_field_deref_idx_0() {
259 impl core::ops::Deref for B {
262 fn deref(&self) -> &Self::Target {
269 fn test_generate_field_deref_idx_1() {
275 struct B(u8, $0A);"#,
280 impl core::ops::Deref for B {
283 fn deref(&self) -> &Self::Target {
291 fn test_generates_derefmut_when_deref_present() {
295 //- minicore: deref, deref_mut
298 impl core::ops::Deref for B {}
303 impl core::ops::DerefMut for B {
304 fn deref_mut(&mut self) -> &mut Self::Target {
309 impl core::ops::Deref for B {}
315 fn test_generate_record_deref_not_applicable_if_already_impl() {
316 cov_mark::check!(test_add_record_deref_impl_already_exists);
317 check_assist_not_applicable(
320 //- minicore: deref, deref_mut
324 impl core::ops::Deref for B {}
325 impl core::ops::DerefMut for B {}
331 fn test_generate_field_deref_not_applicable_if_already_impl() {
332 cov_mark::check!(test_add_field_deref_impl_already_exists);
333 check_assist_not_applicable(
336 //- minicore: deref, deref_mut
340 impl core::ops::Deref for B {}
341 impl core::ops::DerefMut for B {}