Emit function bodies in expanding builtin derives

This commit is contained in:
hkalbasi 2023-05-03 14:14:47 +03:30
parent 9811a3af5f
commit d9f4cbbe8f
7 changed files with 962 additions and 38 deletions

View file

@ -84,13 +84,33 @@ fn test_clone_expand() {
r#" r#"
//- minicore: derive, clone //- minicore: derive, clone
#[derive(Clone)] #[derive(Clone)]
struct Foo<A, B>; enum Command<A, B> {
Move { x: A, y: B },
Do(&'static str),
Jump,
}
"#, "#,
expect![[r#" expect![[r#"
#[derive(Clone)] #[derive(Clone)]
struct Foo<A, B>; enum Command<A, B> {
Move { x: A, y: B },
Do(&'static str),
Jump,
}
impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Foo<A, B, > where {}"#]], impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where {
fn clone(&self ) -> Self {
match self {
Command::Move {
x: x, y: y,
}
=>Command::Move {
x: x.clone(), y: y.clone(),
}
, Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump,
}
}
}"#]],
); );
} }
@ -106,6 +126,270 @@ struct Foo<const X: usize, T>(u32);
#[derive(Clone)] #[derive(Clone)]
struct Foo<const X: usize, T>(u32); struct Foo<const X: usize, T>(u32);
impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {}"#]], impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {
fn clone(&self ) -> Self {
match self {
Foo(f0, )=>Foo(f0.clone(), ),
}
}
}"#]],
);
}
#[test]
fn test_default_expand() {
check(
r#"
//- minicore: derive, default
#[derive(Default)]
struct Foo {
field1: i32,
field2: (),
}
#[derive(Default)]
enum Bar {
Foo(u8),
#[default]
Bar,
}
"#,
expect![[r#"
#[derive(Default)]
struct Foo {
field1: i32,
field2: (),
}
#[derive(Default)]
enum Bar {
Foo(u8),
#[default]
Bar,
}
impl < > core::default::Default for Foo< > where {
fn default() -> Self {
Foo {
field1: core::default::Default::default(), field2: core::default::Default::default(),
}
}
}
impl < > core::default::Default for Bar< > where {
fn default() -> Self {
Bar::Bar
}
}"#]],
);
}
#[test]
fn test_partial_eq_expand() {
check(
r#"
//- minicore: derive, eq
#[derive(PartialEq, Eq)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
"#,
expect![[r#"
#[derive(PartialEq, Eq)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
impl < > core::cmp::PartialEq for Command< > where {
fn eq(&self , other: &Self ) -> bool {
match (self , other) {
(Command::Move {
x: x_self, y: y_self,
}
, Command::Move {
x: x_other, y: y_other,
}
)=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false
}
}
}
impl < > core::cmp::Eq for Command< > where {}"#]],
);
}
#[test]
fn test_partial_ord_expand() {
check(
r#"
//- minicore: derive, ord
#[derive(PartialOrd, Ord)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
"#,
expect![[r#"
#[derive(PartialOrd, Ord)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
impl < > core::cmp::PartialOrd for Command< > where {
fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> {
match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) {
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
match (self , other) {
(Command::Move {
x: x_self, y: y_self,
}
, Command::Move {
x: x_other, y: y_other,
}
)=>match x_self.partial_cmp(&x_other) {
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
match y_self.partial_cmp(&y_other) {
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
core::option::Option::Some(core::cmp::Ordering::Equal)
}
c=>return c,
}
}
c=>return c,
}
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
core::option::Option::Some(core::cmp::Ordering::Equal)=> {
core::option::Option::Some(core::cmp::Ordering::Equal)
}
c=>return c,
}
, (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal)
}
}
c=>return c,
}
}
}
impl < > core::cmp::Ord for Command< > where {
fn cmp(&self , other: &Self ) -> core::cmp::Ordering {
match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) {
core::cmp::Ordering::Equal=> {
match (self , other) {
(Command::Move {
x: x_self, y: y_self,
}
, Command::Move {
x: x_other, y: y_other,
}
)=>match x_self.cmp(&x_other) {
core::cmp::Ordering::Equal=> {
match y_self.cmp(&y_other) {
core::cmp::Ordering::Equal=> {
core::cmp::Ordering::Equal
}
c=>return c,
}
}
c=>return c,
}
, (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
core::cmp::Ordering::Equal=> {
core::cmp::Ordering::Equal
}
c=>return c,
}
, (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal
}
}
c=>return c,
}
}
}"#]],
);
}
#[test]
fn test_hash_expand() {
check(
r#"
//- minicore: derive, hash
use core::hash::Hash;
#[derive(Hash)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
"#,
expect![[r#"
use core::hash::Hash;
#[derive(Hash)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
impl < > core::hash::Hash for Command< > where {
fn hash<H: core::hash::Hasher>(&self , state: &mut H) {
core::mem::discriminant(self ).hash(state);
match self {
Command::Move {
x: x, y: y,
}
=> {
x.hash(state);
y.hash(state);
}
, Command::Do(f0, )=> {
f0.hash(state);
}
, Command::Jump=> {}
,
}
}
}"#]],
);
}
#[test]
fn test_debug_expand() {
check(
r#"
//- minicore: derive, fmt
use core::fmt::Debug;
#[derive(Debug)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
"#,
expect![[r#"
use core::fmt::Debug;
#[derive(Debug)]
enum Command {
Move { x: i32, y: i32 },
Do(&'static str),
Jump,
}
impl < > core::fmt::Debug for Command< > where {
fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Command::Move {
x: x, y: y,
}
=>f.debug_struct("Move").field("x", x).field("y", y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(f0).finish(), Command::Jump=>f.write_str("Jump"),
}
}
}"#]],
); );
} }

View file

@ -1,12 +1,19 @@
//! Builtin derives. //! Builtin derives.
use ::tt::Ident;
use base_db::{CrateOrigin, LangCrateOrigin}; use base_db::{CrateOrigin, LangCrateOrigin};
use itertools::izip;
use mbe::TokenMap;
use std::collections::HashSet; use std::collections::HashSet;
use stdx::never;
use tracing::debug; use tracing::debug;
use crate::tt::{self, TokenId}; use crate::tt::{self, TokenId};
use syntax::{ use syntax::{
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, PathType}, ast::{
self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName,
HasTypeBounds, PathType,
},
match_ast, match_ast,
}; };
@ -59,8 +66,124 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
BuiltinDeriveExpander::find_by_name(ident) BuiltinDeriveExpander::find_by_name(ident)
} }
enum VariantShape {
Struct(Vec<tt::Ident>),
Tuple(usize),
Unit,
}
fn tuple_field_iterator(n: usize) -> impl Iterator<Item = tt::Ident> {
(0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified()))
}
impl VariantShape {
fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree {
self.as_pattern_map(path, |x| quote!(#x))
}
fn field_names(&self) -> Vec<tt::Ident> {
match self {
VariantShape::Struct(s) => s.clone(),
VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(),
VariantShape::Unit => vec![],
}
}
fn as_pattern_map(
&self,
path: tt::Subtree,
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
) -> tt::Subtree {
match self {
VariantShape::Struct(fields) => {
let fields = fields.iter().map(|x| {
let mapped = field_map(x);
quote! { #x : #mapped , }
});
quote! {
#path { ##fields }
}
}
&VariantShape::Tuple(n) => {
let fields = tuple_field_iterator(n).map(|x| {
let mapped = field_map(&x);
quote! {
#mapped ,
}
});
quote! {
#path ( ##fields )
}
}
VariantShape::Unit => path,
}
}
fn from(value: Option<FieldList>, token_map: &TokenMap) -> Result<Self, ExpandError> {
let r = match value {
None => VariantShape::Unit,
Some(FieldList::RecordFieldList(x)) => VariantShape::Struct(
x.fields()
.map(|x| x.name())
.map(|x| name_to_token(token_map, x))
.collect::<Result<_, _>>()?,
),
Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()),
};
Ok(r)
}
}
enum AdtShape {
Struct(VariantShape),
Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option<usize> },
Union,
}
impl AdtShape {
fn as_pattern(&self, name: &tt::Ident) -> Vec<tt::Subtree> {
self.as_pattern_map(name, |x| quote!(#x))
}
fn field_names(&self) -> Vec<Vec<tt::Ident>> {
match self {
AdtShape::Struct(s) => {
vec![s.field_names()]
}
AdtShape::Enum { variants, .. } => {
variants.iter().map(|(_, fields)| fields.field_names()).collect()
}
AdtShape::Union => {
never!("using fields of union in derive is always wrong");
vec![]
}
}
}
fn as_pattern_map(
&self,
name: &tt::Ident,
field_map: impl Fn(&tt::Ident) -> tt::Subtree,
) -> Vec<tt::Subtree> {
match self {
AdtShape::Struct(s) => {
vec![s.as_pattern_map(quote! { #name }, field_map)]
}
AdtShape::Enum { variants, .. } => variants
.iter()
.map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map))
.collect(),
AdtShape::Union => {
never!("pattern matching on union is always wrong");
vec![quote! { un }]
}
}
}
}
struct BasicAdtInfo { struct BasicAdtInfo {
name: tt::Ident, name: tt::Ident,
shape: AdtShape,
/// first field is the name, and /// 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. /// 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 /// third fields is where bounds, if any
@ -79,11 +202,24 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
ExpandError::Other("no item found".into()) ExpandError::Other("no item found".into())
})?; })?;
let node = item.syntax(); let node = item.syntax();
let (name, params) = match_ast! { let (name, params, shape) = match_ast! {
match node { match node {
ast::Struct(it) => (it.name(), it.generic_param_list()), ast::Struct(it) => (it.name(), it.generic_param_list(), AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?)),
ast::Enum(it) => (it.name(), it.generic_param_list()), ast::Enum(it) => {
ast::Union(it) => (it.name(), it.generic_param_list()), let default_variant = it.variant_list().into_iter().flat_map(|x| x.variants()).position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
(
it.name(),
it.generic_param_list(),
AdtShape::Enum {
default_variant,
variants: it.variant_list()
.into_iter()
.flat_map(|x| x.variants())
.map(|x| Ok((name_to_token(&token_map,x.name())?, VariantShape::from(x.field_list(), &token_map)?))).collect::<Result<_, ExpandError>>()?
}
)
},
ast::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
_ => { _ => {
debug!("unexpected node is {:?}", node); debug!("unexpected node is {:?}", node);
return Err(ExpandError::Other("expected struct, enum or union".into())) return Err(ExpandError::Other("expected struct, enum or union".into()))
@ -154,6 +290,11 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
.filter(is_associated_type) .filter(is_associated_type)
.map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let name_token = name_to_token(&token_map, name)?;
Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
}
fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
let name = name.ok_or_else(|| { let name = name.ok_or_else(|| {
debug!("parsed item has no name"); debug!("parsed item has no name");
ExpandError::Other("missing name".into()) ExpandError::Other("missing name".into())
@ -161,7 +302,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
let name_token_id = let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified); token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { span: name_token_id, text: name.text().into() }; let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
Ok(BasicAdtInfo { name: name_token, param_types, associated_types }) Ok(name_token)
} }
/// Given that we are deriving a trait `DerivedTrait` for a type like: /// Given that we are deriving a trait `DerivedTrait` for a type like:
@ -195,11 +336,16 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
/// ///
/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
/// therefore does not get bound by the derived trait. /// therefore does not get bound by the derived trait.
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> { fn expand_simple_derive(
tt: &tt::Subtree,
trait_path: tt::Subtree,
trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let info = match parse_adt(tt) { let info = match parse_adt(tt) {
Ok(info) => info, Ok(info) => info,
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e), Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
}; };
let trait_body = trait_body(&info);
let mut where_block = vec![]; let mut where_block = vec![];
let (params, args): (Vec<_>, Vec<_>) = info let (params, args): (Vec<_>, Vec<_>) = info
.param_types .param_types
@ -227,7 +373,7 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
let name = info.name; let name = info.name;
let expanded = quote! { let expanded = quote! {
impl < ##params > #trait_path for #name < ##args > where ##where_block {} impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body }
}; };
ExpandResult::ok(expanded) ExpandResult::ok(expanded)
} }
@ -254,7 +400,7 @@ fn copy_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::marker::Copy }) expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {})
} }
fn clone_expand( fn clone_expand(
@ -263,7 +409,63 @@ fn clone_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::clone::Clone }) expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
let star = tt::Punct {
char: '*',
spacing: ::tt::Spacing::Alone,
span: tt::TokenId::unspecified(),
};
return quote! {
fn clone(&self) -> Self {
#star self
}
};
}
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
let star = tt::Punct {
char: '*',
spacing: ::tt::Spacing::Alone,
span: tt::TokenId::unspecified(),
};
return quote! {
fn clone(&self) -> Self {
match #star self {}
}
};
}
let name = &adt.name;
let patterns = adt.shape.as_pattern(name);
let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() });
let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| {
let fat_arrow = fat_arrow();
quote! {
#pat #fat_arrow #expr,
}
});
quote! {
fn clone(&self) -> Self {
match self {
##arms
}
}
}
})
}
/// This function exists since `quote! { => }` doesn't work.
fn fat_arrow() -> ::tt::Subtree<TokenId> {
let eq =
tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
quote! { #eq> }
}
/// This function exists since `quote! { && }` doesn't work.
fn and_and() -> ::tt::Subtree<TokenId> {
let and =
tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
quote! { #and& }
} }
fn default_expand( fn default_expand(
@ -271,8 +473,38 @@ fn default_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::default::Default }) expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| {
let body = match &adt.shape {
AdtShape::Struct(fields) => {
let name = &adt.name;
fields
.as_pattern_map(quote!(#name), |_| quote!(#krate::default::Default::default()))
}
AdtShape::Enum { default_variant, variants } => {
if let Some(d) = default_variant {
let (name, fields) = &variants[*d];
let adt_name = &adt.name;
fields.as_pattern_map(
quote!(#adt_name :: #name),
|_| quote!(#krate::default::Default::default()),
)
} else {
// FIXME: Return expand error here
quote!()
}
}
AdtShape::Union => {
// FIXME: Return expand error here
quote!()
}
};
quote! {
fn default() -> Self {
#body
}
}
})
} }
fn debug_expand( fn debug_expand(
@ -280,8 +512,79 @@ fn debug_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::fmt::Debug }) expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| {
let for_variant = |name: String, v: &VariantShape| match v {
VariantShape::Struct(fields) => {
let for_fields = fields.iter().map(|x| {
let x_string = x.to_string();
quote! {
.field(#x_string, #x)
}
});
quote! {
f.debug_struct(#name) ##for_fields .finish()
}
}
VariantShape::Tuple(n) => {
let for_fields = tuple_field_iterator(*n).map(|x| {
quote! {
.field(#x)
}
});
quote! {
f.debug_tuple(#name) ##for_fields .finish()
}
}
VariantShape::Unit => quote! {
f.write_str(#name)
},
};
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
let star = tt::Punct {
char: '*',
spacing: ::tt::Spacing::Alone,
span: tt::TokenId::unspecified(),
};
return quote! {
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
match #star self {}
}
};
}
let arms = match &adt.shape {
AdtShape::Struct(fields) => {
let fat_arrow = fat_arrow();
let name = &adt.name;
let pat = fields.as_pattern(quote!(#name));
let expr = for_variant(name.to_string(), fields);
vec![quote! { #pat #fat_arrow #expr }]
}
AdtShape::Enum { variants, .. } => variants
.iter()
.map(|(name, v)| {
let fat_arrow = fat_arrow();
let adt_name = &adt.name;
let pat = v.as_pattern(quote!(#adt_name :: #name));
let expr = for_variant(name.to_string(), v);
quote! {
#pat #fat_arrow #expr ,
}
})
.collect(),
AdtShape::Union => {
// FIXME: Return expand error here
vec![]
}
};
quote! {
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
match self {
##arms
}
}
}
})
} }
fn hash_expand( fn hash_expand(
@ -289,8 +592,47 @@ fn hash_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::hash::Hash }) expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {};
}
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
let star = tt::Punct {
char: '*',
spacing: ::tt::Spacing::Alone,
span: tt::TokenId::unspecified(),
};
return quote! {
fn hash<H: #krate::hash::Hasher>(&self, state: &mut H) {
match #star self {}
}
};
}
let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map(
|(pat, names)| {
let expr = {
let it = names.iter().map(|x| quote! { #x . hash(state); });
quote! { {
##it
} }
};
let fat_arrow = fat_arrow();
quote! {
#pat #fat_arrow #expr ,
}
},
);
quote! {
fn hash<H: #krate::hash::Hasher>(&self, state: &mut H) {
#krate::mem::discriminant(self).hash(state);
match self {
##arms
}
}
}
})
} }
fn eq_expand( fn eq_expand(
@ -299,7 +641,7 @@ fn eq_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::Eq }) expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {})
} }
fn partial_eq_expand( fn partial_eq_expand(
@ -308,7 +650,65 @@ fn partial_eq_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }) expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {};
}
let name = &adt.name;
let (self_patterns, other_patterns) = self_and_other_patterns(adt, name);
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|(pat1, pat2, names)| {
let fat_arrow = fat_arrow();
let body = match &*names {
[] => {
quote!(true)
}
[first, rest @ ..] => {
let rest = rest.iter().map(|x| {
let t1 = Ident::new(format!("{}_self", x.text), x.span);
let t2 = Ident::new(format!("{}_other", x.text), x.span);
let and_and = and_and();
quote!(#and_and #t1 .eq( #t2 ))
});
let first = {
let t1 = Ident::new(format!("{}_self", first.text), first.span);
let t2 = Ident::new(format!("{}_other", first.text), first.span);
quote!(#t1 .eq( #t2 ))
};
quote!(#first ##rest)
}
};
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
},
);
let fat_arrow = fat_arrow();
quote! {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
##arms
_unused #fat_arrow false
}
}
}
})
}
fn self_and_other_patterns(
adt: &BasicAdtInfo,
name: &tt::Ident,
) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
let self_patterns = adt.shape.as_pattern_map(name, |x| {
let t = Ident::new(format!("{}_self", x.text), x.span);
quote!(#t)
});
let other_patterns = adt.shape.as_pattern_map(name, |x| {
let t = Ident::new(format!("{}_other", x.text), x.span);
quote!(#t)
});
(self_patterns, other_patterns)
} }
fn ord_expand( fn ord_expand(
@ -316,8 +716,63 @@ fn ord_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::Ord }) expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| {
fn compare(
krate: &tt::TokenTree,
left: tt::Subtree,
right: tt::Subtree,
rest: tt::Subtree,
) -> tt::Subtree {
let fat_arrow1 = fat_arrow();
let fat_arrow2 = fat_arrow();
quote! {
match #left.cmp(&#right) {
#krate::cmp::Ordering::Equal #fat_arrow1 {
#rest
}
c #fat_arrow2 return c,
}
}
}
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote!();
}
let left = quote!(#krate::intrinsics::discriminant_value(self));
let right = quote!(#krate::intrinsics::discriminant_value(other));
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|(pat1, pat2, fields)| {
let mut body = quote!(#krate::cmp::Ordering::Equal);
for f in fields.into_iter().rev() {
let t1 = Ident::new(format!("{}_self", f.text), f.span);
let t2 = Ident::new(format!("{}_other", f.text), f.span);
body = compare(krate, quote!(#t1), quote!(#t2), body);
}
let fat_arrow = fat_arrow();
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
},
);
let fat_arrow = fat_arrow();
let body = compare(
krate,
left,
right,
quote! {
match (self, other) {
##arms
_unused #fat_arrow #krate::cmp::Ordering::Equal
}
},
);
quote! {
fn cmp(&self, other: &Self) -> #krate::cmp::Ordering {
#body
}
}
})
} }
fn partial_ord_expand( fn partial_ord_expand(
@ -325,6 +780,61 @@ fn partial_ord_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }) expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| {
fn compare(
krate: &tt::TokenTree,
left: tt::Subtree,
right: tt::Subtree,
rest: tt::Subtree,
) -> tt::Subtree {
let fat_arrow1 = fat_arrow();
let fat_arrow2 = fat_arrow();
quote! {
match #left.partial_cmp(&#right) {
#krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 {
#rest
}
c #fat_arrow2 return c,
}
}
}
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote!();
}
let left = quote!(#krate::intrinsics::discriminant_value(self));
let right = quote!(#krate::intrinsics::discriminant_value(other));
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|(pat1, pat2, fields)| {
let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
for f in fields.into_iter().rev() {
let t1 = Ident::new(format!("{}_self", f.text), f.span);
let t2 = Ident::new(format!("{}_other", f.text), f.span);
body = compare(krate, quote!(#t1), quote!(#t2), body);
}
let fat_arrow = fat_arrow();
quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
},
);
let fat_arrow = fat_arrow();
let body = compare(
krate,
left,
right,
quote! {
match (self, other) {
##arms
_unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal)
}
},
);
quote! {
fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> {
#body
}
}
})
} }

View file

@ -162,6 +162,12 @@ impl ToTokenTree for crate::tt::TokenTree {
} }
} }
impl ToTokenTree for &crate::tt::TokenTree {
fn to_token(self) -> crate::tt::TokenTree {
self.clone()
}
}
impl ToTokenTree for crate::tt::Subtree { impl ToTokenTree for crate::tt::Subtree {
fn to_token(self) -> crate::tt::TokenTree { fn to_token(self) -> crate::tt::TokenTree {
self.into() self.into()

View file

@ -1227,6 +1227,53 @@ fn from_trait() {
); );
} }
#[test]
fn builtin_derive_macro() {
check_number(
r#"
//- minicore: clone, derive, builtin_impls
#[derive(Clone)]
enum Z {
Foo(Y),
Bar,
}
#[derive(Clone)]
struct X(i32, Z, i64)
#[derive(Clone)]
struct Y {
field1: i32,
field2: u8,
}
const GOAL: u8 = {
let x = X(2, Z::Foo(Y { field1: 4, field2: 5 }), 8);
let x = x.clone();
let Z::Foo(t) = x.1;
t.field2
};
"#,
5,
);
check_number(
r#"
//- minicore: default, derive, builtin_impls
#[derive(Default)]
struct X(i32, Y, i64)
#[derive(Default)]
struct Y {
field1: i32,
field2: u8,
}
const GOAL: u8 = {
let x = X::default();
x.1.field2
};
"#,
0,
);
}
#[test] #[test]
fn try_operator() { fn try_operator() {
check_number( check_number(

View file

@ -151,9 +151,11 @@ fn _format(
_db: &RootDatabase, _db: &RootDatabase,
_kind: SyntaxKind, _kind: SyntaxKind,
_file_id: FileId, _file_id: FileId,
_expansion: &str, expansion: &str,
) -> Option<String> { ) -> Option<String> {
None // remove trailing spaces for test
use itertools::Itertools;
Some(expansion.lines().map(|x| x.trim_end()).join("\n"))
} }
#[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))] #[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))]
@ -276,8 +278,7 @@ f$0oo!();
"#, "#,
expect![[r#" expect![[r#"
foo! foo!
fn b(){} fn b(){}"#]],
"#]],
); );
} }
@ -471,8 +472,17 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Clone Clone
impl < >core::clone::Clone for Foo< >where{} impl < >core::clone::Clone for Foo< >where {
"#]], fn clone(&self) -> Self {
match self {
Foo{}
=> Foo{}
,
}
}
}"#]],
); );
} }
@ -488,8 +498,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Copy Copy
impl < >core::marker::Copy for Foo< >where{} impl < >core::marker::Copy for Foo< >where{}"#]],
"#]],
); );
} }
@ -504,8 +513,7 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Copy Copy
impl < >core::marker::Copy for Foo< >where{} impl < >core::marker::Copy for Foo< >where{}"#]],
"#]],
); );
check( check(
r#" r#"
@ -516,8 +524,17 @@ struct Foo {}
"#, "#,
expect![[r#" expect![[r#"
Clone Clone
impl < >core::clone::Clone for Foo< >where{} impl < >core::clone::Clone for Foo< >where {
"#]], fn clone(&self) -> Self {
match self {
Foo{}
=> Foo{}
,
}
}
}"#]],
); );
} }
} }

View file

@ -11,6 +11,7 @@
//! add: //! add:
//! as_ref: sized //! as_ref: sized
//! bool_impl: option, fn //! bool_impl: option, fn
//! builtin_impls:
//! cell: copy, drop //! cell: copy, drop
//! clone: sized //! clone: sized
//! coerce_unsized: unsize //! coerce_unsized: unsize
@ -127,6 +128,27 @@ pub mod default {
#[rustc_builtin_macro(Default, attributes(default))] #[rustc_builtin_macro(Default, attributes(default))]
pub macro Default($item:item) {} pub macro Default($item:item) {}
// endregion:derive // endregion:derive
// region:builtin_impls
macro_rules! impl_default {
($v:literal; $($t:ty)*) => {
$(
impl const Default for $t {
fn default() -> Self {
$v
}
}
)*
}
}
impl_default! {
0; usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128
}
impl_default! {
0.0; f32 f64
}
// endregion:builtin_impls
} }
// endregion:default // endregion:default
@ -137,6 +159,11 @@ pub mod hash {
pub trait Hash { pub trait Hash {
fn hash<H: Hasher>(&self, state: &mut H); fn hash<H: Hasher>(&self, state: &mut H);
} }
// region:derive
#[rustc_builtin_macro]
pub macro Hash($item:item) {}
// endregion:derive
} }
// endregion:hash // endregion:hash
@ -198,6 +225,28 @@ pub mod clone {
*self *self
} }
} }
// region:builtin_impls
macro_rules! impl_clone {
($($t:ty)*) => {
$(
impl const Clone for $t {
fn clone(&self) -> Self {
*self
}
}
)*
}
}
impl_clone! {
usize u8 u16 u32 u64 u128
isize i8 i16 i32 i64 i128
f32 f64
bool char
}
// endregion:builtin_impls
// region:derive // region:derive
#[rustc_builtin_macro] #[rustc_builtin_macro]
pub macro Clone($item:item) {} pub macro Clone($item:item) {}
@ -723,6 +772,11 @@ pub mod fmt {
pub trait Display { pub trait Display {
fn fmt(&self, f: &mut Formatter<'_>) -> Result; fn fmt(&self, f: &mut Formatter<'_>) -> Result;
} }
// region:derive
#[rustc_builtin_macro]
pub macro Debug($item:item) {}
// endregion:derive
} }
// endregion:fmt // endregion:fmt

View file

@ -153,6 +153,12 @@ pub struct Ident<Span> {
pub span: Span, pub span: Span,
} }
impl<S> Ident<S> {
pub fn new(text: impl Into<SmolStr>, span: S) -> Self {
Ident { text: text.into(), span }
}
}
fn print_debug_subtree<Span: fmt::Debug>( fn print_debug_subtree<Span: fmt::Debug>(
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
subtree: &Subtree<Span>, subtree: &Subtree<Span>,