rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

355 lines
8.3 KiB
Rust
Raw Normal View History

2021-04-19 04:51:17 +00:00
use std::fmt::Display;
2022-05-16 18:10:46 +00:00
use hir::{ModPath, ModuleDef};
2022-03-06 18:01:30 +00:00
use ide_db::{famous_defs::FamousDefs, RootDatabase};
2021-04-11 07:31:20 +00:00
use syntax::{
2021-09-27 10:54:24 +00:00
ast::{self, HasName},
2021-04-19 04:51:17 +00:00
AstNode, SyntaxNode,
2021-04-11 07:31:20 +00:00
};
use crate::{
assist_context::{AssistContext, Assists, SourceChangeBuilder},
2021-04-11 07:31:20 +00:00
utils::generate_trait_impl_text,
AssistId, AssistKind,
};
// Assist: generate_deref
//
// Generate `Deref` impl using the given struct field.
//
// ```
2022-05-16 18:10:46 +00:00
// # //- minicore: deref, deref_mut
2021-04-11 07:31:20 +00:00
// struct A;
// struct B {
// $0a: A
// }
// ```
// ->
// ```
// struct A;
// struct B {
// a: A
// }
//
2022-05-16 18:10:46 +00:00
// impl core::ops::Deref for B {
2021-04-11 07:31:20 +00:00
// type Target = A;
//
// fn deref(&self) -> &Self::Target {
// &self.a
// }
// }
// ```
2022-07-20 13:02:08 +00:00
pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
2021-04-19 04:51:17 +00:00
generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
}
2022-07-20 13:02:08 +00:00
fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
2021-04-11 07:31:20 +00:00
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
2022-05-16 18:10:46 +00:00
let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
None => DerefType::Deref,
Some(DerefType::Deref) => DerefType::DerefMut,
Some(DerefType::DerefMut) => {
cov_mark::hit!(test_add_record_deref_impl_already_exists);
return None;
}
};
let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
let trait_path = module.find_use_path(
ctx.db(),
ModuleDef::Trait(trait_),
ctx.config.prefer_no_std,
ctx.config.prefer_prelude,
)?;
2021-04-11 07:31:20 +00:00
let field_type = field.ty()?;
let field_name = field.name()?;
let target = field.syntax().text_range();
acc.add(
AssistId("generate_deref", AssistKind::Generate),
format!("Generate `{deref_type_to_generate:?}` impl using `{field_name}`"),
2021-04-11 07:31:20 +00:00
target,
2022-05-16 18:10:46 +00:00
|edit| {
generate_edit(
ctx.db(),
2022-05-16 18:10:46 +00:00
edit,
strukt,
field_type.syntax(),
field_name.syntax(),
deref_type_to_generate,
trait_path,
)
},
2021-04-19 04:51:17 +00:00
)
}
2022-07-20 13:02:08 +00:00
fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
2021-04-19 04:51:17 +00:00
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
let field = ctx.find_node_at_offset::<ast::TupleField>()?;
let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
2022-12-30 08:42:44 +00:00
let field_list_index = field_list.syntax().children().position(|s| &s == field.syntax())?;
2021-04-19 04:51:17 +00:00
2022-05-16 18:10:46 +00:00
let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
None => DerefType::Deref,
Some(DerefType::Deref) => DerefType::DerefMut,
Some(DerefType::DerefMut) => {
cov_mark::hit!(test_add_field_deref_impl_already_exists);
return None;
}
};
let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
let trait_path = module.find_use_path(
ctx.db(),
ModuleDef::Trait(trait_),
ctx.config.prefer_no_std,
ctx.config.prefer_prelude,
)?;
2021-04-19 04:51:17 +00:00
let field_type = field.ty()?;
let target = field.syntax().text_range();
acc.add(
AssistId("generate_deref", AssistKind::Generate),
format!("Generate `{deref_type_to_generate:?}` impl using `{field}`"),
2021-04-19 04:51:17 +00:00
target,
2022-05-16 18:10:46 +00:00
|edit| {
generate_edit(
ctx.db(),
2022-05-16 18:10:46 +00:00
edit,
strukt,
field_type.syntax(),
field_list_index,
deref_type_to_generate,
trait_path,
)
},
2021-04-19 04:51:17 +00:00
)
}
fn generate_edit(
db: &RootDatabase,
edit: &mut SourceChangeBuilder,
2021-04-19 04:51:17 +00:00
strukt: ast::Struct,
field_type_syntax: &SyntaxNode,
field_name: impl Display,
2022-05-16 18:10:46 +00:00
deref_type: DerefType,
trait_path: ModPath,
2021-04-19 04:51:17 +00:00
) {
let start_offset = strukt.syntax().text_range().end();
2022-05-16 18:10:46 +00:00
let impl_code = match deref_type {
DerefType::Deref => format!(
r#" type Target = {field_type_syntax};
2021-04-11 07:31:20 +00:00
fn deref(&self) -> &Self::Target {{
&self.{field_name}
2021-04-11 07:31:20 +00:00
}}"#,
2022-05-16 18:10:46 +00:00
),
DerefType::DerefMut => format!(
r#" fn deref_mut(&mut self) -> &mut Self::Target {{
&mut self.{field_name}
2022-05-16 18:10:46 +00:00
}}"#,
),
};
2021-04-19 04:51:17 +00:00
let strukt_adt = ast::Adt::Struct(strukt);
let deref_impl =
generate_trait_impl_text(&strukt_adt, &trait_path.display(db).to_string(), &impl_code);
2021-04-19 04:51:17 +00:00
edit.insert(start_offset, deref_impl);
2021-04-11 07:31:20 +00:00
}
fn existing_deref_impl(
2022-05-16 18:10:46 +00:00
sema: &hir::Semantics<'_, RootDatabase>,
2021-04-11 07:31:20 +00:00
strukt: &ast::Struct,
2022-05-16 18:10:46 +00:00
) -> Option<DerefType> {
2021-04-11 07:31:20 +00:00
let strukt = sema.to_def(strukt)?;
let krate = strukt.module(sema.db).krate();
let deref_trait = FamousDefs(sema, krate).core_ops_Deref()?;
2022-05-16 18:10:46 +00:00
let deref_mut_trait = FamousDefs(sema, krate).core_ops_DerefMut()?;
2021-04-11 07:31:20 +00:00
let strukt_type = strukt.ty(sema.db);
if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
2022-05-16 18:10:46 +00:00
if strukt_type.impls_trait(sema.db, deref_mut_trait, &[]) {
Some(DerefType::DerefMut)
} else {
Some(DerefType::Deref)
}
2021-04-11 07:31:20 +00:00
} else {
None
}
}
2022-05-16 18:10:46 +00:00
#[derive(Debug)]
enum DerefType {
Deref,
DerefMut,
}
impl DerefType {
fn to_trait(
&self,
sema: &hir::Semantics<'_, RootDatabase>,
krate: hir::Crate,
) -> Option<hir::Trait> {
match self {
DerefType::Deref => FamousDefs(sema, krate).core_ops_Deref(),
DerefType::DerefMut => FamousDefs(sema, krate).core_ops_DerefMut(),
}
}
}
2021-04-11 07:31:20 +00:00
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
use super::*;
#[test]
2021-04-19 04:51:17 +00:00
fn test_generate_record_deref() {
2021-04-11 07:31:20 +00:00
check_assist(
generate_deref,
2022-05-16 18:10:46 +00:00
r#"
//- minicore: deref
struct A { }
2021-04-11 07:31:20 +00:00
struct B { $0a: A }"#,
2022-05-16 18:10:46 +00:00
r#"
struct A { }
2021-04-11 07:31:20 +00:00
struct B { a: A }
2022-05-16 18:10:46 +00:00
impl core::ops::Deref for B {
type Target = A;
fn deref(&self) -> &Self::Target {
&self.a
}
}"#,
);
}
#[test]
fn test_generate_record_deref_short_path() {
check_assist(
generate_deref,
r#"
//- minicore: deref
use core::ops::Deref;
struct A { }
struct B { $0a: A }"#,
r#"
use core::ops::Deref;
struct A { }
struct B { a: A }
impl Deref for B {
2021-04-11 07:31:20 +00:00
type Target = A;
fn deref(&self) -> &Self::Target {
&self.a
}
}"#,
);
}
2021-04-19 04:51:17 +00:00
#[test]
fn test_generate_field_deref_idx_0() {
check_assist(
generate_deref,
2022-05-16 18:10:46 +00:00
r#"
//- minicore: deref
struct A { }
2021-04-19 04:51:17 +00:00
struct B($0A);"#,
2022-05-16 18:10:46 +00:00
r#"
struct A { }
2021-04-19 04:51:17 +00:00
struct B(A);
2022-05-16 18:10:46 +00:00
impl core::ops::Deref for B {
2021-04-19 04:51:17 +00:00
type Target = A;
fn deref(&self) -> &Self::Target {
&self.0
}
}"#,
);
}
#[test]
fn test_generate_field_deref_idx_1() {
check_assist(
generate_deref,
2022-05-16 18:10:46 +00:00
r#"
//- minicore: deref
struct A { }
2021-04-19 04:51:17 +00:00
struct B(u8, $0A);"#,
2022-05-16 18:10:46 +00:00
r#"
struct A { }
2021-04-19 04:51:17 +00:00
struct B(u8, A);
2022-05-16 18:10:46 +00:00
impl core::ops::Deref for B {
2021-04-19 04:51:17 +00:00
type Target = A;
fn deref(&self) -> &Self::Target {
&self.1
}
}"#,
);
}
2022-05-16 18:10:46 +00:00
#[test]
fn test_generates_derefmut_when_deref_present() {
check_assist(
generate_deref,
r#"
//- minicore: deref, deref_mut
struct B { $0a: u8 }
impl core::ops::Deref for B {}
"#,
r#"
struct B { a: u8 }
impl core::ops::DerefMut for B {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.a
}
}
impl core::ops::Deref for B {}
"#,
);
}
2021-04-11 07:31:20 +00:00
#[test]
2021-04-19 04:51:17 +00:00
fn test_generate_record_deref_not_applicable_if_already_impl() {
cov_mark::check!(test_add_record_deref_impl_already_exists);
check_assist_not_applicable(
generate_deref,
r#"
2022-05-16 18:10:46 +00:00
//- minicore: deref, deref_mut
struct A { }
2021-04-11 07:31:20 +00:00
struct B { $0a: A }
2022-05-16 18:10:46 +00:00
impl core::ops::Deref for B {}
impl core::ops::DerefMut for B {}
"#,
2021-04-19 04:51:17 +00:00
)
}
#[test]
fn test_generate_field_deref_not_applicable_if_already_impl() {
cov_mark::check!(test_add_field_deref_impl_already_exists);
check_assist_not_applicable(
generate_deref,
r#"
2022-05-16 18:10:46 +00:00
//- minicore: deref, deref_mut
struct A { }
2021-04-19 04:51:17 +00:00
struct B($0A)
2022-05-16 18:10:46 +00:00
impl core::ops::Deref for B {}
impl core::ops::DerefMut for B {}
"#,
2021-04-11 07:31:20 +00:00
)
}
}