Skip to main content

hir_def/
builtin_derive.rs

1//! Definition of builtin derive impls.
2//!
3//! To save time and memory, builtin derives are not really expanded. Instead, we record them
4//! and create their impls based on lowered data, see crates/hir-ty/src/builtin_derive.rs.
5
6use base_db::SourceDatabase;
7use hir_expand::{InFile, builtin::BuiltinDeriveExpander, name::Name};
8use intern::{Symbol, sym};
9use tt::TextRange;
10
11use crate::{
12    AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, MacroId,
13    lang_item::LangItems,
14};
15
16macro_rules! declare_enum {
17    ( $( $trait:ident => [ $( $method:ident ),* ] ),* $(,)? ) => {
18        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19        pub enum BuiltinDeriveImplTrait {
20            $( $trait, )*
21        }
22
23        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24        #[allow(non_camel_case_types)]
25        pub enum BuiltinDeriveImplMethod {
26            $( $( $method, )* )*
27        }
28
29        impl BuiltinDeriveImplTrait {
30            #[inline]
31            pub fn name(self) -> Symbol {
32                match self {
33                    $( Self::$trait => sym::$trait, )*
34                }
35            }
36
37            #[inline]
38            pub fn get_id(self, lang_items: &crate::lang_item::LangItems) -> Option<crate::TraitId> {
39                match self {
40                    $( Self::$trait => lang_items.$trait, )*
41                }
42            }
43
44            #[inline]
45            pub fn get_method(self, method_name: &Symbol) -> Option<BuiltinDeriveImplMethod> {
46                match self {
47                    $(
48                        Self::$trait => {
49                            match method_name {
50                                $( _ if *method_name == sym::$method => Some(BuiltinDeriveImplMethod::$method), )*
51                                _ => None,
52                            }
53                        }
54                    )*
55                }
56            }
57
58            #[inline]
59            pub fn all_methods(self) -> &'static [BuiltinDeriveImplMethod] {
60                match self {
61                    $( Self::$trait => &[ $(BuiltinDeriveImplMethod::$method),* ], )*
62                }
63            }
64        }
65
66        impl BuiltinDeriveImplMethod {
67            #[inline]
68            pub fn name(self) -> Symbol {
69                match self {
70                    $( $( BuiltinDeriveImplMethod::$method => sym::$method, )* )*
71                }
72            }
73        }
74    };
75}
76
77declare_enum!(
78    Copy => [],
79    Clone => [clone],
80    Default => [default],
81    Debug => [fmt],
82    Hash => [hash],
83    Ord => [cmp],
84    PartialOrd => [partial_cmp],
85    Eq => [],
86    PartialEq => [eq],
87    CoerceUnsized => [],
88    DispatchFromDyn => [],
89    Reborrow => [],
90);
91
92impl BuiltinDeriveImplTrait {
93    pub fn derive_macro(self, lang_items: &LangItems) -> Option<MacroId> {
94        match self {
95            BuiltinDeriveImplTrait::Copy => lang_items.CopyDerive,
96            BuiltinDeriveImplTrait::Clone => lang_items.CloneDerive,
97            BuiltinDeriveImplTrait::Default => lang_items.DefaultDerive,
98            BuiltinDeriveImplTrait::Debug => lang_items.DebugDerive,
99            BuiltinDeriveImplTrait::Hash => lang_items.HashDerive,
100            BuiltinDeriveImplTrait::Ord => lang_items.OrdDerive,
101            BuiltinDeriveImplTrait::PartialOrd => lang_items.PartialOrdDerive,
102            BuiltinDeriveImplTrait::Eq => lang_items.EqDerive,
103            BuiltinDeriveImplTrait::PartialEq => lang_items.PartialEqDerive,
104            BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
105                lang_items.CoercePointeeDerive
106            }
107            BuiltinDeriveImplTrait::Reborrow => lang_items.ReborrowDerive,
108        }
109    }
110}
111
112pub(crate) fn has_builtin_derive_impl(derive: BuiltinDeriveExpander) -> bool {
113    !matches!(derive, BuiltinDeriveExpander::CoerceShared)
114}
115
116impl BuiltinDeriveImplMethod {
117    pub fn trait_method(
118        self,
119        db: &dyn SourceDatabase,
120        impl_: BuiltinDeriveImplId,
121    ) -> Option<FunctionId> {
122        let loc = impl_.loc(db);
123        let lang_items = crate::lang_item::lang_items(db, loc.krate(db));
124        let trait_ = impl_.loc(db).trait_.get_id(lang_items)?;
125        trait_.trait_items(db).method_by_name(&Name::new_symbol_root(self.name()))
126    }
127}
128
129pub(crate) fn with_derive_traits(
130    derive: BuiltinDeriveExpander,
131    mut f: impl FnMut(BuiltinDeriveImplTrait),
132) {
133    debug_assert!(has_builtin_derive_impl(derive));
134    match derive {
135        BuiltinDeriveExpander::Copy => f(BuiltinDeriveImplTrait::Copy),
136        BuiltinDeriveExpander::Clone => f(BuiltinDeriveImplTrait::Clone),
137        BuiltinDeriveExpander::Default => f(BuiltinDeriveImplTrait::Default),
138        BuiltinDeriveExpander::Debug => f(BuiltinDeriveImplTrait::Debug),
139        BuiltinDeriveExpander::Hash => f(BuiltinDeriveImplTrait::Hash),
140        BuiltinDeriveExpander::Ord => f(BuiltinDeriveImplTrait::Ord),
141        BuiltinDeriveExpander::PartialOrd => f(BuiltinDeriveImplTrait::PartialOrd),
142        BuiltinDeriveExpander::Eq => f(BuiltinDeriveImplTrait::Eq),
143        BuiltinDeriveExpander::PartialEq => f(BuiltinDeriveImplTrait::PartialEq),
144        BuiltinDeriveExpander::CoercePointee => {
145            f(BuiltinDeriveImplTrait::CoerceUnsized);
146            f(BuiltinDeriveImplTrait::DispatchFromDyn);
147        }
148        BuiltinDeriveExpander::Reborrow => f(BuiltinDeriveImplTrait::Reborrow),
149        BuiltinDeriveExpander::CoerceShared => {}
150    }
151}
152
153impl BuiltinDeriveImplLoc {
154    pub fn source(&self, db: &dyn SourceDatabase) -> InFile<TextRange> {
155        let (adt_ast_id, module) = match self.adt {
156            AdtId::StructId(adt) => {
157                let adt_loc = adt.loc(db);
158                (adt_loc.id.upcast(), adt_loc.container)
159            }
160            AdtId::UnionId(adt) => {
161                let adt_loc = adt.loc(db);
162                (adt_loc.id.upcast(), adt_loc.container)
163            }
164            AdtId::EnumId(adt) => {
165                let adt_loc = adt.loc(db);
166                (adt_loc.id.upcast(), adt_loc.container)
167            }
168        };
169        let derive_range = self.derive_attr_id.find_derive_range(
170            db,
171            module.krate(db),
172            adt_ast_id,
173            self.derive_index,
174        );
175        adt_ast_id.with_value(derive_range)
176    }
177}