diff options
| author | Josh Driver <keeperofdakeys@gmail.com> | 2017-02-01 21:03:09 +1030 |
|---|---|---|
| committer | Josh Driver <keeperofdakeys@gmail.com> | 2017-02-05 09:31:02 +1030 |
| commit | fbdd0388664127c3dd2c3e6a162b597a87ffa7c9 (patch) | |
| tree | 96056b6ba5bb09e6618c7ed99f70cf5347cc9082 /src/libsyntax/ext/derive.rs | |
| parent | 0a7380d7fcd99ef288ee038fd145da5af41ce84a (diff) | |
| download | rust-fbdd0388664127c3dd2c3e6a162b597a87ffa7c9.tar.gz rust-fbdd0388664127c3dd2c3e6a162b597a87ffa7c9.zip | |
Move derive macro expansion into the MacroExpander
This removes the expand_derives function, and sprinkles the functionality throughout the Invocation Collector, Expander and Resolver.
Diffstat (limited to 'src/libsyntax/ext/derive.rs')
| -rw-r--r-- | src/libsyntax/ext/derive.rs | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs new file mode 100644 index 00000000000..946448eaaee --- /dev/null +++ b/src/libsyntax/ext/derive.rs @@ -0,0 +1,218 @@ +// Copyright 2012-2017 The Rust Project Developers. See the COPYRIGHT +// 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. + +use ast::Name; +use attr; +use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension}; +use codemap; +use ext::build::AstBuilder; +use feature_gate; +use symbol::Symbol; +use syntax_pos::Span; + +pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) + -> Option<&'a NestedMetaItem> { + if attr.name() != "derive" { + return None; + } + if attr.value_str().is_some() { + cx.span_err(attr.span, "unexpected value in `derive`"); + return None; + } + + let traits = attr.meta_item_list().unwrap_or(&[]); + + if traits.is_empty() { + cx.span_warn(attr.span, "empty trait list in `derive`"); + return None; + } + + return traits.get(0); +} + +pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) { + for attr in attrs { + if attr.name() != "derive" { + continue; + } + + if attr.value_str().is_some() { + cx.span_err(attr.span, "unexpected value in `derive`"); + } + + let traits = attr.meta_item_list().unwrap_or(&[]).to_owned(); + + if traits.is_empty() { + cx.span_warn(attr.span, "empty trait list in `derive`"); + attr::mark_used(&attr); + continue; + } + for titem in traits { + if titem.word().is_none() { + cx.span_err(titem.span, "malformed `derive` entry"); + } + } + } +} + +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum DeriveType { + Legacy, + ProcMacro, + Builtin +} + +impl DeriveType { + // Classify a derive trait name by resolving the macro. + pub fn classify(cx: &mut ExtCtxt, tname: Name) -> DeriveType { + let legacy_derive_name = Symbol::intern(&format!("derive_{}", tname)); + + if let Ok(_) = cx.resolver.resolve_builtin_macro(legacy_derive_name) { + return DeriveType::Legacy; + } + + match cx.resolver.resolve_builtin_macro(tname) { + Ok(ext) => match *ext { + SyntaxExtension::BuiltinDerive(..) => DeriveType::Builtin, + _ => DeriveType::ProcMacro, + }, + Err(_) => DeriveType::ProcMacro, + } + } +} + +pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>, + derive_type: DeriveType) -> Option<ast::Attribute> { + for i in 0..attrs.len() { + if attrs[i].name() != "derive" { + continue; + } + + if attrs[i].value_str().is_some() { + continue; + } + + let mut traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); + + // First, weed out malformed #[derive] + traits.retain(|titem| titem.word().is_some()); + + let mut titem = None; + + // See if we can find a matching trait. + for j in 0..traits.len() { + let tname = match traits[j].name() { + Some(tname) => tname, + _ => continue, + }; + + if DeriveType::classify(cx, tname) == derive_type { + titem = Some(traits.remove(j)); + break; + } + } + + // If we find a trait, remove the trait from the attribute. + if let Some(titem) = titem { + if traits.len() == 0 { + attrs.remove(i); + } else { + let derive = Symbol::intern("derive"); + let mitem = cx.meta_list(titem.span, derive, traits); + attrs[i] = cx.attribute(titem.span, mitem); + } + let derive = Symbol::intern("derive"); + let mitem = cx.meta_list(titem.span, derive, vec![titem]); + return Some(cx.attribute(mitem.span, mitem)); + } + } + return None; +} + +fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { + Span { + expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { + call_site: span, + callee: codemap::NameAndSpan { + format: codemap::MacroAttribute(Symbol::intern(attr_name)), + span: Some(span), + allow_internal_unstable: true, + }, + }), + ..span + } +} + +pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) { + if attrs.is_empty() { + return; + } + + let titems = attrs.iter().filter(|a| { + a.name() == "derive" + }).flat_map(|a| { + a.meta_item_list().unwrap_or(&[]).iter() + }).filter_map(|titem| { + titem.name() + }).collect::<Vec<_>>(); + + let span = attrs[0].span; + + if !attrs.iter().any(|a| a.name() == "structural_match") && + titems.iter().any(|t| *t == "PartialEq") && titems.iter().any(|t| *t == "Eq") { + let structural_match = Symbol::intern("structural_match"); + let span = allow_unstable(cx, span, "derive(PartialEq, Eq)"); + let meta = cx.meta_word(span, structural_match); + attrs.push(cx.attribute(span, meta)); + } + + if !attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") && + titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") { + let structural_match = Symbol::intern("rustc_copy_clone_marker"); + let span = allow_unstable(cx, span, "derive(Copy, Clone)"); + let meta = cx.meta_word(span, structural_match); + attrs.push(cx.attribute(span, meta)); + } +} + +pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) + -> Option<ast::Attribute> { + verify_derive_attrs(cx, attrs); + get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| { + let titem = derive_attr_trait(cx, &a); + titem.and_then(|titem| { + let tword = titem.word().unwrap(); + let tname = tword.name(); + if !cx.ecfg.enable_custom_derive() { + feature_gate::emit_feature_err( + &cx.parse_sess, + "custom_derive", + titem.span, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_CUSTOM_DERIVE + ); + None + } else { + let name = Symbol::intern(&format!("derive_{}", tname)); + if !cx.resolver.is_whitelisted_legacy_custom_derive(name) { + cx.span_warn(titem.span, + feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE); + } + let mitem = cx.meta_word(titem.span, name); + Some(cx.attribute(mitem.span, mitem)) + } + }) + }).or_else(|| { + get_derive_attr(cx, attrs, DeriveType::ProcMacro) + }).or_else(|| { + add_derived_markers(cx, attrs); + get_derive_attr(cx, attrs, DeriveType::Builtin) + }) +} |
