]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/mod.rs
Auto merge of #60669 - c410-f3r:attrs-fn, r=petrochenkov
[rust.git] / src / libsyntax_ext / deriving / mod.rs
1 //! The compiler code necessary to implement the `#[derive]` extensions.
2
3 use rustc_data_structures::sync::Lrc;
4 use syntax::ast::{self, MetaItem};
5 use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension, Resolver, MultiItemModifier};
6 use syntax::ext::build::AstBuilder;
7 use syntax::ext::hygiene::{Mark, SyntaxContext};
8 use syntax::ptr::P;
9 use syntax::symbol::{Symbol, sym};
10 use syntax_pos::Span;
11
12 macro path_local($x:ident) {
13     generic::ty::Path::new_local(stringify!($x))
14 }
15
16 macro pathvec_std($cx:expr, $($rest:ident)::+) {{
17     vec![ $( stringify!($rest) ),+ ]
18 }}
19
20 macro path_std($($x:tt)*) {
21     generic::ty::Path::new( pathvec_std!( $($x)* ) )
22 }
23
24 pub mod bounds;
25 pub mod clone;
26 pub mod encodable;
27 pub mod decodable;
28 pub mod hash;
29 pub mod debug;
30 pub mod default;
31 pub mod custom;
32
33 #[path="cmp/partial_eq.rs"]
34 pub mod partial_eq;
35 #[path="cmp/eq.rs"]
36 pub mod eq;
37 #[path="cmp/partial_ord.rs"]
38 pub mod partial_ord;
39 #[path="cmp/ord.rs"]
40 pub mod ord;
41
42 pub mod generic;
43
44 struct BuiltinDerive(
45     fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable))
46 );
47
48 impl MultiItemModifier for BuiltinDerive {
49     fn expand(&self,
50               ecx: &mut ExtCtxt<'_>,
51               span: Span,
52               meta_item: &MetaItem,
53               item: Annotatable)
54               -> Vec<Annotatable> {
55         let mut items = Vec::new();
56         (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a));
57         items
58     }
59 }
60
61 macro_rules! derive_traits {
62     ($( $name:expr => $func:path, )+) => {
63         pub fn is_builtin_trait(name: ast::Name) -> bool {
64             match &*name.as_str() {
65                 $( $name )|+ => true,
66                 _ => false,
67             }
68         }
69
70         pub fn register_builtin_derives(resolver: &mut dyn Resolver) {
71             $(
72                 resolver.add_builtin(
73                     ast::Ident::with_empty_ctxt(Symbol::intern($name)),
74                     Lrc::new(SyntaxExtension::LegacyDerive(Box::new(BuiltinDerive($func))))
75                 );
76             )*
77         }
78     }
79 }
80
81 derive_traits! {
82     "Clone" => clone::expand_deriving_clone,
83
84     "Hash" => hash::expand_deriving_hash,
85
86     "RustcEncodable" => encodable::expand_deriving_rustc_encodable,
87
88     "RustcDecodable" => decodable::expand_deriving_rustc_decodable,
89
90     "PartialEq" => partial_eq::expand_deriving_partial_eq,
91     "Eq" => eq::expand_deriving_eq,
92     "PartialOrd" => partial_ord::expand_deriving_partial_ord,
93     "Ord" => ord::expand_deriving_ord,
94
95     "Debug" => debug::expand_deriving_debug,
96
97     "Default" => default::expand_deriving_default,
98
99     "Send" => bounds::expand_deriving_unsafe_bound,
100     "Sync" => bounds::expand_deriving_unsafe_bound,
101     "Copy" => bounds::expand_deriving_copy,
102
103     // deprecated
104     "Encodable" => encodable::expand_deriving_encodable,
105     "Decodable" => decodable::expand_deriving_decodable,
106 }
107
108 #[inline] // because `name` is a compile-time constant
109 fn warn_if_deprecated(ecx: &mut ExtCtxt<'_>, sp: Span, name: &str) {
110     if let Some(replacement) = match name {
111         "Encodable" => Some("RustcEncodable"),
112         "Decodable" => Some("RustcDecodable"),
113         _ => None,
114     } {
115         ecx.span_warn(sp,
116                       &format!("derive({}) is deprecated in favor of derive({})",
117                                name,
118                                replacement));
119     }
120 }
121
122 /// Construct a name for the inner type parameter that can't collide with any type parameters of
123 /// the item. This is achieved by starting with a base and then concatenating the names of all
124 /// other type parameters.
125 // FIXME(aburka): use real hygiene when that becomes possible
126 fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
127     let mut typaram = String::from(base);
128     if let Annotatable::Item(ref item) = *item {
129         match item.node {
130             ast::ItemKind::Struct(_, ast::Generics { ref params, .. }) |
131             ast::ItemKind::Enum(_, ast::Generics { ref params, .. }) => {
132                 for param in params {
133                     match param.kind {
134                         ast::GenericParamKind::Type { .. } => {
135                             typaram.push_str(&param.ident.as_str());
136                         }
137                         _ => {}
138                     }
139                 }
140             }
141
142             _ => {}
143         }
144     }
145
146     typaram
147 }
148
149 /// Constructs an expression that calls an intrinsic
150 fn call_intrinsic(cx: &ExtCtxt<'_>,
151                   mut span: Span,
152                   intrinsic: &str,
153                   args: Vec<P<ast::Expr>>)
154                   -> P<ast::Expr> {
155     let intrinsic_allowed_via_allow_internal_unstable = cx
156         .current_expansion.mark.expn_info().unwrap()
157         .allow_internal_unstable.map_or(false, |features| features.iter().any(|&s|
158             s == sym::core_intrinsics
159         ));
160     if intrinsic_allowed_via_allow_internal_unstable {
161         span = span.with_ctxt(cx.backtrace());
162     } else { // Avoid instability errors with user defined curstom derives, cc #36316
163         let mut info = cx.current_expansion.mark.expn_info().unwrap();
164         info.allow_internal_unstable = Some(vec![sym::core_intrinsics].into());
165         let mark = Mark::fresh(Mark::root());
166         mark.set_expn_info(info);
167         span = span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
168     }
169     let path = cx.std_path(&[sym::intrinsics, Symbol::intern(intrinsic)]);
170     let call = cx.expr_call_global(span, path, args);
171
172     cx.expr_block(P(ast::Block {
173         stmts: vec![cx.stmt_expr(call)],
174         id: ast::DUMMY_NODE_ID,
175         rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
176         span,
177     }))
178 }