diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index fafcde25ae7..b7932f550e2 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -16,7 +16,7 @@ fn test_copy_expand_simple() { #[derive(Copy)] struct Foo; -impl < > core::marker::Copy for Foo< > {}"#]], +impl < > core::marker::Copy for Foo< > where {}"#]], ); } @@ -41,7 +41,7 @@ fn test_copy_expand_in_core() { #[derive(Copy)] struct Foo; -impl < > crate ::marker::Copy for Foo< > {}"#]], +impl < > crate ::marker::Copy for Foo< > where {}"#]], ); } @@ -57,7 +57,7 @@ fn test_copy_expand_with_type_params() { #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo {}"#]], +impl core::marker::Copy for Foo where {}"#]], ); } @@ -74,7 +74,7 @@ fn test_copy_expand_with_lifetimes() { #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo {}"#]], +impl core::marker::Copy for Foo where {}"#]], ); } @@ -90,7 +90,7 @@ fn test_clone_expand() { #[derive(Clone)] struct Foo; -impl core::clone::Clone for Foo {}"#]], +impl core::clone::Clone for Foo where {}"#]], ); } @@ -106,6 +106,6 @@ fn test_clone_expand_with_const_generics() { #[derive(Clone)] struct Foo(u32); -impl core::clone::Clone for Foo {}"#]], +impl core::clone::Clone for Foo where u32: core::clone::Clone, {}"#]], ); } diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 5c1a75132ee..2b874bffff6 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,11 +1,12 @@ //! Builtin derives. use base_db::{CrateOrigin, LangCrateOrigin}; +use either::Either; use tracing::debug; use crate::tt::{self, TokenId}; use syntax::{ - ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName}, + ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds}, match_ast, }; @@ -60,8 +61,11 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option struct BasicAdtInfo { name: tt::Ident, - /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. - param_types: Vec>, + /// first field is the name, and + /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. + /// third fields is where bounds, if any + param_types: Vec<(tt::Subtree, Option, Option)>, + field_types: Vec, } fn parse_adt(tt: &tt::Subtree) -> Result { @@ -75,17 +79,34 @@ fn parse_adt(tt: &tt::Subtree) -> Result { ExpandError::Other("no item found".into()) })?; let node = item.syntax(); - let (name, params) = match_ast! { + let (name, params, fields) = match_ast! { match node { - ast::Struct(it) => (it.name(), it.generic_param_list()), - ast::Enum(it) => (it.name(), it.generic_param_list()), - ast::Union(it) => (it.name(), it.generic_param_list()), + ast::Struct(it) => { + (it.name(), it.generic_param_list(), it.field_list().into_iter().collect::>()) + }, + ast::Enum(it) => (it.name(), it.generic_param_list(), it.variant_list().into_iter().flat_map(|x| x.variants()).filter_map(|x| x.field_list()).collect()), + ast::Union(it) => (it.name(), it.generic_param_list(), it.record_field_list().into_iter().map(|x| ast::FieldList::RecordFieldList(x)).collect()), _ => { debug!("unexpected node is {:?}", node); return Err(ExpandError::Other("expected struct, enum or union".into())) }, } }; + let field_types = fields + .into_iter() + .flat_map(|f| match f { + ast::FieldList::RecordFieldList(x) => Either::Left( + x.fields() + .filter_map(|x| x.ty()) + .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0), + ), + ast::FieldList::TupleFieldList(x) => Either::Right( + x.fields() + .filter_map(|x| x.ty()) + .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0), + ), + }) + .collect::>(); let name = name.ok_or_else(|| { debug!("parsed item has no name"); ExpandError::Other("missing name".into()) @@ -97,7 +118,17 @@ fn parse_adt(tt: &tt::Subtree) -> Result { .into_iter() .flat_map(|param_list| param_list.type_or_const_params()) .map(|param| { - if let ast::TypeOrConstParam::Const(param) = param { + let name = param + .name() + .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + .unwrap_or_else(tt::Subtree::empty); + let bounds = match ¶m { + ast::TypeOrConstParam::Type(x) => { + x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + } + ast::TypeOrConstParam::Const(_) => None, + }; + let ty = if let ast::TypeOrConstParam::Const(param) = param { let ty = param .ty() .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0) @@ -105,10 +136,11 @@ fn parse_adt(tt: &tt::Subtree) -> Result { Some(ty) } else { None - } + }; + (name, ty, bounds) }) .collect(); - Ok(BasicAdtInfo { name: name_token, param_types }) + Ok(BasicAdtInfo { name: name_token, param_types, field_types }) } fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult { @@ -116,16 +148,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu Ok(info) => info, Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e), }; + let mut where_block = vec![]; let (params, args): (Vec<_>, Vec<_>) = info .param_types .into_iter() - .enumerate() - .map(|(idx, param_ty)| { - let ident = tt::Leaf::Ident(tt::Ident { - span: tt::TokenId::unspecified(), - text: format!("T{idx}").into(), - }); + .map(|(ident, param_ty, bound)| { let ident_ = ident.clone(); + if let Some(b) = bound { + let ident = ident.clone(); + where_block.push(quote! { #ident : #b , }); + } if let Some(ty) = param_ty { (quote! { const #ident : #ty , }, quote! { #ident_ , }) } else { @@ -134,9 +166,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu } }) .unzip(); + + where_block.extend(info.field_types.iter().map(|x| { + let x = x.clone(); + let bound = trait_path.clone(); + quote! { #x : #bound , } + })); + let name = info.name; let expanded = quote! { - impl < ##params > #trait_path for #name < ##args > {} + impl < ##params > #trait_path for #name < ##args > where ##where_block {} }; ExpandResult::ok(expanded) } diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 4382af43438..91af5716ca5 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -471,7 +471,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >{} + impl < >core::clone::Clone for Foo< >where{} "#]], ); } @@ -488,7 +488,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >{} + impl < >core::marker::Copy for Foo< >where{} "#]], ); } @@ -504,7 +504,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >{} + impl < >core::marker::Copy for Foo< >where{} "#]], ); check( @@ -516,7 +516,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >{} + impl < >core::clone::Clone for Foo< >where{} "#]], ); }