about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhkalbasi <hamidrezakalbasi@protonmail.com>2023-05-03 14:14:47 +0330
committerhkalbasi <hamidrezakalbasi@protonmail.com>2023-05-03 14:14:47 +0330
commitd9f4cbbe8f261278cb16ad462b56d5eafbd22bed (patch)
treeed3a391bfef85cdea564280690012dac5b6e72ce
parent9811a3af5fb90834f42f4b35b4f531746af934ca (diff)
downloadrust-d9f4cbbe8f261278cb16ad462b56d5eafbd22bed.tar.gz
rust-d9f4cbbe8f261278cb16ad462b56d5eafbd22bed.zip
Emit function bodies in expanding builtin derives
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs292
-rw-r--r--crates/hir-expand/src/builtin_derive_macro.rs554
-rw-r--r--crates/hir-expand/src/quote.rs6
-rw-r--r--crates/hir-ty/src/consteval/tests.rs47
-rw-r--r--crates/ide/src/expand_macro.rs41
-rw-r--r--crates/test-utils/src/minicore.rs54
-rw-r--r--crates/tt/src/lib.rs6
7 files changed, 962 insertions, 38 deletions
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 37cf348c92d..9ea688a8c1a 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
@@ -84,13 +84,33 @@ fn test_clone_expand() {
         r#"
 //- minicore: derive, clone
 #[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+    Move { x: A, y: B },
+    Do(&'static str),
+    Jump,
+}
 "#,
         expect![[r#"
 #[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)]
 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"),
+        }
+    }
+}"#]],
     );
 }
diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs
index 4b9f9704c25..4ce71e9774b 100644
--- a/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/crates/hir-expand/src/builtin_derive_macro.rs
@@ -1,12 +1,19 @@
 //! Builtin derives.
 
+use ::tt::Ident;
 use base_db::{CrateOrigin, LangCrateOrigin};
+use itertools::izip;
+use mbe::TokenMap;
 use std::collections::HashSet;
+use stdx::never;
 use tracing::debug;
 
 use crate::tt::{self, TokenId};
 use syntax::{
-    ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, PathType},
+    ast::{
+        self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName,
+        HasTypeBounds, PathType,
+    },
     match_ast,
 };
 
@@ -59,8 +66,124 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
     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 {
     name: tt::Ident,
+    shape: AdtShape,
     /// 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
@@ -79,11 +202,24 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
         ExpandError::Other("no item found".into())
     })?;
     let node = item.syntax();
-    let (name, params) = match_ast! {
+    let (name, params, shape) = 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(), AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?)),
+            ast::Enum(it) => {
+                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);
                 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)
         .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
         .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(|| {
         debug!("parsed item has no name");
         ExpandError::Other("missing name".into())
@@ -161,7 +302,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
     let name_token_id =
         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() };
-    Ok(BasicAdtInfo { name: name_token, param_types, associated_types })
+    Ok(name_token)
 }
 
 /// 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
 /// 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) {
         Ok(info) => info,
         Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
+    let trait_body = trait_body(&info);
     let mut where_block = vec![];
     let (params, args): (Vec<_>, Vec<_>) = info
         .param_types
@@ -227,7 +373,7 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
 
     let name = info.name;
     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)
 }
@@ -254,7 +400,7 @@ fn copy_expand(
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
     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(
@@ -263,7 +409,63 @@ fn clone_expand(
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
     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(
@@ -271,8 +473,38 @@ fn default_expand(
     id: MacroCallId,
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::default::Default })
+    let krate = &find_builtin_crate(db, id);
+    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(
@@ -280,8 +512,79 @@ fn debug_expand(
     id: MacroCallId,
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::fmt::Debug })
+    let krate = &find_builtin_crate(db, id);
+    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(
@@ -289,8 +592,47 @@ fn hash_expand(
     id: MacroCallId,
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::hash::Hash })
+    let krate = &find_builtin_crate(db, id);
+    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(
@@ -299,7 +641,7 @@ fn eq_expand(
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
     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(
@@ -308,7 +650,65 @@ fn partial_eq_expand(
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
     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(
@@ -316,8 +716,63 @@ fn ord_expand(
     id: MacroCallId,
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::Ord })
+    let krate = &find_builtin_crate(db, id);
+    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(
@@ -325,6 +780,61 @@ fn partial_ord_expand(
     id: MacroCallId,
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
+    let krate = &find_builtin_crate(db, id);
+    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
+            }
+        }
+    })
 }
diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs
index 63586f9daf0..ab3809abc7a 100644
--- a/crates/hir-expand/src/quote.rs
+++ b/crates/hir-expand/src/quote.rs
@@ -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 {
     fn to_token(self) -> crate::tt::TokenTree {
         self.into()
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 5a850f6d570..12b15065ddf 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1228,6 +1228,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]
 fn try_operator() {
     check_number(
         r#"
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 91af5716ca5..8c03d197e1c 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -151,9 +151,11 @@ fn _format(
     _db: &RootDatabase,
     _kind: SyntaxKind,
     _file_id: FileId,
-    _expansion: &str,
+    expansion: &str,
 ) -> 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")))]
@@ -276,8 +278,7 @@ f$0oo!();
 "#,
             expect![[r#"
                 foo!
-                fn b(){}
-            "#]],
+                fn b(){}"#]],
         );
     }
 
@@ -471,8 +472,17 @@ struct Foo {}
 "#,
             expect![[r#"
                 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#"
                 Copy
-                impl < >core::marker::Copy for Foo< >where{}
-            "#]],
+                impl < >core::marker::Copy for Foo< >where{}"#]],
         );
     }
 
@@ -504,8 +513,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Copy
-                impl < >core::marker::Copy for Foo< >where{}
-            "#]],
+                impl < >core::marker::Copy for Foo< >where{}"#]],
         );
         check(
             r#"
@@ -516,8 +524,17 @@ struct Foo {}
 "#,
             expect![[r#"
                 Clone
-                impl < >core::clone::Clone for Foo< >where{}
-            "#]],
+                impl < >core::clone::Clone for Foo< >where {
+                  fn clone(&self) -> Self {
+                    match self {
+                      Foo{}
+                       => Foo{}
+                      ,
+
+                      }
+                  }
+
+                  }"#]],
         );
     }
 }
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index f403ef8ee03..c693235f344 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -11,6 +11,7 @@
 //!     add:
 //!     as_ref: sized
 //!     bool_impl: option, fn
+//!     builtin_impls:
 //!     cell: copy, drop
 //!     clone: sized
 //!     coerce_unsized: unsize
@@ -127,6 +128,27 @@ pub mod default {
     #[rustc_builtin_macro(Default, attributes(default))]
     pub macro Default($item:item) {}
     // 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
 
@@ -137,6 +159,11 @@ pub mod hash {
     pub trait Hash {
         fn hash<H: Hasher>(&self, state: &mut H);
     }
+
+    // region:derive
+    #[rustc_builtin_macro]
+    pub macro Hash($item:item) {}
+    // endregion:derive
 }
 // endregion:hash
 
@@ -198,6 +225,28 @@ pub mod clone {
             *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
     #[rustc_builtin_macro]
     pub macro Clone($item:item) {}
@@ -723,6 +772,11 @@ pub mod fmt {
     pub trait Display {
         fn fmt(&self, f: &mut Formatter<'_>) -> Result;
     }
+
+    // region:derive
+    #[rustc_builtin_macro]
+    pub macro Debug($item:item) {}
+    // endregion:derive
 }
 // endregion:fmt
 
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index b7dbc82e1d6..c2ebf03746a 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -153,6 +153,12 @@ pub struct Ident<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>(
     f: &mut fmt::Formatter<'_>,
     subtree: &Subtree<Span>,