From 6690cfe25f1ea4a430cc08efbe4a4d4ef0b736b2 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Tue, 24 Jan 2017 01:31:49 +1030 Subject: [PATCH 01/18] Make builtin derives a SyntaxExtension This allows builtin derives to be registered and resolved, just like other derive types. --- src/libsyntax/ext/base.rs | 8 ++++- src/libsyntax/ext/expand.rs | 4 +-- src/libsyntax_ext/deriving/decodable.rs | 1 + src/libsyntax_ext/deriving/encodable.rs | 1 + src/libsyntax_ext/deriving/mod.rs | 42 ++++++++++++------------- src/libsyntax_ext/lib.rs | 2 ++ 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index edf74e1fe19f..231e2e6205cf 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -10,7 +10,7 @@ pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT}; -use ast::{self, Attribute, Name, PatKind}; +use ast::{self, Attribute, Name, PatKind, MetaItem}; use attr::HasAttrs; use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; @@ -471,6 +471,9 @@ impl MacResult for DummyResult { } } +pub type BuiltinDeriveFn = + for<'cx> fn(&'cx mut ExtCtxt, Span, &MetaItem, &Annotatable, &mut FnMut(Annotatable)); + /// An enum representing the different kinds of syntax extensions. pub enum SyntaxExtension { /// A syntax extension that is attached to an item and creates new items @@ -508,6 +511,9 @@ pub enum SyntaxExtension { IdentTT(Box, Option, bool), CustomDerive(Box), + + /// An attribute-like procedural macro that derives a builtin trait. + BuiltinDerive(BuiltinDeriveFn), } pub type NamedSyntaxExtension = (Name, SyntaxExtension); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 226625ebc8e5..0e5d94e03810 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -370,7 +370,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let tok_result = mac.expand(self.cx, attr.span, attr_toks, item_toks); self.parse_expansion(tok_result, kind, name, attr.span) } - SyntaxExtension::CustomDerive(_) => { + SyntaxExtension::CustomDerive(..) | SyntaxExtension::BuiltinDerive(..) => { self.cx.span_err(attr.span, &format!("`{}` is a derive mode", name)); kind.dummy(attr.span) } @@ -440,7 +440,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { return kind.dummy(span); } - SyntaxExtension::CustomDerive(..) => { + SyntaxExtension::CustomDerive(..) | SyntaxExtension::BuiltinDerive(..) => { self.cx.span_err(path.span, &format!("`{}` is a derive mode", extname)); return kind.dummy(span); } diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs index e2634c60dcaa..6359d642d157 100644 --- a/src/libsyntax_ext/deriving/decodable.rs +++ b/src/libsyntax_ext/deriving/decodable.rs @@ -35,6 +35,7 @@ pub fn expand_deriving_decodable(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { + deriving::warn_if_deprecated(cx, span, "Decodable"); expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize") } diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs index 092738ab8a03..a276193e81b9 100644 --- a/src/libsyntax_ext/deriving/encodable.rs +++ b/src/libsyntax_ext/deriving/encodable.rs @@ -112,6 +112,7 @@ pub fn expand_deriving_encodable(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { + deriving::warn_if_deprecated(cx, span, "Encodable"); expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize") } diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 096f6dfd5d8d..30d0da588a5d 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -10,10 +10,11 @@ //! The compiler code necessary to implement the `#[derive]` extensions. +use std::rc::Rc; use syntax::ast::{self, MetaItem}; use syntax::attr::HasAttrs; use syntax::codemap; -use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension}; +use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension, Resolver}; use syntax::ext::build::AstBuilder; use syntax::feature_gate; use syntax::ptr::P; @@ -292,7 +293,10 @@ pub fn expand_derive(cx: &mut ExtCtxt, for titem in traits.iter() { let tname = titem.word().unwrap().name(); let name = Symbol::intern(&format!("derive({})", tname)); + let tname_cx = ast::Ident::with_empty_ctxt(titem.name().unwrap()); let mitem = cx.meta_word(titem.span, name); + let path = ast::Path::from_ident(titem.span, tname_cx); + let ext = cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).unwrap(); let span = Span { expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { @@ -306,11 +310,15 @@ pub fn expand_derive(cx: &mut ExtCtxt, ..titem.span }; - let my_item = Annotatable::Item(item); - expand_builtin(&tname.as_str(), cx, span, &mitem, &my_item, &mut |a| { - items.push(a); - }); - item = my_item.expect_item(); + if let SyntaxExtension::BuiltinDerive(ref func) = *ext { + let my_item = Annotatable::Item(item); + func(cx, span, &mitem, &my_item, &mut |a| { + items.push(a) + }); + item = my_item.expect_item(); + } else { + unreachable!(); + } } items.insert(0, Annotatable::Item(item)); @@ -326,21 +334,13 @@ macro_rules! derive_traits { } } - fn expand_builtin(name: &str, - ecx: &mut ExtCtxt, - span: Span, - mitem: &MetaItem, - item: &Annotatable, - push: &mut FnMut(Annotatable)) { - match name { - $( - $name => { - warn_if_deprecated(ecx, span, $name); - $func(ecx, span, mitem, item, push); - } - )* - _ => panic!("not a builtin derive mode: {}", name), - } + pub fn register_builtin_derives(resolver: &mut Resolver) { + $( + resolver.add_ext( + ast::Ident::with_empty_ctxt(Symbol::intern($name)), + Rc::new(SyntaxExtension::BuiltinDerive($func)) + ); + )* } } } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index ebec23d0901a..e872cfaeacb7 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -57,6 +57,8 @@ use syntax::symbol::Symbol; pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, user_exts: Vec, enable_quotes: bool) { + deriving::register_builtin_derives(resolver); + let mut register = |name, ext| { resolver.add_ext(ast::Ident::with_empty_ctxt(name), Rc::new(ext)); }; From bcf859c589c10e6c10c2d39fa18846b98a1743dc Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Tue, 24 Jan 2017 08:55:08 +1030 Subject: [PATCH 02/18] Rename CustomDerive to ProcMacroDerive for macros 1.1 --- src/librustc_metadata/creader.rs | 6 +++--- src/librustc_resolve/build_reduced_graph.rs | 2 +- src/libsyntax/ext/base.rs | 6 +++++- src/libsyntax/ext/expand.rs | 4 ++-- src/libsyntax_ext/deriving/custom.rs | 18 +++++++++--------- src/libsyntax_ext/deriving/mod.rs | 4 ++-- src/libsyntax_ext/proc_macro_registrar.rs | 8 ++++---- .../proc-macro/derive-bad.rs | 2 +- .../proc-macro/load-panic.rs | 2 +- .../proc-macro/no-macro-use-attr.rs | 2 +- src/test/compile-fail/no-link.rs | 2 +- src/test/ui/custom-derive/issue-36935.stderr | 2 +- 12 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 161331b1728b..8cb123b54f16 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -577,7 +577,7 @@ impl<'a> CrateLoader<'a> { use proc_macro::TokenStream; use proc_macro::__internal::Registry; use rustc_back::dynamic_lib::DynamicLibrary; - use syntax_ext::deriving::custom::CustomDerive; + use syntax_ext::deriving::custom::ProcMacroDerive; use syntax_ext::proc_macro_impl::AttrProcMacro; let path = match dylib { @@ -609,8 +609,8 @@ impl<'a> CrateLoader<'a> { expand: fn(TokenStream) -> TokenStream, attributes: &[&'static str]) { let attrs = attributes.iter().cloned().map(Symbol::intern).collect(); - let derive = SyntaxExtension::CustomDerive( - Box::new(CustomDerive::new(expand, attrs)) + let derive = SyntaxExtension::ProcMacroDerive( + Box::new(ProcMacroDerive::new(expand, attrs)) ); self.0.push((Symbol::intern(trait_name), Rc::new(derive))); } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index f74af416cde0..95e3c96a2522 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -546,7 +546,7 @@ impl<'a> Resolver<'a> { "an `extern crate` loading macros must be at the crate root"); } else if !self.use_extern_macros && !used && self.session.cstore.dep_kind(module.def_id().unwrap().krate).macros_only() { - let msg = "custom derive crates and `#[no_link]` crates have no effect without \ + let msg = "proc macro crates and `#[no_link]` crates have no effect without \ `#[macro_use]`"; self.session.span_warn(item.span, msg); used = true; // Avoid the normal unused extern crate warning diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 231e2e6205cf..17b0b97468df 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -510,7 +510,11 @@ pub enum SyntaxExtension { /// IdentTT(Box, Option, bool), - CustomDerive(Box), + /// An attribute-like procedural macro. TokenStream -> TokenStream. + /// The input is the annotated item. + /// Allows generating code to implement a Trait for a given struct + /// or enum item. + ProcMacroDerive(Box), /// An attribute-like procedural macro that derives a builtin trait. BuiltinDerive(BuiltinDeriveFn), diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0e5d94e03810..01a8c215d47a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -370,7 +370,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let tok_result = mac.expand(self.cx, attr.span, attr_toks, item_toks); self.parse_expansion(tok_result, kind, name, attr.span) } - SyntaxExtension::CustomDerive(..) | SyntaxExtension::BuiltinDerive(..) => { + SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => { self.cx.span_err(attr.span, &format!("`{}` is a derive mode", name)); kind.dummy(attr.span) } @@ -440,7 +440,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { return kind.dummy(span); } - SyntaxExtension::CustomDerive(..) | SyntaxExtension::BuiltinDerive(..) => { + SyntaxExtension::ProcMacroDerive(..) | SyntaxExtension::BuiltinDerive(..) => { self.cx.span_err(path.span, &format!("`{}` is a derive mode", extname)); return kind.dummy(span); } diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index 2ce6fc03f773..e118ef1ea01f 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -32,18 +32,18 @@ impl<'a> Visitor<'a> for MarkAttrs<'a> { fn visit_mac(&mut self, _mac: &Mac) {} } -pub struct CustomDerive { +pub struct ProcMacroDerive { inner: fn(TokenStream) -> TokenStream, attrs: Vec, } -impl CustomDerive { - pub fn new(inner: fn(TokenStream) -> TokenStream, attrs: Vec) -> CustomDerive { - CustomDerive { inner: inner, attrs: attrs } +impl ProcMacroDerive { + pub fn new(inner: fn(TokenStream) -> TokenStream, attrs: Vec) -> ProcMacroDerive { + ProcMacroDerive { inner: inner, attrs: attrs } } } -impl MultiItemModifier for CustomDerive { +impl MultiItemModifier for ProcMacroDerive { fn expand(&self, ecx: &mut ExtCtxt, span: Span, @@ -54,7 +54,7 @@ impl MultiItemModifier for CustomDerive { Annotatable::Item(item) => item, Annotatable::ImplItem(_) | Annotatable::TraitItem(_) => { - ecx.span_err(span, "custom derive attributes may only be \ + ecx.span_err(span, "proc_macro_derive attributes may only be \ applied to struct/enum items"); return Vec::new() } @@ -63,7 +63,7 @@ impl MultiItemModifier for CustomDerive { ItemKind::Struct(..) | ItemKind::Enum(..) => {}, _ => { - ecx.span_err(span, "custom derive attributes may only be \ + ecx.span_err(span, "proc_macro_derive attributes may only be \ applied to struct/enum items"); return Vec::new() } @@ -81,7 +81,7 @@ impl MultiItemModifier for CustomDerive { let stream = match res { Ok(stream) => stream, Err(e) => { - let msg = "custom derive attribute panicked"; + let msg = "proc_macro_derive attribute panicked"; let mut err = ecx.struct_span_fatal(span, msg); if let Some(s) = e.downcast_ref::() { err.help(&format!("message: {}", s)); @@ -100,7 +100,7 @@ impl MultiItemModifier for CustomDerive { Ok(new_items) => new_items, Err(_) => { // FIXME: handle this better - let msg = "custom derive produced unparseable tokens"; + let msg = "proc_macro_derive produced unparseable tokens"; ecx.struct_span_fatal(span, msg).emit(); panic!(FatalError); } diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 30d0da588a5d..311b8ae41f8b 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -163,7 +163,7 @@ pub fn expand_derive(cx: &mut ExtCtxt, if is_builtin_trait(tname) || { let derive_mode = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(tname)); cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| { - if let SyntaxExtension::CustomDerive(_) = *ext { true } else { false } + if let SyntaxExtension::ProcMacroDerive(_) = *ext { true } else { false } }).unwrap_or(false) } { return true; @@ -249,7 +249,7 @@ pub fn expand_derive(cx: &mut ExtCtxt, ..mitem.span }; - if let SyntaxExtension::CustomDerive(ref ext) = *ext { + if let SyntaxExtension::ProcMacroDerive(ref ext) = *ext { return ext.expand(cx, span, &mitem, item); } else { unreachable!() diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index c8af16e9242f..325f09a83dda 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -27,7 +27,7 @@ use syntax_pos::{Span, DUMMY_SP}; use deriving; -struct CustomDerive { +struct ProcMacroDerive { trait_name: ast::Name, function_name: Ident, span: Span, @@ -40,7 +40,7 @@ struct AttrProcMacro { } struct CollectProcMacros<'a> { - derives: Vec, + derives: Vec, attr_macros: Vec, in_root: bool, handler: &'a errors::Handler, @@ -176,7 +176,7 @@ impl<'a> CollectProcMacros<'a> { }; if self.in_root && item.vis == ast::Visibility::Public { - self.derives.push(CustomDerive { + self.derives.push(ProcMacroDerive { span: item.span, trait_name: trait_name, function_name: item.ident, @@ -319,7 +319,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { // } // } fn mk_registrar(cx: &mut ExtCtxt, - custom_derives: &[CustomDerive], + custom_derives: &[ProcMacroDerive], custom_attrs: &[AttrProcMacro]) -> P { let eid = cx.codemap().record_expansion(ExpnInfo { call_site: DUMMY_SP, diff --git a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs index a5359946c09c..bc4da9fee47e 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs @@ -16,7 +16,7 @@ extern crate derive_bad; #[derive( A )] -//~^^ ERROR: custom derive produced unparseable tokens +//~^^ ERROR: proc_macro_derive produced unparseable tokens struct A; fn main() {} diff --git a/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs b/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs index f9906b650fb8..107273d012dd 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs @@ -14,7 +14,7 @@ extern crate derive_panic; #[derive(A)] -//~^ ERROR: custom derive attribute panicked +//~^ ERROR: proc_macro_derive attribute panicked //~| HELP: message: nope! struct Foo; diff --git a/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs b/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs index f61b8b4073b6..e47a4aefb5e0 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/no-macro-use-attr.rs @@ -13,7 +13,7 @@ #![feature(rustc_attrs)] extern crate derive_a; -//~^ WARN custom derive crates and `#[no_link]` crates have no effect without `#[macro_use]` +//~^ WARN proc macro crates and `#[no_link]` crates have no effect without `#[macro_use]` #[rustc_error] fn main() {} //~ ERROR compilation successful diff --git a/src/test/compile-fail/no-link.rs b/src/test/compile-fail/no-link.rs index d8e7411bded2..f74ff55e2c08 100644 --- a/src/test/compile-fail/no-link.rs +++ b/src/test/compile-fail/no-link.rs @@ -12,7 +12,7 @@ #[no_link] extern crate empty_struct; -//~^ WARN custom derive crates and `#[no_link]` crates have no effect without `#[macro_use]` +//~^ WARN proc macro crates and `#[no_link]` crates have no effect without `#[macro_use]` fn main() { empty_struct::XEmpty1; //~ ERROR cannot find value `XEmpty1` in module `empty_struct` diff --git a/src/test/ui/custom-derive/issue-36935.stderr b/src/test/ui/custom-derive/issue-36935.stderr index 213366a307d4..ad1382cbc8e4 100644 --- a/src/test/ui/custom-derive/issue-36935.stderr +++ b/src/test/ui/custom-derive/issue-36935.stderr @@ -1,4 +1,4 @@ -error: custom derive attribute panicked +error: proc_macro_derive attribute panicked --> $DIR/issue-36935.rs:17:15 | 17 | #[derive(Foo, Bar)] From 4331790ec5b0936989636f12cf993a97d46589bd Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Sat, 28 Jan 2017 15:55:35 +1030 Subject: [PATCH 03/18] Move derive expansion into the InvocationCollector --- src/libsyntax/ext/derive.rs | 250 ++++++++++++++++++++++++ src/libsyntax/ext/expand.rs | 134 +++++++++++-- src/libsyntax/lib.rs | 1 + src/libsyntax_ext/deriving/decodable.rs | 3 +- src/libsyntax_ext/deriving/encodable.rs | 3 +- src/libsyntax_ext/deriving/mod.rs | 239 +--------------------- src/libsyntax_ext/lib.rs | 5 +- 7 files changed, 378 insertions(+), 257 deletions(-) create mode 100644 src/libsyntax/ext/derive.rs diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs new file mode 100644 index 000000000000..919ff496a9d6 --- /dev/null +++ b/src/libsyntax/ext/derive.rs @@ -0,0 +1,250 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast::{self, NestedMetaItem}; +use ext::base::{ExtCtxt, SyntaxExtension}; +use ext::build::AstBuilder; +use feature_gate; +use symbol::Symbol; +use syntax_pos::Span; +use codemap; + +pub fn is_derive_attr(attr: &ast::Attribute) -> bool { + let res = attr.name() == Symbol::intern("derive"); + res +} + +pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) + -> Option<&'a NestedMetaItem> { + if !is_derive_attr(attr) { + 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(); + + 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: &mut Vec) { + for i in 0..attrs.len() { + if !is_derive_attr(&attrs[i]) { + continue; + } + + let span = attrs[i].span; + + if attrs[i].value_str().is_some() { + cx.span_err(span, "unexpected value in `derive`"); + } + + let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); + + if traits.is_empty() { + cx.span_warn(span, "empty trait list in `derive`"); + continue; + } + for titem in traits { + if titem.word().is_none() { + cx.span_err(titem.span, "malformed `derive` entry"); + } + } + } +} + +pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, + is_derive_type: fn(&mut ExtCtxt, &NestedMetaItem) -> bool) + -> Option { + for i in 0..attrs.len() { + if !is_derive_attr(&attrs[i]) { + continue; + } + + let span = attrs[i].span; + + 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() { + if is_derive_type(cx, &traits[j]) { + 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(span, derive, traits); + attrs[i] = cx.attribute(span, mitem); + } + let derive = Symbol::intern("derive"); + let mitem = cx.meta_list(span, derive, vec![titem]); + return Some(cx.attribute(span, mitem)); + } + } + return None; +} + +pub fn get_legacy_derive(cx: &mut ExtCtxt, attrs: &mut Vec) + -> Option { + get_derive_attr(cx, attrs, is_legacy_derive).and_then(|a| { + let titem = derive_attr_trait(cx, &a); + if let Some(titem) = 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); + } 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); + return Some(cx.attribute(mitem.span, mitem)); + } + } + None + }) +} + +pub fn get_proc_macro_derive(cx: &mut ExtCtxt, attrs: &mut Vec) + -> Option { + get_derive_attr(cx, attrs, is_proc_macro_derive) +} + +pub fn get_builtin_derive(cx: &mut ExtCtxt, attrs: &mut Vec) + -> Option { + get_derive_attr(cx, attrs, is_builtin_derive) +} + +pub fn is_legacy_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { + !is_builtin_derive(cx, titem) && !is_proc_macro_derive(cx, titem) +} + +pub fn is_builtin_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { + let tname = titem.name().unwrap(); + let derive_mode = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(tname)); + cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| { + if let SyntaxExtension::BuiltinDerive(_) = *ext { true } else { false } + }).unwrap_or(false) +} + +pub fn is_proc_macro_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { + let tname = titem.name().unwrap(); + let derive_mode = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(tname)); + cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| { + if let SyntaxExtension::ProcMacroDerive(_) = *ext { true } else { false } + }).unwrap_or(false) +} + +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_structural_marker(cx: &mut ExtCtxt, attrs: &mut Vec) { + if attrs.iter().any(|a| a.name() == "structural_match") { + return; + } + + let (mut seen_peq, mut seen_eq) = (false, false); + + for i in 0..attrs.len() { + if !is_derive_attr(&attrs[i]) { + continue; + } + let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); + let span = attrs[i].span; + + let (partial_eq, eq) = (Symbol::intern("PartialEq"), Symbol::intern("Eq")); + if !seen_peq && traits.iter().any(|t| t.name() == Some(partial_eq)) { + seen_peq = true; + } + + if !seen_eq && traits.iter().any(|t| t.name() == Some(eq)) { + seen_eq = true; + } + + if seen_peq && seen_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)); + return; + } + } +} + +pub fn add_copy_clone_marker(cx: &mut ExtCtxt, attrs: &mut Vec) { + if attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") { + return; + } + + let (mut seen_clone, mut seen_copy) = (false, false); + + for i in 0..attrs.len() { + if !is_derive_attr(&attrs[i]) { + continue; + } + let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); + let span = attrs[i].span; + + let (copy, clone) = (Symbol::intern("Copy"), Symbol::intern("Clone")); + if !seen_clone && traits.iter().any(|t| t.name() == Some(clone)) { + seen_clone = true; + } + + if !seen_copy && traits.iter().any(|t| t.name() == Some(copy)) { + seen_copy = true; + } + + if seen_clone && seen_copy { + let marker = Symbol::intern("rustc_copy_clone_marker"); + let span = allow_unstable(cx, span, "derive(Copy, Clone)"); + let meta = cx.meta_word(span, marker); + attrs.push(cx.attribute(span, meta)); + return; + } + } +} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 01a8c215d47a..3570c0c8bf3d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,26 +8,29 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{Block, Ident, Mac_, PatKind}; +use ast::{self, Block, Ident, Mac_, PatKind}; use ast::{Name, MacStmtStyle, StmtKind, ItemKind}; -use ast; -use ext::hygiene::Mark; -use ext::placeholders::{placeholder, PlaceholderExpander}; use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; -use syntax_pos::{self, Span, ExpnId}; use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; +use ext::derive::{is_derive_attr, get_legacy_derive, get_proc_macro_derive, get_builtin_derive, + derive_attr_trait, add_structural_marker, add_copy_clone_marker, + verify_derive_attrs}; +use ext::hygiene::Mark; +use ext::placeholders::{placeholder, PlaceholderExpander}; use feature_gate::{self, Features}; use fold; use fold::*; -use parse::{ParseSess, DirectoryOwnership, PResult, filemap_to_tts}; use parse::parser::Parser; use parse::token; +use parse::{ParseSess, DirectoryOwnership, PResult, filemap_to_tts}; use print::pprust; use ptr::P; use std_inject; +use symbol::Symbol; use symbol::keywords; +use syntax_pos::{self, Span, ExpnId}; use tokenstream::{TokenTree, TokenStream}; use util::small_vector::SmallVector; use visit::Visitor; @@ -166,6 +169,10 @@ pub enum InvocationKind { attr: ast::Attribute, item: Annotatable, }, + Derive { + attr: ast::Attribute, + item: Annotatable, + }, } impl Invocation { @@ -173,6 +180,7 @@ impl Invocation { match self.kind { InvocationKind::Bang { span, .. } => span, InvocationKind::Attr { ref attr, .. } => attr.span, + InvocationKind::Derive { ref attr, .. } => attr.span, } } } @@ -250,6 +258,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let path = ast::Path::from_ident(attr.span, ident); self.cx.resolver.resolve_macro(scope, &path, force) } + InvocationKind::Derive { ref attr, .. } => { + let titem = derive_attr_trait(self.cx, &attr).unwrap(); + let tname = titem.name().unwrap(); + let path = ast::Path::from_ident(attr.span, Ident::with_empty_ctxt(tname)); + self.cx.resolver.resolve_macro(scope, &path, force) + } }; let ext = match resolution { Ok(ext) => Some(ext), @@ -330,6 +344,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { match invoc.kind { InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext), InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext), + InvocationKind::Derive { .. } => self.expand_derive_invoc(invoc, ext), } } @@ -486,6 +501,71 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }) } + /// Expand a derive invocation. Returns the result of expansion. + fn expand_derive_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { + let Invocation { expansion_kind: kind, .. } = invoc; + let (attr, item) = match invoc.kind { + InvocationKind::Derive { attr, item } => (attr, item), + _ => unreachable!(), + }; + + attr::mark_used(&attr); + let titem = derive_attr_trait(self.cx, &attr).unwrap(); + let tname = ast::Ident::with_empty_ctxt(titem.name().unwrap()); + let name = Symbol::intern(&format!("derive({})", tname)); + let mitem = &attr.value; + + self.cx.bt_push(ExpnInfo { + call_site: attr.span, + callee: NameAndSpan { + format: MacroAttribute(attr.name()), + span: Some(attr.span), + allow_internal_unstable: false, + } + }); + + match *ext { + SyntaxExtension::ProcMacroDerive(ref ext) => { + let span = Span { + expn_id: self.cx.codemap().record_expansion(ExpnInfo { + call_site: mitem.span, + callee: NameAndSpan { + format: MacroAttribute(Symbol::intern(&format!("derive({})", tname))), + span: None, + allow_internal_unstable: false, + }, + }), + ..mitem.span + }; + return kind.expect_from_annotatables(ext.expand(self.cx, span, &mitem, item)); + } + SyntaxExtension::BuiltinDerive(func) => { + let span = Span { + expn_id: self.cx.codemap().record_expansion(ExpnInfo { + call_site: titem.span, + callee: NameAndSpan { + format: MacroAttribute(name), + span: None, + allow_internal_unstable: true, + }, + }), + ..titem.span + }; + let mut items = Vec::new(); + func(self.cx, span, &mitem, &item, &mut |a| { + items.push(a) + }); + items.insert(0, item); + return kind.expect_from_annotatables(items); + } + _ => { + let msg = &format!("macro `{}` may not be used for derive attributes", name); + self.cx.span_err(attr.span, &msg); + kind.dummy(attr.span) + } + } + } + fn parse_expansion(&mut self, toks: TokenStream, kind: ExpansionKind, name: Name, span: Span) -> Expansion { let mut parser = self.cx.new_parser_from_tts(&toks.trees().cloned().collect::>()); @@ -595,16 +675,35 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) -> Expansion { - self.collect(kind, InvocationKind::Attr { attr: attr, item: item }) + let invoc_kind = if is_derive_attr(&attr) { + InvocationKind::Derive { attr: attr, item: item } + } else { + InvocationKind::Attr { attr: attr, item: item } + }; + + self.collect(kind, invoc_kind) } // If `item` is an attr invocation, remove and return the macro attribute. fn classify_item(&mut self, mut item: T) -> (T, Option) { let mut attr = None; + item = item.map_attrs(|mut attrs| { - attr = self.cx.resolver.find_attr_invoc(&mut attrs); + attr = self.cx.resolver.find_attr_invoc(&mut attrs) + .or_else(|| { + verify_derive_attrs(self.cx, &mut attrs); + get_legacy_derive(self.cx, &mut attrs) + }).or_else(|| { + get_proc_macro_derive(self.cx, &mut attrs) + }).or_else(|| { + add_structural_marker(self.cx, &mut attrs); + add_copy_clone_marker(self.cx, &mut attrs); + get_builtin_derive(self.cx, &mut attrs) + }); + attrs }); + (item, attr) } @@ -836,9 +935,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = - Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); - return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() + if !is_derive_attr(&attr) { + let item = + Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); + return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() + } else { + self.cx.span_err(attr.span, "`derive` can't be only be applied to items"); + } } match item.node { @@ -856,8 +959,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); - return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); + if !is_derive_attr(&attr) { + let item = + Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); + return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); + } else { + self.cx.span_err(attr.span, "`derive` can't be applied to impl items"); + } } match item.node { diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 871e6b3783a4..4a0deefbc4ac 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -129,6 +129,7 @@ pub mod print { pub mod ext { pub mod base; pub mod build; + pub mod derive; pub mod expand; pub mod placeholders; pub mod hygiene; diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs index 6359d642d157..498f2348b80f 100644 --- a/src/libsyntax_ext/deriving/decodable.rs +++ b/src/libsyntax_ext/deriving/decodable.rs @@ -13,6 +13,7 @@ use deriving; use deriving::generic::*; use deriving::generic::ty::*; +use deriving::warn_if_deprecated; use syntax::ast; use syntax::ast::{Expr, MetaItem, Mutability}; @@ -35,7 +36,7 @@ pub fn expand_deriving_decodable(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { - deriving::warn_if_deprecated(cx, span, "Decodable"); + warn_if_deprecated(cx, span, "Decodable"); expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize") } diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs index a276193e81b9..9d155c22ad03 100644 --- a/src/libsyntax_ext/deriving/encodable.rs +++ b/src/libsyntax_ext/deriving/encodable.rs @@ -91,6 +91,7 @@ use deriving; use deriving::generic::*; use deriving::generic::ty::*; +use deriving::warn_if_deprecated; use syntax::ast::{Expr, ExprKind, MetaItem, Mutability}; use syntax::ext::base::{Annotatable, ExtCtxt}; @@ -112,7 +113,7 @@ pub fn expand_deriving_encodable(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { - deriving::warn_if_deprecated(cx, span, "Encodable"); + warn_if_deprecated(cx, span, "Encodable"); expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize") } diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 311b8ae41f8b..3bceb02f3d6c 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -11,12 +11,10 @@ //! The compiler code necessary to implement the `#[derive]` extensions. use std::rc::Rc; -use syntax::ast::{self, MetaItem}; -use syntax::attr::HasAttrs; +use syntax::ast; use syntax::codemap; use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension, Resolver}; use syntax::ext::build::AstBuilder; -use syntax::feature_gate; use syntax::ptr::P; use syntax::symbol::Symbol; use syntax_pos::Span; @@ -90,241 +88,6 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { } } -pub fn expand_derive(cx: &mut ExtCtxt, - span: Span, - mitem: &MetaItem, - annotatable: Annotatable) - -> Vec { - debug!("expand_derive: span = {:?}", span); - debug!("expand_derive: mitem = {:?}", mitem); - debug!("expand_derive: annotatable input = {:?}", annotatable); - let mut item = match annotatable { - Annotatable::Item(item) => item, - other => { - cx.span_err(span, "`derive` can only be applied to items"); - return vec![other] - } - }; - - let derive = Symbol::intern("derive"); - let mut derive_attrs = Vec::new(); - item = item.map_attrs(|attrs| { - let partition = attrs.into_iter().partition(|attr| attr.name() == derive); - derive_attrs = partition.0; - partition.1 - }); - - // Expand `#[derive]`s after other attribute macro invocations. - if cx.resolver.find_attr_invoc(&mut item.attrs.clone()).is_some() { - return vec![Annotatable::Item(item.map_attrs(|mut attrs| { - attrs.push(cx.attribute(span, mitem.clone())); - attrs.extend(derive_attrs); - attrs - }))]; - } - - let get_traits = |mitem: &MetaItem, cx: &ExtCtxt| { - if mitem.value_str().is_some() { - cx.span_err(mitem.span, "unexpected value in `derive`"); - } - - let traits = mitem.meta_item_list().unwrap_or(&[]).to_owned(); - if traits.is_empty() { - cx.span_warn(mitem.span, "empty trait list in `derive`"); - } - traits - }; - - let mut traits = get_traits(mitem, cx); - for derive_attr in derive_attrs { - traits.extend(get_traits(&derive_attr.value, cx)); - } - - // First, weed out malformed #[derive] - traits.retain(|titem| { - if titem.word().is_none() { - cx.span_err(titem.span, "malformed `derive` entry"); - false - } else { - true - } - }); - - // Next, check for old-style #[derive(Foo)] - // - // These all get expanded to `#[derive_Foo]` and will get expanded first. If - // we actually add any attributes here then we return to get those expanded - // and then eventually we'll come back to finish off the other derive modes. - let mut new_attributes = Vec::new(); - traits.retain(|titem| { - let tword = titem.word().unwrap(); - let tname = tword.name(); - - if is_builtin_trait(tname) || { - let derive_mode = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(tname)); - cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| { - if let SyntaxExtension::ProcMacroDerive(_) = *ext { true } else { false } - }).unwrap_or(false) - } { - return true; - } - - 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); - } 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); - new_attributes.push(cx.attribute(mitem.span, mitem)); - } - false - }); - if new_attributes.len() > 0 { - item = item.map(|mut i| { - i.attrs.extend(new_attributes); - if traits.len() > 0 { - let list = cx.meta_list(mitem.span, derive, traits); - i.attrs.push(cx.attribute(mitem.span, list)); - } - i - }); - return vec![Annotatable::Item(item)] - } - - // Now check for macros-1.1 style custom #[derive]. - // - // Expand each of them in order given, but *before* we expand any built-in - // derive modes. The logic here is to: - // - // 1. Collect the remaining `#[derive]` annotations into a list. If - // there are any left, attach a `#[derive]` attribute to the item - // that we're currently expanding with the remaining derive modes. - // 2. Manufacture a `#[derive(Foo)]` attribute to pass to the expander. - // 3. Expand the current item we're expanding, getting back a list of - // items that replace it. - // 4. Extend the returned list with the current list of items we've - // collected so far. - // 5. Return everything! - // - // If custom derive extensions end up threading through the `#[derive]` - // attribute, we'll get called again later on to continue expanding - // those modes. - let macros_11_derive = traits.iter() - .cloned() - .enumerate() - .filter(|&(_, ref name)| !is_builtin_trait(name.name().unwrap())) - .next(); - if let Some((i, titem)) = macros_11_derive { - let tname = ast::Ident::with_empty_ctxt(titem.name().unwrap()); - let path = ast::Path::from_ident(titem.span, tname); - let ext = cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).unwrap(); - - traits.remove(i); - if traits.len() > 0 { - item = item.map(|mut i| { - let list = cx.meta_list(mitem.span, derive, traits); - i.attrs.push(cx.attribute(mitem.span, list)); - i - }); - } - let titem = cx.meta_list_item_word(titem.span, titem.name().unwrap()); - let mitem = cx.meta_list(titem.span, derive, vec![titem]); - let item = Annotatable::Item(item); - - let span = Span { - expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { - call_site: mitem.span, - callee: codemap::NameAndSpan { - format: codemap::MacroAttribute(Symbol::intern(&format!("derive({})", tname))), - span: None, - allow_internal_unstable: false, - }, - }), - ..mitem.span - }; - - if let SyntaxExtension::ProcMacroDerive(ref ext) = *ext { - return ext.expand(cx, span, &mitem, item); - } else { - unreachable!() - } - } - - // Ok, at this point we know that there are no old-style `#[derive_Foo]` nor - // any macros-1.1 style `#[derive(Foo)]`. Expand all built-in traits here. - - // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted) - // `#[structural_match]` attribute. - let (partial_eq, eq) = (Symbol::intern("PartialEq"), Symbol::intern("Eq")); - if traits.iter().any(|t| t.name() == Some(partial_eq)) && - traits.iter().any(|t| t.name() == Some(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); - item = item.map(|mut i| { - i.attrs.push(cx.attribute(span, meta)); - i - }); - } - - // RFC #1521. `Clone` can assume that `Copy` types' clone implementation is - // the same as the copy implementation. - // - // Add a marker attribute here picked up during #[derive(Clone)] - let (copy, clone) = (Symbol::intern("Copy"), Symbol::intern("Clone")); - if traits.iter().any(|t| t.name() == Some(clone)) && - traits.iter().any(|t| t.name() == Some(copy)) { - let marker = Symbol::intern("rustc_copy_clone_marker"); - let span = allow_unstable(cx, span, "derive(Copy, Clone)"); - let meta = cx.meta_word(span, marker); - item = item.map(|mut i| { - i.attrs.push(cx.attribute(span, meta)); - i - }); - } - - let mut items = Vec::new(); - for titem in traits.iter() { - let tname = titem.word().unwrap().name(); - let name = Symbol::intern(&format!("derive({})", tname)); - let tname_cx = ast::Ident::with_empty_ctxt(titem.name().unwrap()); - let mitem = cx.meta_word(titem.span, name); - let path = ast::Path::from_ident(titem.span, tname_cx); - let ext = cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).unwrap(); - - let span = Span { - expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { - call_site: titem.span, - callee: codemap::NameAndSpan { - format: codemap::MacroAttribute(name), - span: None, - allow_internal_unstable: true, - }, - }), - ..titem.span - }; - - if let SyntaxExtension::BuiltinDerive(ref func) = *ext { - let my_item = Annotatable::Item(item); - func(cx, span, &mitem, &my_item, &mut |a| { - items.push(a) - }); - item = my_item.expect_item(); - } else { - unreachable!(); - } - } - - items.insert(0, Annotatable::Item(item)); - return items -} - macro_rules! derive_traits { ($( $name:expr => $func:path, )+) => { pub fn is_builtin_trait(name: ast::Name) -> bool { diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index e872cfaeacb7..7533171b0855 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -24,7 +24,6 @@ #![feature(staged_api)] extern crate fmt_macros; -#[macro_use] extern crate log; #[macro_use] extern crate syntax; @@ -51,7 +50,7 @@ pub mod proc_macro_impl; use std::rc::Rc; use syntax::ast; -use syntax::ext::base::{MacroExpanderFn, NormalTT, MultiModifier, NamedSyntaxExtension}; +use syntax::ext::base::{MacroExpanderFn, NormalTT, NamedSyntaxExtension}; use syntax::symbol::Symbol; pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, @@ -114,8 +113,6 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, register(Symbol::intern("format_args"), NormalTT(Box::new(format::expand_format_args), None, true)); - register(Symbol::intern("derive"), MultiModifier(Box::new(deriving::expand_derive))); - for (name, ext) in user_exts { register(name, ext); } From 876482834ae69f3c88fcdbcfc3793c54f996501b Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Mon, 30 Jan 2017 08:06:21 +1030 Subject: [PATCH 04/18] Update proc-macro derive error messages --- src/libsyntax_ext/deriving/custom.rs | 8 ++++---- src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs | 2 +- src/test/compile-fail-fulldeps/proc-macro/load-panic.rs | 2 +- src/test/ui/custom-derive/issue-36935.stderr | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index e118ef1ea01f..974e86a57412 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -54,7 +54,7 @@ impl MultiItemModifier for ProcMacroDerive { Annotatable::Item(item) => item, Annotatable::ImplItem(_) | Annotatable::TraitItem(_) => { - ecx.span_err(span, "proc_macro_derive attributes may only be \ + ecx.span_err(span, "proc-macro derives may only be \ applied to struct/enum items"); return Vec::new() } @@ -63,7 +63,7 @@ impl MultiItemModifier for ProcMacroDerive { ItemKind::Struct(..) | ItemKind::Enum(..) => {}, _ => { - ecx.span_err(span, "proc_macro_derive attributes may only be \ + ecx.span_err(span, "proc-macro derives may only be \ applied to struct/enum items"); return Vec::new() } @@ -81,7 +81,7 @@ impl MultiItemModifier for ProcMacroDerive { let stream = match res { Ok(stream) => stream, Err(e) => { - let msg = "proc_macro_derive attribute panicked"; + let msg = "proc-macro derive panicked"; let mut err = ecx.struct_span_fatal(span, msg); if let Some(s) = e.downcast_ref::() { err.help(&format!("message: {}", s)); @@ -100,7 +100,7 @@ impl MultiItemModifier for ProcMacroDerive { Ok(new_items) => new_items, Err(_) => { // FIXME: handle this better - let msg = "proc_macro_derive produced unparseable tokens"; + let msg = "proc-macro derive produced unparseable tokens"; ecx.struct_span_fatal(span, msg).emit(); panic!(FatalError); } diff --git a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs index bc4da9fee47e..42fad803bfa6 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/derive-bad.rs @@ -16,7 +16,7 @@ extern crate derive_bad; #[derive( A )] -//~^^ ERROR: proc_macro_derive produced unparseable tokens +//~^^ ERROR: proc-macro derive produced unparseable tokens struct A; fn main() {} diff --git a/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs b/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs index 107273d012dd..c483c048b418 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/load-panic.rs @@ -14,7 +14,7 @@ extern crate derive_panic; #[derive(A)] -//~^ ERROR: proc_macro_derive attribute panicked +//~^ ERROR: proc-macro derive panicked //~| HELP: message: nope! struct Foo; diff --git a/src/test/ui/custom-derive/issue-36935.stderr b/src/test/ui/custom-derive/issue-36935.stderr index ad1382cbc8e4..9a5e2de14e3b 100644 --- a/src/test/ui/custom-derive/issue-36935.stderr +++ b/src/test/ui/custom-derive/issue-36935.stderr @@ -1,4 +1,4 @@ -error: proc_macro_derive attribute panicked +error: proc-macro derive panicked --> $DIR/issue-36935.rs:17:15 | 17 | #[derive(Foo, Bar)] From f4391792e19749219a07acfbd77e2cb52ea047ec Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Mon, 30 Jan 2017 08:06:36 +1030 Subject: [PATCH 05/18] Fix various expand_derive issues --- src/libsyntax/ext/derive.rs | 60 ++++++++++++++++++++++--------------- src/libsyntax/ext/expand.rs | 8 ++--- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 919ff496a9d6..930964c350a0 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -16,14 +16,9 @@ use symbol::Symbol; use syntax_pos::Span; use codemap; -pub fn is_derive_attr(attr: &ast::Attribute) -> bool { - let res = attr.name() == Symbol::intern("derive"); - res -} - pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) -> Option<&'a NestedMetaItem> { - if !is_derive_attr(attr) { + if attr.name() != "derive" { return None; } if attr.value_str().is_some() { @@ -42,21 +37,19 @@ pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) } pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &mut Vec) { - for i in 0..attrs.len() { - if !is_derive_attr(&attrs[i]) { + for attr in attrs { + if attr.name() != "derive" { continue; } - let span = attrs[i].span; - - if attrs[i].value_str().is_some() { - cx.span_err(span, "unexpected value in `derive`"); + if attr.value_str().is_some() { + cx.span_err(attr.span, "unexpected value in `derive`"); } - let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); + let traits = attr.meta_item_list().unwrap_or(&[]).to_owned(); if traits.is_empty() { - cx.span_warn(span, "empty trait list in `derive`"); + cx.span_warn(attr.span, "empty trait list in `derive`"); continue; } for titem in traits { @@ -67,11 +60,26 @@ pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &mut Vec) { } } +pub enum DeriveType { + Legacy, + ProcMacro, + Builtin +} + +impl DeriveType { + pub fn is_derive_type(&self, cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { + match *self { + DeriveType::Legacy => is_legacy_derive(cx, titem), + DeriveType::ProcMacro => is_proc_macro_derive(cx, titem), + DeriveType::Builtin => is_builtin_derive(cx, titem), + } + } +} + pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, - is_derive_type: fn(&mut ExtCtxt, &NestedMetaItem) -> bool) - -> Option { + derive_type: DeriveType) -> Option { for i in 0..attrs.len() { - if !is_derive_attr(&attrs[i]) { + if attrs[i].name() != "derive" { continue; } @@ -90,7 +98,7 @@ pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, // See if we can find a matching trait. for j in 0..traits.len() { - if is_derive_type(cx, &traits[j]) { + if derive_type.is_derive_type(cx, &traits[j]) { titem = Some(traits.remove(j)); break; } @@ -115,7 +123,7 @@ pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, pub fn get_legacy_derive(cx: &mut ExtCtxt, attrs: &mut Vec) -> Option { - get_derive_attr(cx, attrs, is_legacy_derive).and_then(|a| { + get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| { let titem = derive_attr_trait(cx, &a); if let Some(titem) = titem { let tword = titem.word().unwrap(); @@ -141,16 +149,20 @@ pub fn get_legacy_derive(cx: &mut ExtCtxt, attrs: &mut Vec) pub fn get_proc_macro_derive(cx: &mut ExtCtxt, attrs: &mut Vec) -> Option { - get_derive_attr(cx, attrs, is_proc_macro_derive) + get_derive_attr(cx, attrs, DeriveType::ProcMacro) } pub fn get_builtin_derive(cx: &mut ExtCtxt, attrs: &mut Vec) -> Option { - get_derive_attr(cx, attrs, is_builtin_derive) + get_derive_attr(cx, attrs, DeriveType::Builtin) } pub fn is_legacy_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { - !is_builtin_derive(cx, titem) && !is_proc_macro_derive(cx, titem) + let tname = titem.name().unwrap(); + let name = Symbol::intern(&format!("derive_{}", tname)); + let path = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(name)); + !is_proc_macro_derive(cx, titem) && !is_builtin_derive(cx, titem) && + cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).is_ok() } pub fn is_builtin_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { @@ -191,7 +203,7 @@ pub fn add_structural_marker(cx: &mut ExtCtxt, attrs: &mut Vec) let (mut seen_peq, mut seen_eq) = (false, false); for i in 0..attrs.len() { - if !is_derive_attr(&attrs[i]) { + if attrs[i].name() != "derive" { continue; } let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); @@ -224,7 +236,7 @@ pub fn add_copy_clone_marker(cx: &mut ExtCtxt, attrs: &mut Vec) let (mut seen_clone, mut seen_copy) = (false, false); for i in 0..attrs.len() { - if !is_derive_attr(&attrs[i]) { + if attrs[i].name() != "derive" { continue; } let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3570c0c8bf3d..493b43e7bf07 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -14,7 +14,7 @@ use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; -use ext::derive::{is_derive_attr, get_legacy_derive, get_proc_macro_derive, get_builtin_derive, +use ext::derive::{get_legacy_derive, get_proc_macro_derive, get_builtin_derive, derive_attr_trait, add_structural_marker, add_copy_clone_marker, verify_derive_attrs}; use ext::hygiene::Mark; @@ -675,7 +675,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) -> Expansion { - let invoc_kind = if is_derive_attr(&attr) { + let invoc_kind = if attr.name() == "derive" { InvocationKind::Derive { attr: attr, item: item } } else { InvocationKind::Attr { attr: attr, item: item } @@ -935,7 +935,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - if !is_derive_attr(&attr) { + if attr.name() != "derive" { let item = Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() @@ -959,7 +959,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - if !is_derive_attr(&attr) { + if attr.name() != "derive" { let item = Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); From e70d4ef1adc5767beb3fe9f41a9349a20c8d6b67 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Mon, 30 Jan 2017 22:31:47 +1030 Subject: [PATCH 06/18] Use trait spans instead of attribute spans --- src/libsyntax/ext/derive.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 930964c350a0..22ea6adba629 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -83,8 +83,6 @@ pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, continue; } - let span = attrs[i].span; - if attrs[i].value_str().is_some() { continue; } @@ -110,12 +108,12 @@ pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, attrs.remove(i); } else { let derive = Symbol::intern("derive"); - let mitem = cx.meta_list(span, derive, traits); - attrs[i] = cx.attribute(span, mitem); + 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(span, derive, vec![titem]); - return Some(cx.attribute(span, mitem)); + let mitem = cx.meta_list(titem.span, derive, vec![titem]); + return Some(cx.attribute(mitem.span, mitem)); } } return None; From dafc42dc123be1e54c0ba769fb6fcd021ca5f028 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Mon, 30 Jan 2017 22:32:20 +1030 Subject: [PATCH 07/18] Move derive item check to collect_attr --- src/libsyntax/ext/expand.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 493b43e7bf07..8421779c5702 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -676,6 +676,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) -> Expansion { let invoc_kind = if attr.name() == "derive" { + if kind == ExpansionKind::TraitItems || kind == ExpansionKind::ImplItems { + self.cx.span_err(attr.span, "`derive` can be only be applied to items"); + return kind.expect_from_annotatables(::std::iter::once(item)); + } InvocationKind::Derive { attr: attr, item: item } } else { InvocationKind::Attr { attr: attr, item: item } @@ -935,13 +939,9 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - if attr.name() != "derive" { - let item = - Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); - return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() - } else { - self.cx.span_err(attr.span, "`derive` can't be only be applied to items"); - } + let item = + Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); + return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() } match item.node { @@ -959,13 +959,9 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - if attr.name() != "derive" { - let item = - Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); - return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); - } else { - self.cx.span_err(attr.span, "`derive` can't be applied to impl items"); - } + let item = + Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); + return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); } match item.node { From 2dfa9443da8051cd509a515311cfcf0cc8c79285 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Mon, 30 Jan 2017 22:32:36 +1030 Subject: [PATCH 08/18] Combine derive markers into one function --- src/libsyntax/ext/derive.rs | 82 +++++++++++-------------------------- src/libsyntax/ext/expand.rs | 6 +-- 2 files changed, 26 insertions(+), 62 deletions(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 22ea6adba629..81cfadd0f5d5 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -193,68 +193,34 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { } } -pub fn add_structural_marker(cx: &mut ExtCtxt, attrs: &mut Vec) { - if attrs.iter().any(|a| a.name() == "structural_match") { +pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec) { + if attrs.is_empty() { return; } - let (mut seen_peq, mut seen_eq) = (false, false); - - for i in 0..attrs.len() { - if attrs[i].name() != "derive" { - continue; - } - let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); - let span = attrs[i].span; - - let (partial_eq, eq) = (Symbol::intern("PartialEq"), Symbol::intern("Eq")); - if !seen_peq && traits.iter().any(|t| t.name() == Some(partial_eq)) { - seen_peq = true; - } - - if !seen_eq && traits.iter().any(|t| t.name() == Some(eq)) { - seen_eq = true; - } - - if seen_peq && seen_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)); - return; - } - } -} - -pub fn add_copy_clone_marker(cx: &mut ExtCtxt, attrs: &mut Vec) { - if attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") { - return; + let titems = attrs.iter().filter(|a| { + a.name() == "derive" + }).flat_map(|a| { + a.meta_item_list().unwrap().iter() + }).filter_map(|titem| { + titem.name() + }).collect::>(); + + 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)); } - let (mut seen_clone, mut seen_copy) = (false, false); - - for i in 0..attrs.len() { - if attrs[i].name() != "derive" { - continue; - } - let traits = attrs[i].meta_item_list().unwrap_or(&[]).to_owned(); - let span = attrs[i].span; - - let (copy, clone) = (Symbol::intern("Copy"), Symbol::intern("Clone")); - if !seen_clone && traits.iter().any(|t| t.name() == Some(clone)) { - seen_clone = true; - } - - if !seen_copy && traits.iter().any(|t| t.name() == Some(copy)) { - seen_copy = true; - } - - if seen_clone && seen_copy { - let marker = Symbol::intern("rustc_copy_clone_marker"); - let span = allow_unstable(cx, span, "derive(Copy, Clone)"); - let meta = cx.meta_word(span, marker); - attrs.push(cx.attribute(span, meta)); - return; - } + 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("structural_match"); + let span = allow_unstable(cx, span, "derive(Copy, Clone)"); + let meta = cx.meta_word(span, structural_match); + attrs.push(cx.attribute(span, meta)); } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8421779c5702..0d4af1888dc8 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -15,8 +15,7 @@ use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; use ext::derive::{get_legacy_derive, get_proc_macro_derive, get_builtin_derive, - derive_attr_trait, add_structural_marker, add_copy_clone_marker, - verify_derive_attrs}; + derive_attr_trait, add_derived_markers, verify_derive_attrs}; use ext::hygiene::Mark; use ext::placeholders::{placeholder, PlaceholderExpander}; use feature_gate::{self, Features}; @@ -700,8 +699,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { }).or_else(|| { get_proc_macro_derive(self.cx, &mut attrs) }).or_else(|| { - add_structural_marker(self.cx, &mut attrs); - add_copy_clone_marker(self.cx, &mut attrs); + add_derived_markers(self.cx, &mut attrs); get_builtin_derive(self.cx, &mut attrs) }); From dae6850b3c6151a9a37e3f6f54279f316e1db56b Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Tue, 31 Jan 2017 19:50:37 +1030 Subject: [PATCH 09/18] Let get_proc_macro_derive collect unknown derives This means that proc_macro_derives that haven't been imported yet don't get ignored. This is safe because legacy_derives and builtin derives are instantly resolvable. --- src/libsyntax/ext/derive.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 81cfadd0f5d5..e9f4350838ac 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -70,7 +70,7 @@ impl DeriveType { pub fn is_derive_type(&self, cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { match *self { DeriveType::Legacy => is_legacy_derive(cx, titem), - DeriveType::ProcMacro => is_proc_macro_derive(cx, titem), + DeriveType::ProcMacro => !is_builtin_derive(cx, titem), DeriveType::Builtin => is_builtin_derive(cx, titem), } } @@ -145,21 +145,20 @@ pub fn get_legacy_derive(cx: &mut ExtCtxt, attrs: &mut Vec) }) } -pub fn get_proc_macro_derive(cx: &mut ExtCtxt, attrs: &mut Vec) - -> Option { - get_derive_attr(cx, attrs, DeriveType::ProcMacro) -} - pub fn get_builtin_derive(cx: &mut ExtCtxt, attrs: &mut Vec) -> Option { get_derive_attr(cx, attrs, DeriveType::Builtin) } +pub fn get_proc_macro_derive(cx: &mut ExtCtxt, attrs: &mut Vec) + -> Option { + get_derive_attr(cx, attrs, DeriveType::ProcMacro) +} + pub fn is_legacy_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { let tname = titem.name().unwrap(); let name = Symbol::intern(&format!("derive_{}", tname)); let path = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(name)); - !is_proc_macro_derive(cx, titem) && !is_builtin_derive(cx, titem) && cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).is_ok() } From dd05fc0b9a8c32270b0d2bba59da58c8f82c7224 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Tue, 31 Jan 2017 20:32:02 +1030 Subject: [PATCH 10/18] Fix meta_item_list in add_derived_markers --- src/libsyntax/ext/derive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index e9f4350838ac..b6ef0fde4426 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -200,7 +200,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec) { let titems = attrs.iter().filter(|a| { a.name() == "derive" }).flat_map(|a| { - a.meta_item_list().unwrap().iter() + a.value.meta_item_list().unwrap_or(&[]).iter() }).filter_map(|titem| { titem.name() }).collect::>(); From 2dd9dc206eb2041b2ca70c1e7f338cceda548513 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Tue, 31 Jan 2017 20:45:31 +1030 Subject: [PATCH 11/18] Split derives in test because one triggers before the other --- src/test/compile-fail/macros-nonfatal-errors.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/compile-fail/macros-nonfatal-errors.rs b/src/test/compile-fail/macros-nonfatal-errors.rs index 723e936212a5..f96a2b206470 100644 --- a/src/test/compile-fail/macros-nonfatal-errors.rs +++ b/src/test/compile-fail/macros-nonfatal-errors.rs @@ -14,9 +14,11 @@ #![feature(asm)] #![feature(trace_macros, concat_idents)] -#[derive(Default, //~ ERROR - Zero)] //~ ERROR -enum CantDeriveThose {} +#[derive(Zero)] //~ ERROR +struct CantDeriveThis; + +#[derive(Default)] +enum OrDeriveThis {} fn main() { doesnt_exist!(); //~ ERROR From a914a239676f783228cb0caa2e4cea021c9528fa Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Tue, 31 Jan 2017 23:31:00 +1030 Subject: [PATCH 12/18] macro resolving refactor, get_derive_attr code refactor, and nits --- src/librustc_resolve/macros.rs | 8 +++ src/libsyntax/ext/base.rs | 4 ++ src/libsyntax/ext/derive.rs | 116 +++++++++++---------------------- src/libsyntax/ext/expand.rs | 45 +++++++++---- 4 files changed, 81 insertions(+), 92 deletions(-) diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 682b3ff834fa..038914a9bf4e 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -250,6 +250,14 @@ impl<'a> base::Resolver for Resolver<'a> { } result } + + fn resolve_builtin_macro(&mut self, tname: Name) -> Result, Determinacy> { + match self.builtin_macros.get(&tname).cloned() { + Some(binding) => Ok(binding.get_macro(self)), + None => Err(Determinacy::Undetermined), + } + } + } impl<'a> Resolver<'a> { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 17b0b97468df..a7720e2aad8b 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -536,6 +536,7 @@ pub trait Resolver { fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option; fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) -> Result, Determinacy>; + fn resolve_builtin_macro(&mut self, tname: Name) -> Result, Determinacy>; } #[derive(Copy, Clone, Debug)] @@ -562,6 +563,9 @@ impl Resolver for DummyResolver { -> Result, Determinacy> { Err(Determinacy::Determined) } + fn resolve_builtin_macro(&mut self, _tname: Name) -> Result, Determinacy> { + Err(Determinacy::Determined) + } } #[derive(Clone)] diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index b6ef0fde4426..ea3eb766abb2 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -11,7 +11,7 @@ use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension}; use ext::build::AstBuilder; -use feature_gate; +use ast::Name; use symbol::Symbol; use syntax_pos::Span; use codemap; @@ -26,7 +26,7 @@ pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) return None; } - let traits = attr.meta_item_list().unwrap(); + let traits = attr.meta_item_list().unwrap_or(&[]); if traits.is_empty() { cx.span_warn(attr.span, "empty trait list in `derive`"); @@ -36,7 +36,7 @@ pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) return traits.get(0); } -pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &mut Vec) { +pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) { for attr in attrs { if attr.name() != "derive" { continue; @@ -60,6 +60,7 @@ pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &mut Vec) { } } +#[derive(PartialEq, Debug, Clone, Copy)] pub enum DeriveType { Legacy, ProcMacro, @@ -67,11 +68,21 @@ pub enum DeriveType { } impl DeriveType { - pub fn is_derive_type(&self, cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { - match *self { - DeriveType::Legacy => is_legacy_derive(cx, titem), - DeriveType::ProcMacro => !is_builtin_derive(cx, titem), - DeriveType::Builtin => is_builtin_derive(cx, titem), + // Classify a derive trait name by resolving the macro. + pub fn classify(cx: &mut ExtCtxt, tname: Name) -> Option { + let legacy_derive_name = Symbol::intern(&format!("derive_{}", tname)); + + if let Ok(_) = cx.resolver.resolve_builtin_macro(legacy_derive_name) { + return Some(DeriveType::Legacy); + } + + match cx.resolver.resolve_builtin_macro(tname) { + Ok(ext) => match *ext { + SyntaxExtension::BuiltinDerive(..) => Some(DeriveType::Builtin), + SyntaxExtension::ProcMacroDerive(..) => Some(DeriveType::ProcMacro), + _ => None, + }, + Err(_) => Some(DeriveType::ProcMacro), } } } @@ -96,7 +107,13 @@ pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, // See if we can find a matching trait. for j in 0..traits.len() { - if derive_type.is_derive_type(cx, &traits[j]) { + let tname = if let Some(tname) = traits[j].name() { + tname + } else { + continue; + }; + + if DeriveType::classify(cx, tname) == Some(derive_type) { titem = Some(traits.remove(j)); break; } @@ -119,65 +136,6 @@ pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, return None; } -pub fn get_legacy_derive(cx: &mut ExtCtxt, attrs: &mut Vec) - -> Option { - get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| { - let titem = derive_attr_trait(cx, &a); - if let Some(titem) = 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); - } 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); - return Some(cx.attribute(mitem.span, mitem)); - } - } - None - }) -} - -pub fn get_builtin_derive(cx: &mut ExtCtxt, attrs: &mut Vec) - -> Option { - get_derive_attr(cx, attrs, DeriveType::Builtin) -} - -pub fn get_proc_macro_derive(cx: &mut ExtCtxt, attrs: &mut Vec) - -> Option { - get_derive_attr(cx, attrs, DeriveType::ProcMacro) -} - -pub fn is_legacy_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { - let tname = titem.name().unwrap(); - let name = Symbol::intern(&format!("derive_{}", tname)); - let path = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(name)); - cx.resolver.resolve_macro(cx.current_expansion.mark, &path, false).is_ok() -} - -pub fn is_builtin_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { - let tname = titem.name().unwrap(); - let derive_mode = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(tname)); - cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| { - if let SyntaxExtension::BuiltinDerive(_) = *ext { true } else { false } - }).unwrap_or(false) -} - -pub fn is_proc_macro_derive(cx: &mut ExtCtxt, titem: &NestedMetaItem) -> bool { - let tname = titem.name().unwrap(); - let derive_mode = ast::Path::from_ident(titem.span, ast::Ident::with_empty_ctxt(tname)); - cx.resolver.resolve_macro(cx.current_expansion.mark, &derive_mode, false).map(|ext| { - if let SyntaxExtension::ProcMacroDerive(_) = *ext { true } else { false } - }).unwrap_or(false) -} - fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { Span { expn_id: cx.codemap().record_expansion(codemap::ExpnInfo { @@ -200,7 +158,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec) { let titems = attrs.iter().filter(|a| { a.name() == "derive" }).flat_map(|a| { - a.value.meta_item_list().unwrap_or(&[]).iter() + a.meta_item_list().unwrap_or(&[]).iter() }).filter_map(|titem| { titem.name() }).collect::>(); @@ -208,18 +166,18 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut 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)); + 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("structural_match"); - let span = allow_unstable(cx, span, "derive(Copy, Clone)"); - let meta = cx.meta_word(span, structural_match); - attrs.push(cx.attribute(span, meta)); + titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") { + let structural_match = Symbol::intern("structural_match"); + let span = allow_unstable(cx, span, "derive(Copy, Clone)"); + let meta = cx.meta_word(span, structural_match); + attrs.push(cx.attribute(span, meta)); } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0d4af1888dc8..6ae0f22a3cf5 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -14,8 +14,9 @@ use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; -use ext::derive::{get_legacy_derive, get_proc_macro_derive, get_builtin_derive, - derive_attr_trait, add_derived_markers, verify_derive_attrs}; +use ext::build::AstBuilder; +use ext::derive::{get_derive_attr, derive_attr_trait, add_derived_markers, verify_derive_attrs, + DeriveType}; use ext::hygiene::Mark; use ext::placeholders::{placeholder, PlaceholderExpander}; use feature_gate::{self, Features}; @@ -260,8 +261,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { InvocationKind::Derive { ref attr, .. } => { let titem = derive_attr_trait(self.cx, &attr).unwrap(); let tname = titem.name().unwrap(); - let path = ast::Path::from_ident(attr.span, Ident::with_empty_ctxt(tname)); - self.cx.resolver.resolve_macro(scope, &path, force) + self.cx.resolver.resolve_builtin_macro(tname) } }; let ext = match resolution { @@ -694,13 +694,34 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { item = item.map_attrs(|mut attrs| { attr = self.cx.resolver.find_attr_invoc(&mut attrs) .or_else(|| { - verify_derive_attrs(self.cx, &mut attrs); - get_legacy_derive(self.cx, &mut attrs) - }).or_else(|| { - get_proc_macro_derive(self.cx, &mut attrs) + verify_derive_attrs(self.cx, &attrs); + get_derive_attr(self.cx, &mut attrs, DeriveType::Legacy).and_then(|a| { + let titem = derive_attr_trait(self.cx, &a); + if let Some(titem) = titem { + let tword = titem.word().unwrap(); + let tname = tword.name(); + if !self.cx.ecfg.enable_custom_derive() { + feature_gate::emit_feature_err(&self.cx.parse_sess, + "custom_derive", + titem.span, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_CUSTOM_DERIVE); + } else { + let name = Symbol::intern(&format!("derive_{}", tname)); + if !self.cx.resolver.is_whitelisted_legacy_custom_derive(name) { + self.cx.span_warn(titem.span, feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE); + } + let mitem = self.cx.meta_word(titem.span, name); + return Some(self.cx.attribute(mitem.span, mitem)); + } + } + None + }) }).or_else(|| { add_derived_markers(self.cx, &mut attrs); - get_builtin_derive(self.cx, &mut attrs) + get_derive_attr(self.cx, &mut attrs, DeriveType::Builtin) + }).or_else(|| { + get_derive_attr(self.cx, &mut attrs, DeriveType::ProcMacro) }); attrs @@ -937,8 +958,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = - Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); + let item = Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() } @@ -957,8 +977,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = - Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); + let item = Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); } From 6c22abe62ee9e1a065e6a890b1034a1574ad4e20 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Wed, 1 Feb 2017 00:16:00 +1030 Subject: [PATCH 13/18] Move files around + line ending fixes --- src/libsyntax/ext/derive.rs | 36 ++++++++++++++++++++++++++++++++ src/libsyntax/ext/expand.rs | 41 ++++++------------------------------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index ea3eb766abb2..57c63b83a77b 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -12,6 +12,7 @@ use ast::{self, NestedMetaItem}; use ext::base::{ExtCtxt, SyntaxExtension}; use ext::build::AstBuilder; use ast::Name; +use feature_gate; use symbol::Symbol; use syntax_pos::Span; use codemap; @@ -181,3 +182,38 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec) { attrs.push(cx.attribute(span, meta)); } } + +pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec) + -> Option { + verify_derive_attrs(cx, attrs); + get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| { + let titem = derive_attr_trait(cx, &a); + if let Some(titem) = 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 + ); + } 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); + return Some(cx.attribute(mitem.span, mitem)); + } + } + None + }).or_else(|| { + add_derived_markers(cx, attrs); + get_derive_attr(cx, attrs, DeriveType::Builtin) + }).or_else(|| { + get_derive_attr(cx, attrs, DeriveType::ProcMacro) + }) +} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6ae0f22a3cf5..19c57b4cc91a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -14,9 +14,7 @@ use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use config::{is_test_or_bench, StripUnconfigured}; use ext::base::*; -use ext::build::AstBuilder; -use ext::derive::{get_derive_attr, derive_attr_trait, add_derived_markers, verify_derive_attrs, - DeriveType}; +use ext::derive::{find_derive_attr, derive_attr_trait}; use ext::hygiene::Mark; use ext::placeholders::{placeholder, PlaceholderExpander}; use feature_gate::{self, Features}; @@ -692,37 +690,9 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let mut attr = None; item = item.map_attrs(|mut attrs| { - attr = self.cx.resolver.find_attr_invoc(&mut attrs) - .or_else(|| { - verify_derive_attrs(self.cx, &attrs); - get_derive_attr(self.cx, &mut attrs, DeriveType::Legacy).and_then(|a| { - let titem = derive_attr_trait(self.cx, &a); - if let Some(titem) = titem { - let tword = titem.word().unwrap(); - let tname = tword.name(); - if !self.cx.ecfg.enable_custom_derive() { - feature_gate::emit_feature_err(&self.cx.parse_sess, - "custom_derive", - titem.span, - feature_gate::GateIssue::Language, - feature_gate::EXPLAIN_CUSTOM_DERIVE); - } else { - let name = Symbol::intern(&format!("derive_{}", tname)); - if !self.cx.resolver.is_whitelisted_legacy_custom_derive(name) { - self.cx.span_warn(titem.span, feature_gate::EXPLAIN_DEPR_CUSTOM_DERIVE); - } - let mitem = self.cx.meta_word(titem.span, name); - return Some(self.cx.attribute(mitem.span, mitem)); - } - } - None - }) - }).or_else(|| { - add_derived_markers(self.cx, &mut attrs); - get_derive_attr(self.cx, &mut attrs, DeriveType::Builtin) - }).or_else(|| { - get_derive_attr(self.cx, &mut attrs, DeriveType::ProcMacro) - }); + attr = self.cx.resolver.find_attr_invoc(&mut attrs).or_else(|| { + find_derive_attr(self.cx, &mut attrs) + }); attrs }); @@ -958,7 +928,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); + let item = + Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() } From 242f3f36043a24bd7e7768fccc872e00113cb7cc Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Wed, 1 Feb 2017 07:34:25 +1030 Subject: [PATCH 14/18] Mark empty derive attributes as used (no traits is a warning) --- src/libsyntax/ext/derive.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 57c63b83a77b..d73f65cfd853 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -51,6 +51,7 @@ pub fn verify_derive_attrs(cx: &mut ExtCtxt, attrs: &[ast::Attribute]) { if traits.is_empty() { cx.span_warn(attr.span, "empty trait list in `derive`"); + attr::mark_used(&attr); continue; } for titem in traits { From 755957448cdb21290a706bffb005a744e1e34642 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Wed, 1 Feb 2017 07:39:54 +1030 Subject: [PATCH 15/18] Fix rustc_copy_clone_marker typo --- src/libsyntax/ext/derive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index d73f65cfd853..c3d97c6bbb6b 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -177,7 +177,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec) { 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("structural_match"); + 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)); From 8b27f760fff9f287991de884ceef157d6b451ace Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Wed, 1 Feb 2017 19:59:34 +1030 Subject: [PATCH 16/18] Add explicit derive macro resolver --- src/librustc_resolve/macros.rs | 19 +++++++++++++++++++ src/libsyntax/ext/base.rs | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 038914a9bf4e..30ceea257762 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -258,6 +258,25 @@ impl<'a> base::Resolver for Resolver<'a> { } } + fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) + -> Result, Determinacy> { + let ast::Path { ref segments, span } = *path; + match self.resolve_macro(scope, path, force) { + Ok(ext) => match *ext { + SyntaxExtension::BuiltinDerive(..) | + SyntaxExtension::ProcMacroDerive(..) => Ok(ext), + _ => Err(Determinacy::Determined), + }, + Err(Determinacy::Undetermined) if force => { + let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect(); + let msg = format!("derive macro does not exist: '{}'", path[0].name); + let mut err = self.session.struct_span_err(span, &msg); + err.emit(); + Err(Determinacy::Determined) + }, + Err(err) => Err(err), + } + } } impl<'a> Resolver<'a> { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index a7720e2aad8b..9a717b86d091 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -537,6 +537,8 @@ pub trait Resolver { fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) -> Result, Determinacy>; fn resolve_builtin_macro(&mut self, tname: Name) -> Result, Determinacy>; + fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) + -> Result, Determinacy>; } #[derive(Copy, Clone, Debug)] @@ -566,6 +568,10 @@ impl Resolver for DummyResolver { fn resolve_builtin_macro(&mut self, _tname: Name) -> Result, Determinacy> { Err(Determinacy::Determined) } + fn resolve_derive_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool) + -> Result, Determinacy> { + Err(Determinacy::Determined) + } } #[derive(Clone)] From 260107636fe8b5c1be4d37682586d0325ebc7fe9 Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Wed, 1 Feb 2017 20:00:01 +1030 Subject: [PATCH 17/18] Various fixes --- src/libsyntax/ext/derive.rs | 74 ++++++++++++++++++------------------- src/libsyntax/ext/expand.rs | 6 ++- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index c3d97c6bbb6b..87608d56518b 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -8,14 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{self, NestedMetaItem}; -use ext::base::{ExtCtxt, SyntaxExtension}; -use ext::build::AstBuilder; 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; -use codemap; pub fn derive_attr_trait<'a>(cx: &mut ExtCtxt, attr: &'a ast::Attribute) -> Option<&'a NestedMetaItem> { @@ -71,20 +71,19 @@ pub enum DeriveType { impl DeriveType { // Classify a derive trait name by resolving the macro. - pub fn classify(cx: &mut ExtCtxt, tname: Name) -> Option { + 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 Some(DeriveType::Legacy); + return DeriveType::Legacy; } match cx.resolver.resolve_builtin_macro(tname) { Ok(ext) => match *ext { - SyntaxExtension::BuiltinDerive(..) => Some(DeriveType::Builtin), - SyntaxExtension::ProcMacroDerive(..) => Some(DeriveType::ProcMacro), - _ => None, + SyntaxExtension::BuiltinDerive(..) => DeriveType::Builtin, + _ => DeriveType::ProcMacro, }, - Err(_) => Some(DeriveType::ProcMacro), + Err(_) => DeriveType::ProcMacro, } } } @@ -109,13 +108,12 @@ pub fn get_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec, // See if we can find a matching trait. for j in 0..traits.len() { - let tname = if let Some(tname) = traits[j].name() { - tname - } else { - continue; + let tname = match traits[j].name() { + Some(tname) => tname, + _ => continue, }; - if DeriveType::classify(cx, tname) == Some(derive_type) { + if DeriveType::classify(cx, tname) == derive_type { titem = Some(traits.remove(j)); break; } @@ -168,7 +166,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut 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") { + 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); @@ -176,7 +174,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, attrs: &mut Vec) { } if !attrs.iter().any(|a| a.name() == "rustc_copy_clone_marker") && - titems.iter().any(|t| *t == "Copy") && titems.iter().any(|t| *t == "Clone") { + 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); @@ -188,29 +186,29 @@ pub fn find_derive_attr(cx: &mut ExtCtxt, attrs: &mut Vec) -> Option { verify_derive_attrs(cx, attrs); get_derive_attr(cx, attrs, DeriveType::Legacy).and_then(|a| { - let titem = derive_attr_trait(cx, &a); - if let Some(titem) = 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 - ); - } 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); - return Some(cx.attribute(mitem.span, mitem)); + 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)) } - None + }) }).or_else(|| { add_derived_markers(cx, attrs); get_derive_attr(cx, attrs, DeriveType::Builtin) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 19c57b4cc91a..8e7f8830eafb 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -258,8 +258,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } InvocationKind::Derive { ref attr, .. } => { let titem = derive_attr_trait(self.cx, &attr).unwrap(); - let tname = titem.name().unwrap(); - self.cx.resolver.resolve_builtin_macro(tname) + let tname = titem.name().expect("Expected derive macro name"); + let ident = Ident::with_empty_ctxt(tname); + let path = ast::Path::from_ident(attr.span, ident); + self.cx.resolver.resolve_derive_macro(scope, &path, force) } }; let ext = match resolution { From 65858e03a5cf50898d364e5f0e80108c5a3452fb Mon Sep 17 00:00:00 2001 From: Josh Driver Date: Wed, 1 Feb 2017 20:33:18 +1030 Subject: [PATCH 18/18] Don't force when resolving a derive macro --- src/librustc_resolve/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 30ceea257762..98ac91eda7ed 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -261,7 +261,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) -> Result, Determinacy> { let ast::Path { ref segments, span } = *path; - match self.resolve_macro(scope, path, force) { + match self.resolve_macro(scope, path, false) { Ok(ext) => match *ext { SyntaxExtension::BuiltinDerive(..) | SyntaxExtension::ProcMacroDerive(..) => Ok(ext),