Fix `num-macros` `FromPrimitive` implementation

Current solution follow syntax proposed in rust-lang/rfcs#1681.

Tracking issue rust-lang/rust#35900

Close #227
This commit is contained in:
Łukasz Jan Niemier 2016-09-18 20:56:36 +02:00
parent a11be641ed
commit 11f8289ed4
No known key found for this signature in database
GPG Key ID: C775391C950A6AEE
3 changed files with 63 additions and 135 deletions

View File

@ -1,17 +1,26 @@
[package] [package]
name = "num-macros"
version = "0.1.33"
authors = ["The Rust Project Developers"] authors = ["The Rust Project Developers"]
license = "MIT/Apache-2.0"
homepage = "https://github.com/rust-num/num"
repository = "https://github.com/rust-num/num"
documentation = "http://rust-num.github.io/num"
keywords = ["mathematics", "numerics"]
description = "Numeric syntax extensions" description = "Numeric syntax extensions"
documentation = "http://rust-num.github.io/num"
homepage = "https://github.com/rust-num/num"
keywords = ["mathematics", "numerics"]
license = "MIT/Apache-2.0"
name = "num-macros"
repository = "https://github.com/rust-num/num"
version = "0.1.33"
[lib] [dependencies]
name = "num_macros" quote = "0.1.3"
plugin = true syn = "0.5.2"
[dev-dependencies] [dev-dependencies]
num = { path = "..", version = "0.1" }
[dev-dependencies.num]
path = ".."
version = "0.1"
[lib]
crate-type = ["rustc-macro"]
name = "num_macros"
rustc-macro = true
test = false

View File

@ -8,43 +8,24 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(plugin_registrar, rustc_private)] #![crate_type = "rustc-macro"]
#![feature(rustc_macro, rustc_macro_lib)]
extern crate syntax; extern crate syn;
extern crate syntax_ext; #[macro_use]
extern crate rustc_plugin; extern crate quote;
extern crate rustc_macro;
use syntax::ast::{MetaItem, Expr, BinOpKind}; use rustc_macro::TokenStream;
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::ext::build::AstBuilder;
use syntax_ext::deriving::generic::*;
use syntax_ext::deriving::generic::ty::*;
use syntax::parse::token::InternedString;
use syntax::ptr::P;
use syntax::ext::base::MultiDecorator;
use syntax::parse::token;
use rustc_plugin::Registry; use syn::Body::Enum;
macro_rules! pathvec { #[rustc_macro_derive(FromPrimitive)]
($($x:ident)::+) => ( pub fn from_primitive(input: TokenStream) -> TokenStream {
vec![ $( stringify!($x) ),+ ] let source = input.to_string();
)
}
macro_rules! path { let ast = syn::parse_item(&source).unwrap();
($($x:tt)*) => ( // panic!("{:?}", ast);
::syntax_ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
)
}
macro_rules! path_local {
($x:ident) => (
::syntax_ext::deriving::generic::ty::Path::new_local(stringify!($x))
)
}
macro_rules! pathvec_std { macro_rules! pathvec_std {
($cx:expr, $first:ident :: $($rest:ident)::+) => ({ ($cx:expr, $first:ident :: $($rest:ident)::+) => ({
@ -111,91 +92,32 @@ pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
supports_unions: false, supports_unions: false,
}; };
trait_def.expand(cx, mitem, &item, push) let mut idx = 0;
} let variants: Vec<_> = variants.iter()
.map(|variant| {
let ident = &variant.ident;
let tt = quote!(#idx => Some(#name::#ident));
idx += 1;
tt
})
.collect();
fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> { let res = quote! {
if substr.nonself_args.len() != 1 { #ast
cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
}
let n = &substr.nonself_args[0]; impl ::num::traits::FromPrimitive for #name {
fn from_i64(n: i64) -> Option<Self> {
match *substr.fields { Self::from_u64(n as u64)
StaticStruct(..) => {
cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
return cx.expr_fail(trait_span, InternedString::new(""));
}
StaticEnum(enum_def, _) => {
if enum_def.variants.is_empty() {
cx.span_err(trait_span,
"`FromPrimitive` cannot be derived for enums with no variants");
return cx.expr_fail(trait_span, InternedString::new(""));
} }
let mut arms = Vec::new(); fn from_u64(n: u64) -> Option<Self> {
match n {
for variant in &enum_def.variants { #(variants,)*
match variant.node.data { _ => None,
ast::VariantData::Unit(..) => {
let span = variant.span;
// expr for `$n == $variant as $name`
let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
let variant = cx.expr_path(path);
let ty = cx.ty_ident(span, cx.ident_of(name));
let cast = cx.expr_cast(span, variant.clone(), ty);
let guard = cx.expr_binary(span, BinOpKind::Eq, n.clone(), cast);
// expr for `Some($variant)`
let body = cx.expr_some(span, variant);
// arm for `_ if $guard => $body`
let arm = ast::Arm {
attrs: vec!(),
pats: vec!(cx.pat_wild(span)),
guard: Some(guard),
body: body,
};
arms.push(arm);
}
ast::VariantData::Tuple(..) => {
cx.span_err(trait_span,
"`FromPrimitive` cannot be derived for \
enum variants with arguments");
return cx.expr_fail(trait_span,
InternedString::new(""));
}
ast::VariantData::Struct(..) => {
cx.span_err(trait_span,
"`FromPrimitive` cannot be derived for enums \
with struct variants");
return cx.expr_fail(trait_span,
InternedString::new(""));
}
} }
} }
// arm for `_ => None`
let arm = ast::Arm {
attrs: vec!(),
pats: vec!(cx.pat_wild(trait_span)),
guard: None,
body: cx.expr_none(trait_span),
};
arms.push(arm);
cx.expr_match(trait_span, n.clone(), arms)
} }
_ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)") };
}
}
#[plugin_registrar] res.to_string().parse().unwrap()
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_syntax_extension(
token::intern("derive_NumFromPrimitive"),
MultiDecorator(Box::new(expand_deriving_from_primitive)));
} }

View File

@ -8,12 +8,13 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(custom_derive, plugin)] #![feature(rustc_macro)]
#![plugin(num_macros)]
extern crate num; extern crate num;
#[macro_use]
extern crate num_macros;
#[derive(Debug, PartialEq, NumFromPrimitive)] #[derive(Debug, PartialEq, FromPrimitive)]
enum Color { enum Color {
Red, Red,
Blue, Blue,
@ -22,15 +23,11 @@ enum Color {
#[test] #[test]
fn test_from_primitive() { fn test_from_primitive() {
let v: Vec<Option<Color>> = vec![ let v: [Option<Color>; 4] = [num::FromPrimitive::from_u64(0),
num::FromPrimitive::from_u64(0), num::FromPrimitive::from_u64(1),
num::FromPrimitive::from_u64(1), num::FromPrimitive::from_u64(2),
num::FromPrimitive::from_u64(2), num::FromPrimitive::from_u64(3)];
num::FromPrimitive::from_u64(3),
];
assert_eq!( assert_eq!(v,
v, [Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]);
vec![Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]
);
} }