mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
fix: add generic TypeBoundList
in generated derivable impl
This commit is contained in:
parent
4596847a88
commit
12b05d2416
3 changed files with 98 additions and 7 deletions
|
@ -994,6 +994,68 @@ impl PartialEq for Foo {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_custom_impl_partial_eq_tuple_enum_generic() {
|
||||||
|
check_assist(
|
||||||
|
replace_derive_with_manual_impl,
|
||||||
|
r#"
|
||||||
|
//- minicore: eq, derive
|
||||||
|
#[derive(Partial$0Eq)]
|
||||||
|
enum Either<T, U> {
|
||||||
|
Left(T),
|
||||||
|
Right(U),
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Either<T, U> {
|
||||||
|
Left(T),
|
||||||
|
Right(U),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq, U: PartialEq> PartialEq for Either<T, U> {
|
||||||
|
$0fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Left(l0), Self::Left(r0)) => l0 == r0,
|
||||||
|
(Self::Right(l0), Self::Right(r0)) => l0 == r0,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_custom_impl_partial_eq_tuple_enum_generic_existing_bounds() {
|
||||||
|
check_assist(
|
||||||
|
replace_derive_with_manual_impl,
|
||||||
|
r#"
|
||||||
|
//- minicore: eq, derive
|
||||||
|
#[derive(Partial$0Eq)]
|
||||||
|
enum Either<T: PartialEq + Error, U: Clone> {
|
||||||
|
Left(T),
|
||||||
|
Right(U),
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Either<T: PartialEq + Error, U: Clone> {
|
||||||
|
Left(T),
|
||||||
|
Right(U),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq + Error, U: Clone + PartialEq> PartialEq for Either<T, U> {
|
||||||
|
$0fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Left(l0), Self::Left(r0)) => l0 == r0,
|
||||||
|
(Self::Right(l0), Self::Right(r0)) => l0 == r0,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_custom_impl_partial_eq_record_enum() {
|
fn add_custom_impl_partial_eq_record_enum() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -1170,7 +1232,7 @@ struct Foo<T, U> {
|
||||||
bar: U,
|
bar: U,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Default for Foo<T, U> {
|
impl<T: Default, U: Default> Default for Foo<T, U> {
|
||||||
$0fn default() -> Self {
|
$0fn default() -> Self {
|
||||||
Self { foo: Default::default(), bar: Default::default() }
|
Self { foo: Default::default(), bar: Default::default() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::ops;
|
||||||
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
|
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
|
||||||
use hir::{db::HirDatabase, HirDisplay, Semantics};
|
use hir::{db::HirDatabase, HirDisplay, Semantics};
|
||||||
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
|
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
|
||||||
|
use itertools::Itertools;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
|
@ -452,15 +453,32 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
|
||||||
let lifetime_params =
|
let lifetime_params =
|
||||||
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
|
||||||
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
|
let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
|
||||||
// remove defaults since they can't be specified in impls
|
|
||||||
match param {
|
match param {
|
||||||
ast::TypeOrConstParam::Type(param) => {
|
ast::TypeOrConstParam::Type(param) => {
|
||||||
let param = param.clone_for_update();
|
let param = param.clone_for_update();
|
||||||
|
// remove defaults since they can't be specified in impls
|
||||||
param.remove_default();
|
param.remove_default();
|
||||||
|
let mut bounds = param
|
||||||
|
.type_bound_list()
|
||||||
|
.map_or_else(Vec::new, |it| it.bounds().collect_vec());
|
||||||
|
// `{ty_param}: {trait_text}`
|
||||||
|
if let Some(trait_) = trait_text {
|
||||||
|
// Defense against the following cases:
|
||||||
|
// - The trait is undetermined, e.g. `$0`.
|
||||||
|
// - The trait is a `From`, e.g. `From<T>`.
|
||||||
|
if !trait_.starts_with('$')
|
||||||
|
&& !matches!(trait_.split_once('<'), Some((left, _right)) if left.trim() == "From")
|
||||||
|
{
|
||||||
|
bounds.push(make::type_bound(trait_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let param =
|
||||||
|
make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
|
||||||
Some(ast::GenericParam::TypeParam(param))
|
Some(ast::GenericParam::TypeParam(param))
|
||||||
}
|
}
|
||||||
ast::TypeOrConstParam::Const(param) => {
|
ast::TypeOrConstParam::Const(param) => {
|
||||||
let param = param.clone_for_update();
|
let param = param.clone_for_update();
|
||||||
|
// remove defaults since they can't be specified in impls
|
||||||
param.remove_default();
|
param.remove_default();
|
||||||
Some(ast::GenericParam::ConstParam(param))
|
Some(ast::GenericParam::ConstParam(param))
|
||||||
}
|
}
|
||||||
|
|
|
@ -719,11 +719,22 @@ pub fn param_list(
|
||||||
ast_from_text(&list)
|
ast_from_text(&list)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam {
|
pub fn type_bound(bound: &str) -> ast::TypeBound {
|
||||||
let bound = match ty {
|
ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
|
||||||
Some(it) => format!(": {it}"),
|
}
|
||||||
None => String::new(),
|
|
||||||
};
|
pub fn type_bound_list(
|
||||||
|
bounds: impl IntoIterator<Item = ast::TypeBound>,
|
||||||
|
) -> Option<ast::TypeBoundList> {
|
||||||
|
let bounds = bounds.into_iter().map(|it| it.to_string()).unique().join(" + ");
|
||||||
|
if bounds.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(ast_from_text(&format!("fn f<T: {bounds}>() {{ }}")))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_param(name: ast::Name, bounds: Option<ast::TypeBoundList>) -> ast::TypeParam {
|
||||||
|
let bound = bounds.map_or_else(String::new, |it| format!(": {it}"));
|
||||||
ast_from_text(&format!("fn f<{name}{bound}>() {{ }}"))
|
ast_from_text(&format!("fn f<{name}{bound}>() {{ }}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue