Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/librustc_metadata/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)));
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -507,7 +510,14 @@ pub enum SyntaxExtension {
///
IdentTT(Box<IdentMacroExpander>, Option<Span>, bool),

CustomDerive(Box<MultiItemModifier>),
/// 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<MultiItemModifier>),

/// An attribute-like procedural macro that derives a builtin trait.
BuiltinDerive(BuiltinDeriveFn),
}

pub type NamedSyntaxExtension = (Name, SyntaxExtension);
Expand Down
250 changes: 250 additions & 0 deletions src/libsyntax/ext/derive.rs
Original file line number Diff line number Diff line change
@@ -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 <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::{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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This can just be attr.name() == "derive" (I would inline and remove the function)


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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it might panic on #[derive].


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<ast::Attribute>) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think attrs: &[ast::Attribute] is sufficient here

for i in 0..attrs.len() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: for attr in attrs { ... } is more idiomatic

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<ast::Attribute>,
is_derive_type: fn(&mut ExtCtxt, &NestedMetaItem) -> bool)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be a little cleaner if this were an enum (three variants) and is_derive_type(...) were a match.

-> Option<ast::Attribute> {
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<ast::Attribute>)
-> Option<ast::Attribute> {
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<ast::Attribute>)
-> Option<ast::Attribute> {
get_derive_attr(cx, attrs, is_proc_macro_derive)
}

pub fn get_builtin_derive(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>)
-> Option<ast::Attribute> {
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| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same here, re resolver.builtin_macros)

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<ast::Attribute>) {
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<ast::Attribute>) {
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;
}
}
}
Loading