|
| 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 | +
|
| 6 | +use hir_expand::{InFile, builtin::BuiltinDeriveExpander, name::Name}; |
| 7 | +use intern::{Symbol, sym}; |
| 8 | +use tt::TextRange; |
| 9 | + |
| 10 | +use crate::{ |
| 11 | + AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase, |
| 12 | +}; |
| 13 | + |
| 14 | +macro_rules! declare_enum { |
| 15 | + ( $( $trait:ident => [ $( $method:ident ),* ] ),* $(,)? ) => { |
| 16 | + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
| 17 | + pub enum BuiltinDeriveImplTrait { |
| 18 | + $( $trait, )* |
| 19 | + } |
| 20 | + |
| 21 | + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
| 22 | + #[allow(non_camel_case_types)] |
| 23 | + pub enum BuiltinDeriveImplMethod { |
| 24 | + $( $( $method, )* )* |
| 25 | + } |
| 26 | + |
| 27 | + impl BuiltinDeriveImplTrait { |
| 28 | + #[inline] |
| 29 | + pub fn name(self) -> Symbol { |
| 30 | + match self { |
| 31 | + $( Self::$trait => sym::$trait, )* |
| 32 | + } |
| 33 | + } |
| 34 | + |
| 35 | + #[inline] |
| 36 | + pub fn get_id(self, lang_items: &crate::lang_item::LangItems) -> Option<crate::TraitId> { |
| 37 | + match self { |
| 38 | + $( Self::$trait => lang_items.$trait, )* |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + #[inline] |
| 43 | + pub fn get_method(self, method_name: &Symbol) -> Option<BuiltinDeriveImplMethod> { |
| 44 | + match self { |
| 45 | + $( |
| 46 | + Self::$trait => { |
| 47 | + match method_name { |
| 48 | + $( _ if *method_name == sym::$method => Some(BuiltinDeriveImplMethod::$method), )* |
| 49 | + _ => None, |
| 50 | + } |
| 51 | + } |
| 52 | + )* |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + #[inline] |
| 57 | + pub fn all_methods(self) -> &'static [BuiltinDeriveImplMethod] { |
| 58 | + match self { |
| 59 | + $( Self::$trait => &[ $(BuiltinDeriveImplMethod::$method),* ], )* |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + impl BuiltinDeriveImplMethod { |
| 65 | + #[inline] |
| 66 | + pub fn name(self) -> Symbol { |
| 67 | + match self { |
| 68 | + $( $( BuiltinDeriveImplMethod::$method => sym::$method, )* )* |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | + }; |
| 73 | +} |
| 74 | + |
| 75 | +declare_enum!( |
| 76 | + Copy => [], |
| 77 | + Clone => [clone], |
| 78 | + Default => [default], |
| 79 | + Debug => [fmt], |
| 80 | + Hash => [hash], |
| 81 | + Ord => [cmp], |
| 82 | + PartialOrd => [partial_cmp], |
| 83 | + Eq => [], |
| 84 | + PartialEq => [eq], |
| 85 | + CoerceUnsized => [], |
| 86 | + DispatchFromDyn => [], |
| 87 | +); |
| 88 | + |
| 89 | +impl BuiltinDeriveImplMethod { |
| 90 | + pub fn trait_method( |
| 91 | + self, |
| 92 | + db: &dyn DefDatabase, |
| 93 | + impl_: BuiltinDeriveImplId, |
| 94 | + ) -> Option<FunctionId> { |
| 95 | + let loc = impl_.loc(db); |
| 96 | + let lang_items = crate::lang_item::lang_items(db, loc.krate(db)); |
| 97 | + let trait_ = impl_.loc(db).trait_.get_id(lang_items)?; |
| 98 | + trait_.trait_items(db).method_by_name(&Name::new_symbol_root(self.name())) |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +pub(crate) fn with_derive_traits( |
| 103 | + derive: BuiltinDeriveExpander, |
| 104 | + mut f: impl FnMut(BuiltinDeriveImplTrait), |
| 105 | +) { |
| 106 | + let trait_ = match derive { |
| 107 | + BuiltinDeriveExpander::Copy => BuiltinDeriveImplTrait::Copy, |
| 108 | + BuiltinDeriveExpander::Clone => BuiltinDeriveImplTrait::Clone, |
| 109 | + BuiltinDeriveExpander::Default => BuiltinDeriveImplTrait::Default, |
| 110 | + BuiltinDeriveExpander::Debug => BuiltinDeriveImplTrait::Debug, |
| 111 | + BuiltinDeriveExpander::Hash => BuiltinDeriveImplTrait::Hash, |
| 112 | + BuiltinDeriveExpander::Ord => BuiltinDeriveImplTrait::Ord, |
| 113 | + BuiltinDeriveExpander::PartialOrd => BuiltinDeriveImplTrait::PartialOrd, |
| 114 | + BuiltinDeriveExpander::Eq => BuiltinDeriveImplTrait::Eq, |
| 115 | + BuiltinDeriveExpander::PartialEq => BuiltinDeriveImplTrait::PartialEq, |
| 116 | + BuiltinDeriveExpander::CoercePointee => { |
| 117 | + f(BuiltinDeriveImplTrait::CoerceUnsized); |
| 118 | + f(BuiltinDeriveImplTrait::DispatchFromDyn); |
| 119 | + return; |
| 120 | + } |
| 121 | + }; |
| 122 | + f(trait_); |
| 123 | +} |
| 124 | + |
| 125 | +impl BuiltinDeriveImplLoc { |
| 126 | + pub fn source(&self, db: &dyn DefDatabase) -> InFile<TextRange> { |
| 127 | + let (adt_ast_id, module) = match self.adt { |
| 128 | + AdtId::StructId(adt) => { |
| 129 | + let adt_loc = adt.loc(db); |
| 130 | + (adt_loc.id.upcast(), adt_loc.container) |
| 131 | + } |
| 132 | + AdtId::UnionId(adt) => { |
| 133 | + let adt_loc = adt.loc(db); |
| 134 | + (adt_loc.id.upcast(), adt_loc.container) |
| 135 | + } |
| 136 | + AdtId::EnumId(adt) => { |
| 137 | + let adt_loc = adt.loc(db); |
| 138 | + (adt_loc.id.upcast(), adt_loc.container) |
| 139 | + } |
| 140 | + }; |
| 141 | + let derive_range = self.derive_attr_id.find_derive_range( |
| 142 | + db, |
| 143 | + module.krate(db), |
| 144 | + adt_ast_id, |
| 145 | + self.derive_index, |
| 146 | + ); |
| 147 | + adt_ast_id.with_value(derive_range) |
| 148 | + } |
| 149 | +} |
0 commit comments