mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 07:22:42 +00:00
Improve "Generate Deref
impl" assist
This commit is contained in:
parent
8bc6a8fe6d
commit
1df6560fd8
@ -1,5 +1,6 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use hir::{ModPath, ModuleDef};
|
||||||
use ide_db::{famous_defs::FamousDefs, RootDatabase};
|
use ide_db::{famous_defs::FamousDefs, RootDatabase};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasName},
|
ast::{self, HasName},
|
||||||
@ -17,6 +18,7 @@ use crate::{
|
|||||||
// Generate `Deref` impl using the given struct field.
|
// Generate `Deref` impl using the given struct field.
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
|
// # //- minicore: deref, deref_mut
|
||||||
// struct A;
|
// struct A;
|
||||||
// struct B {
|
// struct B {
|
||||||
// $0a: A
|
// $0a: A
|
||||||
@ -29,7 +31,7 @@ use crate::{
|
|||||||
// a: A
|
// a: A
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// impl std::ops::Deref for B {
|
// impl core::ops::Deref for B {
|
||||||
// type Target = A;
|
// type Target = A;
|
||||||
//
|
//
|
||||||
// fn deref(&self) -> &Self::Target {
|
// fn deref(&self) -> &Self::Target {
|
||||||
@ -45,19 +47,36 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||||||
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
|
||||||
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
|
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
|
||||||
|
|
||||||
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
|
let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
|
||||||
cov_mark::hit!(test_add_record_deref_impl_already_exists);
|
None => DerefType::Deref,
|
||||||
return None;
|
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_))?;
|
||||||
|
|
||||||
let field_type = field.ty()?;
|
let field_type = field.ty()?;
|
||||||
let field_name = field.name()?;
|
let field_name = field.name()?;
|
||||||
let target = field.syntax().text_range();
|
let target = field.syntax().text_range();
|
||||||
acc.add(
|
acc.add(
|
||||||
AssistId("generate_deref", AssistKind::Generate),
|
AssistId("generate_deref", AssistKind::Generate),
|
||||||
format!("Generate `Deref` impl using `{}`", field_name),
|
format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field_name),
|
||||||
target,
|
target,
|
||||||
|edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()),
|
|edit| {
|
||||||
|
generate_edit(
|
||||||
|
edit,
|
||||||
|
strukt,
|
||||||
|
field_type.syntax(),
|
||||||
|
field_name.syntax(),
|
||||||
|
deref_type_to_generate,
|
||||||
|
trait_path,
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,18 +87,35 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
|||||||
let field_list_index =
|
let field_list_index =
|
||||||
field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
|
field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
|
||||||
|
|
||||||
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
|
let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
|
||||||
cov_mark::hit!(test_add_field_deref_impl_already_exists);
|
None => DerefType::Deref,
|
||||||
return None;
|
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_))?;
|
||||||
|
|
||||||
let field_type = field.ty()?;
|
let field_type = field.ty()?;
|
||||||
let target = field.syntax().text_range();
|
let target = field.syntax().text_range();
|
||||||
acc.add(
|
acc.add(
|
||||||
AssistId("generate_deref", AssistKind::Generate),
|
AssistId("generate_deref", AssistKind::Generate),
|
||||||
format!("Generate `Deref` impl using `{}`", field.syntax()),
|
format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field.syntax()),
|
||||||
target,
|
target,
|
||||||
|edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index),
|
|edit| {
|
||||||
|
generate_edit(
|
||||||
|
edit,
|
||||||
|
strukt,
|
||||||
|
field_type.syntax(),
|
||||||
|
field_list_index,
|
||||||
|
deref_type_to_generate,
|
||||||
|
trait_path,
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,38 +124,72 @@ fn generate_edit(
|
|||||||
strukt: ast::Struct,
|
strukt: ast::Struct,
|
||||||
field_type_syntax: &SyntaxNode,
|
field_type_syntax: &SyntaxNode,
|
||||||
field_name: impl Display,
|
field_name: impl Display,
|
||||||
|
deref_type: DerefType,
|
||||||
|
trait_path: ModPath,
|
||||||
) {
|
) {
|
||||||
let start_offset = strukt.syntax().text_range().end();
|
let start_offset = strukt.syntax().text_range().end();
|
||||||
let impl_code = format!(
|
let impl_code = match deref_type {
|
||||||
r#" type Target = {0};
|
DerefType::Deref => format!(
|
||||||
|
r#" type Target = {0};
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {{
|
fn deref(&self) -> &Self::Target {{
|
||||||
&self.{1}
|
&self.{1}
|
||||||
}}"#,
|
}}"#,
|
||||||
field_type_syntax, field_name
|
field_type_syntax, field_name
|
||||||
);
|
),
|
||||||
|
DerefType::DerefMut => format!(
|
||||||
|
r#" fn deref_mut(&mut self) -> &mut Self::Target {{
|
||||||
|
&mut self.{}
|
||||||
|
}}"#,
|
||||||
|
field_name
|
||||||
|
),
|
||||||
|
};
|
||||||
let strukt_adt = ast::Adt::Struct(strukt);
|
let strukt_adt = ast::Adt::Struct(strukt);
|
||||||
let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code);
|
let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code);
|
||||||
edit.insert(start_offset, deref_impl);
|
edit.insert(start_offset, deref_impl);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn existing_deref_impl(
|
fn existing_deref_impl(
|
||||||
sema: &'_ hir::Semantics<'_, RootDatabase>,
|
sema: &hir::Semantics<'_, RootDatabase>,
|
||||||
strukt: &ast::Struct,
|
strukt: &ast::Struct,
|
||||||
) -> Option<()> {
|
) -> Option<DerefType> {
|
||||||
let strukt = sema.to_def(strukt)?;
|
let strukt = sema.to_def(strukt)?;
|
||||||
let krate = strukt.module(sema.db).krate();
|
let krate = strukt.module(sema.db).krate();
|
||||||
|
|
||||||
let deref_trait = FamousDefs(sema, krate).core_ops_Deref()?;
|
let deref_trait = FamousDefs(sema, krate).core_ops_Deref()?;
|
||||||
|
let deref_mut_trait = FamousDefs(sema, krate).core_ops_DerefMut()?;
|
||||||
let strukt_type = strukt.ty(sema.db);
|
let strukt_type = strukt.ty(sema.db);
|
||||||
|
|
||||||
if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
|
if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
|
||||||
Some(())
|
if strukt_type.impls_trait(sema.db, deref_mut_trait, &[]) {
|
||||||
|
Some(DerefType::DerefMut)
|
||||||
|
} else {
|
||||||
|
Some(DerefType::Deref)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||||
@ -130,12 +200,39 @@ mod tests {
|
|||||||
fn test_generate_record_deref() {
|
fn test_generate_record_deref() {
|
||||||
check_assist(
|
check_assist(
|
||||||
generate_deref,
|
generate_deref,
|
||||||
r#"struct A { }
|
r#"
|
||||||
|
//- minicore: deref
|
||||||
|
struct A { }
|
||||||
struct B { $0a: A }"#,
|
struct B { $0a: A }"#,
|
||||||
r#"struct A { }
|
r#"
|
||||||
|
struct A { }
|
||||||
struct B { a: A }
|
struct B { a: A }
|
||||||
|
|
||||||
impl std::ops::Deref for B {
|
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 {
|
||||||
type Target = A;
|
type Target = A;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@ -149,12 +246,15 @@ impl std::ops::Deref for B {
|
|||||||
fn test_generate_field_deref_idx_0() {
|
fn test_generate_field_deref_idx_0() {
|
||||||
check_assist(
|
check_assist(
|
||||||
generate_deref,
|
generate_deref,
|
||||||
r#"struct A { }
|
r#"
|
||||||
|
//- minicore: deref
|
||||||
|
struct A { }
|
||||||
struct B($0A);"#,
|
struct B($0A);"#,
|
||||||
r#"struct A { }
|
r#"
|
||||||
|
struct A { }
|
||||||
struct B(A);
|
struct B(A);
|
||||||
|
|
||||||
impl std::ops::Deref for B {
|
impl core::ops::Deref for B {
|
||||||
type Target = A;
|
type Target = A;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@ -167,12 +267,15 @@ impl std::ops::Deref for B {
|
|||||||
fn test_generate_field_deref_idx_1() {
|
fn test_generate_field_deref_idx_1() {
|
||||||
check_assist(
|
check_assist(
|
||||||
generate_deref,
|
generate_deref,
|
||||||
r#"struct A { }
|
r#"
|
||||||
|
//- minicore: deref
|
||||||
|
struct A { }
|
||||||
struct B(u8, $0A);"#,
|
struct B(u8, $0A);"#,
|
||||||
r#"struct A { }
|
r#"
|
||||||
|
struct A { }
|
||||||
struct B(u8, A);
|
struct B(u8, A);
|
||||||
|
|
||||||
impl std::ops::Deref for B {
|
impl core::ops::Deref for B {
|
||||||
type Target = A;
|
type Target = A;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@ -182,23 +285,43 @@ impl std::ops::Deref for B {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generate_record_deref_not_applicable_if_already_impl() {
|
fn test_generate_record_deref_not_applicable_if_already_impl() {
|
||||||
cov_mark::check!(test_add_record_deref_impl_already_exists);
|
cov_mark::check!(test_add_record_deref_impl_already_exists);
|
||||||
check_assist_not_applicable(
|
check_assist_not_applicable(
|
||||||
generate_deref,
|
generate_deref,
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref, deref_mut
|
||||||
struct A { }
|
struct A { }
|
||||||
struct B { $0a: A }
|
struct B { $0a: A }
|
||||||
|
|
||||||
impl core::ops::Deref for B {
|
impl core::ops::Deref for B {}
|
||||||
type Target = A;
|
impl core::ops::DerefMut for B {}
|
||||||
|
"#,
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.a
|
|
||||||
}
|
|
||||||
}"#,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,17 +331,13 @@ impl core::ops::Deref for B {
|
|||||||
check_assist_not_applicable(
|
check_assist_not_applicable(
|
||||||
generate_deref,
|
generate_deref,
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref, deref_mut
|
||||||
struct A { }
|
struct A { }
|
||||||
struct B($0A)
|
struct B($0A)
|
||||||
|
|
||||||
impl core::ops::Deref for B {
|
impl core::ops::Deref for B {}
|
||||||
type Target = A;
|
impl core::ops::DerefMut for B {}
|
||||||
|
"#,
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}"#,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,6 @@ fn assist_order_field_struct() {
|
|||||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
|
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
|
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
|
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
|
|
||||||
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
|
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -799,6 +799,7 @@ fn doctest_generate_deref() {
|
|||||||
check_doc_test(
|
check_doc_test(
|
||||||
"generate_deref",
|
"generate_deref",
|
||||||
r#####"
|
r#####"
|
||||||
|
//- minicore: deref, deref_mut
|
||||||
struct A;
|
struct A;
|
||||||
struct B {
|
struct B {
|
||||||
$0a: A
|
$0a: A
|
||||||
@ -810,7 +811,7 @@ struct B {
|
|||||||
a: A
|
a: A
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for B {
|
impl core::ops::Deref for B {
|
||||||
type Target = A;
|
type Target = A;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
@ -82,6 +82,10 @@ impl FamousDefs<'_, '_> {
|
|||||||
self.find_trait("core:ops:Deref")
|
self.find_trait("core:ops:Deref")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn core_ops_DerefMut(&self) -> Option<Trait> {
|
||||||
|
self.find_trait("core:ops:DerefMut")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn core_convert_AsRef(&self) -> Option<Trait> {
|
pub fn core_convert_AsRef(&self) -> Option<Trait> {
|
||||||
self.find_trait("core:convert:AsRef")
|
self.find_trait("core:convert:AsRef")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user