2015-03-06 21:15:54 +00:00
|
|
|
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
2012-12-04 00:48:01 +00:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2014-12-31 04:25:18 +00:00
|
|
|
//! The compiler code necessary to implement the `#[derive]` extensions.
|
2012-11-20 02:05:50 +00:00
|
|
|
|
2016-03-08 18:24:28 +00:00
|
|
|
use syntax::ast::{MetaItem, MetaItemKind, self};
|
2015-12-10 14:23:14 +00:00
|
|
|
use syntax::attr::AttrMetaMethods;
|
|
|
|
use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable};
|
|
|
|
use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
|
|
|
|
use syntax::ext::build::AstBuilder;
|
|
|
|
use syntax::feature_gate;
|
2016-05-23 06:27:43 +00:00
|
|
|
use syntax::codemap::{self, Span};
|
2015-12-10 14:23:14 +00:00
|
|
|
use syntax::parse::token::{intern, intern_and_get_ident};
|
2016-03-10 05:31:19 +00:00
|
|
|
use syntax::ptr::P;
|
2014-05-16 07:16:13 +00:00
|
|
|
|
2014-09-07 20:58:41 +00:00
|
|
|
macro_rules! pathvec {
|
|
|
|
($($x:ident)::+) => (
|
|
|
|
vec![ $( stringify!($x) ),+ ]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! path {
|
|
|
|
($($x:tt)*) => (
|
|
|
|
::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2015-02-05 07:19:07 +00:00
|
|
|
macro_rules! path_local {
|
|
|
|
($x:ident) => (
|
2015-12-10 14:23:14 +00:00
|
|
|
::deriving::generic::ty::Path::new_local(stringify!($x))
|
2015-02-05 07:19:07 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2014-09-07 21:57:26 +00:00
|
|
|
macro_rules! pathvec_std {
|
2015-07-30 00:01:14 +00:00
|
|
|
($cx:expr, $first:ident :: $($rest:ident)::+) => ({
|
|
|
|
let mut v = pathvec!($($rest)::+);
|
|
|
|
if let Some(s) = $cx.crate_root {
|
|
|
|
v.insert(0, s);
|
2014-09-07 21:57:26 +00:00
|
|
|
}
|
2015-07-30 00:01:14 +00:00
|
|
|
v
|
|
|
|
})
|
2014-09-07 21:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! path_std {
|
|
|
|
($($x:tt)*) => (
|
2015-12-10 14:23:14 +00:00
|
|
|
::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
|
2014-09-07 21:57:26 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2014-04-30 09:26:50 +00:00
|
|
|
pub mod bounds;
|
2013-03-28 10:50:10 +00:00
|
|
|
pub mod clone;
|
2013-04-10 23:31:51 +00:00
|
|
|
pub mod encodable;
|
2013-04-09 01:53:39 +00:00
|
|
|
pub mod decodable;
|
2014-02-22 05:33:23 +00:00
|
|
|
pub mod hash;
|
2015-10-18 16:03:42 +00:00
|
|
|
pub mod debug;
|
2013-09-12 04:51:13 +00:00
|
|
|
pub mod default;
|
2012-11-20 02:05:50 +00:00
|
|
|
|
2015-04-02 16:14:53 +00:00
|
|
|
#[path="cmp/partial_eq.rs"]
|
|
|
|
pub mod partial_eq;
|
2013-03-30 14:58:05 +00:00
|
|
|
#[path="cmp/eq.rs"]
|
|
|
|
pub mod eq;
|
2015-04-02 16:14:53 +00:00
|
|
|
#[path="cmp/partial_ord.rs"]
|
|
|
|
pub mod partial_ord;
|
2013-03-30 14:58:05 +00:00
|
|
|
#[path="cmp/ord.rs"]
|
|
|
|
pub mod ord;
|
|
|
|
|
|
|
|
|
2013-03-28 10:50:10 +00:00
|
|
|
pub mod generic;
|
|
|
|
|
2015-03-06 21:15:54 +00:00
|
|
|
fn expand_derive(cx: &mut ExtCtxt,
|
2015-04-25 02:04:46 +00:00
|
|
|
span: Span,
|
2015-03-06 21:15:54 +00:00
|
|
|
mitem: &MetaItem,
|
2015-04-28 05:34:39 +00:00
|
|
|
annotatable: Annotatable)
|
|
|
|
-> Annotatable {
|
2016-03-11 18:27:42 +00:00
|
|
|
debug!("expand_derive: span = {:?}", span);
|
|
|
|
debug!("expand_derive: mitem = {:?}", mitem);
|
|
|
|
debug!("expand_derive: annotatable input = {:?}", annotatable);
|
|
|
|
let annot = annotatable.map_item_or(|item| {
|
2015-04-28 05:34:39 +00:00
|
|
|
item.map(|mut item| {
|
|
|
|
if mitem.value_str().is_some() {
|
|
|
|
cx.span_err(mitem.span, "unexpected value in `derive`");
|
|
|
|
}
|
2015-03-06 21:15:54 +00:00
|
|
|
|
2015-04-28 05:34:39 +00:00
|
|
|
let traits = mitem.meta_item_list().unwrap_or(&[]);
|
|
|
|
if traits.is_empty() {
|
|
|
|
cx.span_warn(mitem.span, "empty trait list in `derive`");
|
|
|
|
}
|
2015-03-06 21:15:54 +00:00
|
|
|
|
2016-03-25 14:02:56 +00:00
|
|
|
let mut found_partial_eq = false;
|
|
|
|
let mut found_eq = false;
|
|
|
|
|
2015-04-28 05:34:39 +00:00
|
|
|
for titem in traits.iter().rev() {
|
|
|
|
let tname = match titem.node {
|
2016-02-09 11:05:20 +00:00
|
|
|
MetaItemKind::Word(ref tname) => tname,
|
2015-04-28 05:34:39 +00:00
|
|
|
_ => {
|
|
|
|
cx.span_err(titem.span, "malformed `derive` entry");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
|
|
|
|
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
|
|
|
|
"custom_derive",
|
|
|
|
titem.span,
|
2015-09-04 23:37:22 +00:00
|
|
|
feature_gate::GateIssue::Language,
|
2015-04-28 05:34:39 +00:00
|
|
|
feature_gate::EXPLAIN_CUSTOM_DERIVE);
|
2015-03-06 21:15:54 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-03-11 18:27:42 +00:00
|
|
|
if &tname[..] == "Eq" {
|
2016-03-25 14:02:56 +00:00
|
|
|
found_eq = true;
|
|
|
|
} else if &tname[..] == "PartialEq" {
|
|
|
|
found_partial_eq = true;
|
2016-03-11 18:27:42 +00:00
|
|
|
}
|
|
|
|
|
2016-05-23 06:27:43 +00:00
|
|
|
let span = Span {
|
|
|
|
expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
|
|
|
|
call_site: titem.span,
|
|
|
|
callee: codemap::NameAndSpan {
|
|
|
|
format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
|
|
|
|
span: Some(titem.span),
|
|
|
|
allow_internal_unstable: true,
|
|
|
|
},
|
|
|
|
}), ..titem.span
|
|
|
|
};
|
|
|
|
|
2015-04-28 05:34:39 +00:00
|
|
|
// #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
|
2016-04-06 22:43:03 +00:00
|
|
|
item.attrs.push(cx.attribute(span, cx.meta_word(titem.span,
|
2015-04-28 05:34:39 +00:00
|
|
|
intern_and_get_ident(&format!("derive_{}", tname)))));
|
|
|
|
}
|
2015-03-06 21:15:54 +00:00
|
|
|
|
2016-03-25 14:02:56 +00:00
|
|
|
// RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
|
|
|
|
// `#[structural_match]` attribute.
|
|
|
|
if found_partial_eq && found_eq {
|
2016-05-23 06:27:43 +00:00
|
|
|
// This span is **very** sensitive and crucial to
|
|
|
|
// getting the stability behavior we want. What we are
|
|
|
|
// doing is marking `#[structural_match]` with the
|
|
|
|
// span of the `#[deriving(...)]` attribute (the
|
|
|
|
// entire attribute, not just the `PartialEq` or `Eq`
|
|
|
|
// part), but with the current backtrace. The current
|
|
|
|
// backtrace will contain a topmost entry that IS this
|
|
|
|
// `#[deriving(...)]` attribute and with the
|
|
|
|
// "allow-unstable" flag set to true.
|
|
|
|
//
|
|
|
|
// Note that we do NOT use the span of the `Eq`
|
|
|
|
// text itself. You might think this is
|
|
|
|
// equivalent, because the `Eq` appears within the
|
|
|
|
// `#[deriving(Eq)]` attribute, and hence we would
|
|
|
|
// inherit the "allows unstable" from the
|
|
|
|
// backtrace. But in fact this is not always the
|
|
|
|
// case. The actual source text that led to
|
|
|
|
// deriving can be `#[$attr]`, for example, where
|
|
|
|
// `$attr == deriving(Eq)`. In that case, the
|
|
|
|
// "#[structural_match]" would be considered to
|
|
|
|
// originate not from the deriving call but from
|
|
|
|
// text outside the deriving call, and hence would
|
|
|
|
// be forbidden from using unstable
|
|
|
|
// content.
|
|
|
|
//
|
|
|
|
// See tests src/run-pass/rfc1445 for
|
|
|
|
// examples. --nmatsakis
|
|
|
|
let span = Span { expn_id: cx.backtrace(), .. span };
|
|
|
|
assert!(cx.parse_sess.codemap().span_allows_unstable(span));
|
2016-03-25 14:02:56 +00:00
|
|
|
debug!("inserting structural_match with span {:?}", span);
|
|
|
|
let structural_match = intern_and_get_ident("structural_match");
|
|
|
|
item.attrs.push(cx.attribute(span,
|
|
|
|
cx.meta_word(span,
|
|
|
|
structural_match)));
|
|
|
|
}
|
|
|
|
|
2015-04-28 05:34:39 +00:00
|
|
|
item
|
|
|
|
})
|
|
|
|
}, |a| {
|
|
|
|
cx.span_err(span, "`derive` can only be applied to items");
|
|
|
|
a
|
2016-03-11 18:27:42 +00:00
|
|
|
});
|
|
|
|
debug!("expand_derive: annotatable output = {:?}", annot);
|
|
|
|
annot
|
2015-03-06 21:15:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! derive_traits {
|
2015-03-27 01:07:49 +00:00
|
|
|
($( $name:expr => $func:path, )+) => {
|
2015-03-06 21:15:54 +00:00
|
|
|
pub fn register_all(env: &mut SyntaxEnv) {
|
|
|
|
// Define the #[derive_*] extensions.
|
|
|
|
$({
|
|
|
|
struct DeriveExtension;
|
|
|
|
|
2015-04-28 05:34:39 +00:00
|
|
|
impl MultiItemDecorator for DeriveExtension {
|
2015-03-06 21:15:54 +00:00
|
|
|
fn expand(&self,
|
|
|
|
ecx: &mut ExtCtxt,
|
|
|
|
sp: Span,
|
|
|
|
mitem: &MetaItem,
|
2015-05-22 15:40:14 +00:00
|
|
|
annotatable: &Annotatable,
|
2015-04-28 05:34:39 +00:00
|
|
|
push: &mut FnMut(Annotatable)) {
|
2016-04-06 22:43:03 +00:00
|
|
|
if !ecx.parse_sess.codemap().span_allows_unstable(sp)
|
|
|
|
&& !ecx.ecfg.features.unwrap().custom_derive {
|
|
|
|
// FIXME:
|
|
|
|
// https://github.com/rust-lang/rust/pull/32671#issuecomment-206245303
|
|
|
|
// This is just to avoid breakage with syntex.
|
|
|
|
// Remove that to spawn an error instead.
|
|
|
|
let cm = ecx.parse_sess.codemap();
|
|
|
|
let parent = cm.with_expn_info(ecx.backtrace(),
|
|
|
|
|info| info.unwrap().call_site.expn_id);
|
|
|
|
cm.with_expn_info(parent, |info| {
|
|
|
|
if info.is_some() {
|
|
|
|
let mut w = ecx.parse_sess.span_diagnostic.struct_span_warn(
|
|
|
|
sp, feature_gate::EXPLAIN_DERIVE_UNDERSCORE,
|
|
|
|
);
|
|
|
|
if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_none() {
|
2016-04-29 22:08:02 +00:00
|
|
|
w.help(
|
|
|
|
&format!("add #![feature(custom_derive)] to \
|
|
|
|
the crate attributes to enable")
|
2016-04-06 22:43:03 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
w.emit();
|
|
|
|
} else {
|
|
|
|
feature_gate::emit_feature_err(
|
|
|
|
&ecx.parse_sess.span_diagnostic,
|
|
|
|
"custom_derive", sp, feature_gate::GateIssue::Language,
|
|
|
|
feature_gate::EXPLAIN_DERIVE_UNDERSCORE
|
|
|
|
);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-03-06 21:15:54 +00:00
|
|
|
warn_if_deprecated(ecx, sp, $name);
|
2015-04-28 05:34:39 +00:00
|
|
|
$func(ecx, sp, mitem, annotatable, push);
|
2013-03-11 20:47:23 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-06 21:15:54 +00:00
|
|
|
|
|
|
|
env.insert(intern(concat!("derive_", $name)),
|
2015-04-28 05:34:39 +00:00
|
|
|
MultiDecorator(Box::new(DeriveExtension)));
|
2015-03-27 01:07:49 +00:00
|
|
|
})+
|
2015-03-06 21:15:54 +00:00
|
|
|
|
|
|
|
env.insert(intern("derive"),
|
2015-04-28 05:34:39 +00:00
|
|
|
MultiModifier(Box::new(expand_derive)));
|
2015-03-06 21:15:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_builtin_trait(name: &str) -> bool {
|
|
|
|
match name {
|
2015-03-27 01:07:49 +00:00
|
|
|
$( $name )|+ => true,
|
2015-03-06 21:15:54 +00:00
|
|
|
_ => false,
|
2014-02-13 07:53:52 +00:00
|
|
|
}
|
2013-03-11 20:47:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-06 21:15:54 +00:00
|
|
|
|
|
|
|
derive_traits! {
|
|
|
|
"Clone" => clone::expand_deriving_clone,
|
|
|
|
|
|
|
|
"Hash" => hash::expand_deriving_hash,
|
|
|
|
|
|
|
|
"RustcEncodable" => encodable::expand_deriving_rustc_encodable,
|
|
|
|
|
|
|
|
"RustcDecodable" => decodable::expand_deriving_rustc_decodable,
|
|
|
|
|
2015-04-02 16:14:53 +00:00
|
|
|
"PartialEq" => partial_eq::expand_deriving_partial_eq,
|
|
|
|
"Eq" => eq::expand_deriving_eq,
|
|
|
|
"PartialOrd" => partial_ord::expand_deriving_partial_ord,
|
|
|
|
"Ord" => ord::expand_deriving_ord,
|
2015-03-06 21:15:54 +00:00
|
|
|
|
2015-10-18 16:03:42 +00:00
|
|
|
"Debug" => debug::expand_deriving_debug,
|
2015-03-06 21:15:54 +00:00
|
|
|
|
|
|
|
"Default" => default::expand_deriving_default,
|
|
|
|
|
|
|
|
"Send" => bounds::expand_deriving_unsafe_bound,
|
|
|
|
"Sync" => bounds::expand_deriving_unsafe_bound,
|
|
|
|
"Copy" => bounds::expand_deriving_copy,
|
|
|
|
|
|
|
|
// deprecated
|
|
|
|
"Encodable" => encodable::expand_deriving_encodable,
|
|
|
|
"Decodable" => decodable::expand_deriving_decodable,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline] // because `name` is a compile-time constant
|
|
|
|
fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
|
|
|
|
if let Some(replacement) = match name {
|
|
|
|
"Encodable" => Some("RustcEncodable"),
|
|
|
|
"Decodable" => Some("RustcDecodable"),
|
|
|
|
_ => None,
|
|
|
|
} {
|
|
|
|
ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
|
|
|
|
name, replacement));
|
|
|
|
}
|
|
|
|
}
|
2016-03-08 18:24:28 +00:00
|
|
|
|
|
|
|
/// Construct a name for the inner type parameter that can't collide with any type parameters of
|
|
|
|
/// the item. This is achieved by starting with a base and then concatenating the names of all
|
|
|
|
/// other type parameters.
|
|
|
|
// FIXME(aburka): use real hygiene when that becomes possible
|
|
|
|
fn hygienic_type_parameter(item: &Annotatable, base: &str) -> String {
|
|
|
|
let mut typaram = String::from(base);
|
|
|
|
if let Annotatable::Item(ref item) = *item {
|
|
|
|
match item.node {
|
|
|
|
ast::ItemKind::Struct(_, ast::Generics { ref ty_params, .. }) |
|
|
|
|
ast::ItemKind::Enum(_, ast::Generics { ref ty_params, .. }) => {
|
|
|
|
|
|
|
|
for ty in ty_params.iter() {
|
|
|
|
typaram.push_str(&ty.ident.name.as_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typaram
|
|
|
|
}
|
|
|
|
|
2016-03-10 05:31:19 +00:00
|
|
|
/// Constructs an expression that calls an intrinsic
|
|
|
|
fn call_intrinsic(cx: &ExtCtxt,
|
|
|
|
span: Span,
|
|
|
|
intrinsic: &str,
|
|
|
|
args: Vec<P<ast::Expr>>) -> P<ast::Expr> {
|
|
|
|
let path = cx.std_path(&["intrinsics", intrinsic]);
|
|
|
|
let call = cx.expr_call_global(span, path, args);
|
|
|
|
|
|
|
|
cx.expr_block(P(ast::Block {
|
|
|
|
stmts: vec![],
|
|
|
|
expr: Some(call),
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
|
|
|
|
span: span }))
|
|
|
|
}
|
|
|
|
|