Skip to content

Commit 11f8289

Browse files
committed
Fix num-macros FromPrimitive implementation
Current solution follow syntax proposed in rust-lang/rfcs#1681. Tracking issue rust-lang/rust#35900 Close #227
1 parent a11be64 commit 11f8289

File tree

3 files changed

+64
-136
lines changed

3 files changed

+64
-136
lines changed

macros/Cargo.toml

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
[package]
2-
name = "num-macros"
3-
version = "0.1.33"
42
authors = ["The Rust Project Developers"]
5-
license = "MIT/Apache-2.0"
6-
homepage = "https://github.com/rust-num/num"
7-
repository = "https://github.com/rust-num/num"
3+
description = "Numeric syntax extensions"
84
documentation = "http://rust-num.github.io/num"
5+
homepage = "https://github.com/rust-num/num"
96
keywords = ["mathematics", "numerics"]
10-
description = "Numeric syntax extensions"
7+
license = "MIT/Apache-2.0"
8+
name = "num-macros"
9+
repository = "https://github.com/rust-num/num"
10+
version = "0.1.33"
1111

12-
[lib]
13-
name = "num_macros"
14-
plugin = true
12+
[dependencies]
13+
quote = "0.1.3"
14+
syn = "0.5.2"
1515

1616
[dev-dependencies]
17-
num = { path = "..", version = "0.1" }
17+
18+
[dev-dependencies.num]
19+
path = ".."
20+
version = "0.1"
21+
22+
[lib]
23+
crate-type = ["rustc-macro"]
24+
name = "num_macros"
25+
rustc-macro = true
26+
test = false

macros/src/lib.rs

+35-113
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,24 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(plugin_registrar, rustc_private)]
11+
#![crate_type = "rustc-macro"]
12+
#![feature(rustc_macro, rustc_macro_lib)]
1213

13-
extern crate syntax;
14-
extern crate syntax_ext;
15-
extern crate rustc_plugin;
14+
extern crate syn;
15+
#[macro_use]
16+
extern crate quote;
17+
extern crate rustc_macro;
1618

17-
use syntax::ast::{MetaItem, Expr, BinOpKind};
18-
use syntax::ast;
19-
use syntax::codemap::Span;
20-
use syntax::ext::base::{ExtCtxt, Annotatable};
21-
use syntax::ext::build::AstBuilder;
22-
use syntax_ext::deriving::generic::*;
23-
use syntax_ext::deriving::generic::ty::*;
24-
use syntax::parse::token::InternedString;
25-
use syntax::ptr::P;
26-
use syntax::ext::base::MultiDecorator;
27-
use syntax::parse::token;
19+
use rustc_macro::TokenStream;
2820

29-
use rustc_plugin::Registry;
21+
use syn::Body::Enum;
3022

31-
macro_rules! pathvec {
32-
($($x:ident)::+) => (
33-
vec![ $( stringify!($x) ),+ ]
34-
)
35-
}
23+
#[rustc_macro_derive(FromPrimitive)]
24+
pub fn from_primitive(input: TokenStream) -> TokenStream {
25+
let source = input.to_string();
3626

37-
macro_rules! path {
38-
($($x:tt)*) => (
39-
::syntax_ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
40-
)
41-
}
42-
43-
macro_rules! path_local {
44-
($x:ident) => (
45-
::syntax_ext::deriving::generic::ty::Path::new_local(stringify!($x))
46-
)
47-
}
27+
let ast = syn::parse_item(&source).unwrap();
28+
// panic!("{:?}", ast);
4829

4930
macro_rules! pathvec_std {
5031
($cx:expr, $first:ident :: $($rest:ident)::+) => ({
@@ -111,91 +92,32 @@ pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
11192
supports_unions: false,
11293
};
11394

114-
trait_def.expand(cx, mitem, &item, push)
115-
}
116-
117-
fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
118-
if substr.nonself_args.len() != 1 {
119-
cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
120-
}
121-
122-
let n = &substr.nonself_args[0];
123-
124-
match *substr.fields {
125-
StaticStruct(..) => {
126-
cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
127-
return cx.expr_fail(trait_span, InternedString::new(""));
128-
}
129-
StaticEnum(enum_def, _) => {
130-
if enum_def.variants.is_empty() {
131-
cx.span_err(trait_span,
132-
"`FromPrimitive` cannot be derived for enums with no variants");
133-
return cx.expr_fail(trait_span, InternedString::new(""));
95+
let mut idx = 0;
96+
let variants: Vec<_> = variants.iter()
97+
.map(|variant| {
98+
let ident = &variant.ident;
99+
let tt = quote!(#idx => Some(#name::#ident));
100+
idx += 1;
101+
tt
102+
})
103+
.collect();
104+
105+
let res = quote! {
106+
#ast
107+
108+
impl ::num::traits::FromPrimitive for #name {
109+
fn from_i64(n: i64) -> Option<Self> {
110+
Self::from_u64(n as u64)
134111
}
135112

136-
let mut arms = Vec::new();
137-
138-
for variant in &enum_def.variants {
139-
match variant.node.data {
140-
ast::VariantData::Unit(..) => {
141-
let span = variant.span;
142-
143-
// expr for `$n == $variant as $name`
144-
let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
145-
let variant = cx.expr_path(path);
146-
let ty = cx.ty_ident(span, cx.ident_of(name));
147-
let cast = cx.expr_cast(span, variant.clone(), ty);
148-
let guard = cx.expr_binary(span, BinOpKind::Eq, n.clone(), cast);
149-
150-
// expr for `Some($variant)`
151-
let body = cx.expr_some(span, variant);
152-
153-
// arm for `_ if $guard => $body`
154-
let arm = ast::Arm {
155-
attrs: vec!(),
156-
pats: vec!(cx.pat_wild(span)),
157-
guard: Some(guard),
158-
body: body,
159-
};
160-
161-
arms.push(arm);
162-
}
163-
ast::VariantData::Tuple(..) => {
164-
cx.span_err(trait_span,
165-
"`FromPrimitive` cannot be derived for \
166-
enum variants with arguments");
167-
return cx.expr_fail(trait_span,
168-
InternedString::new(""));
169-
}
170-
ast::VariantData::Struct(..) => {
171-
cx.span_err(trait_span,
172-
"`FromPrimitive` cannot be derived for enums \
173-
with struct variants");
174-
return cx.expr_fail(trait_span,
175-
InternedString::new(""));
176-
}
113+
fn from_u64(n: u64) -> Option<Self> {
114+
match n {
115+
#(variants,)*
116+
_ => None,
177117
}
178118
}
179-
180-
// arm for `_ => None`
181-
let arm = ast::Arm {
182-
attrs: vec!(),
183-
pats: vec!(cx.pat_wild(trait_span)),
184-
guard: None,
185-
body: cx.expr_none(trait_span),
186-
};
187-
arms.push(arm);
188-
189-
cx.expr_match(trait_span, n.clone(), arms)
190119
}
191-
_ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)")
192-
}
193-
}
120+
};
194121

195-
#[plugin_registrar]
196-
#[doc(hidden)]
197-
pub fn plugin_registrar(reg: &mut Registry) {
198-
reg.register_syntax_extension(
199-
token::intern("derive_NumFromPrimitive"),
200-
MultiDecorator(Box::new(expand_deriving_from_primitive)));
122+
res.to_string().parse().unwrap()
201123
}

macros/tests/test_macro.rs

+10-13
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(custom_derive, plugin)]
12-
#![plugin(num_macros)]
11+
#![feature(rustc_macro)]
1312

1413
extern crate num;
14+
#[macro_use]
15+
extern crate num_macros;
1516

16-
#[derive(Debug, PartialEq, NumFromPrimitive)]
17+
#[derive(Debug, PartialEq, FromPrimitive)]
1718
enum Color {
1819
Red,
1920
Blue,
@@ -22,15 +23,11 @@ enum Color {
2223

2324
#[test]
2425
fn test_from_primitive() {
25-
let v: Vec<Option<Color>> = vec![
26-
num::FromPrimitive::from_u64(0),
27-
num::FromPrimitive::from_u64(1),
28-
num::FromPrimitive::from_u64(2),
29-
num::FromPrimitive::from_u64(3),
30-
];
26+
let v: [Option<Color>; 4] = [num::FromPrimitive::from_u64(0),
27+
num::FromPrimitive::from_u64(1),
28+
num::FromPrimitive::from_u64(2),
29+
num::FromPrimitive::from_u64(3)];
3130

32-
assert_eq!(
33-
v,
34-
vec![Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]
35-
);
31+
assert_eq!(v,
32+
[Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]);
3633
}

0 commit comments

Comments
 (0)