@@ -4,105 +4,189 @@ extern crate proc_macro;
44extern crate quote;
55extern crate syn;
66extern crate proc_macro2;
7- use syn:: { Data , Ident , DeriveInput , DataEnum } ;
7+ use syn:: { Data , Ident , DeriveInput , DataEnum , spanned :: Spanned } ;
88use proc_macro2:: TokenStream ;
99use proc_macro2:: Span ;
10- use quote:: ToTokens ;
1110use std:: convert:: From ;
1211
12+ /// Shorthand for a quoted `compile_error!`.
13+ macro_rules! error {
14+ ( $span: expr => $( $x: tt) * ) => {
15+ quote_spanned!( $span =>
16+ :: enumflags2:: _internal:: core:: compile_error!( $( $x) * ) ;
17+ )
18+ } ;
19+ ( $( $x: tt) * ) => {
20+ quote!(
21+ :: enumflags2:: _internal:: core:: compile_error!( $( $x) * ) ;
22+ )
23+ }
24+ }
25+
1326#[ proc_macro_derive( BitFlags_internal ) ]
14- pub fn derive_enum_flags ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
27+ pub fn derive_enum_flags ( input : proc_macro:: TokenStream )
28+ -> proc_macro:: TokenStream
29+ {
1530 let ast: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
1631
1732 match ast. data {
18- Data :: Enum ( ref data) => gen_enumflags ( & ast. ident , & ast, data) . into ( ) ,
19- _ => panic ! ( "`derive(BitFlags)` may only be applied to enums" ) ,
33+ Data :: Enum ( ref data) => {
34+ gen_enumflags ( & ast. ident , & ast, data)
35+ . unwrap_or_else ( |err| err)
36+ . into ( )
37+ }
38+ _ => error ! ( "BitFlags can only be derived on enums" ) . into ( ) ,
39+ }
40+ }
41+
42+ #[ derive( Debug ) ]
43+ enum EvaluationError {
44+ LiteralOutOfRange ( Span ) ,
45+ UnsupportedOperation ( Span ) ,
46+ }
47+
48+ impl From < EvaluationError > for TokenStream {
49+ fn from ( why : EvaluationError ) -> TokenStream {
50+ use EvaluationError :: * ;
51+
52+ match why {
53+ LiteralOutOfRange ( span) => {
54+ error ! ( span => "Integer literal out of range" )
55+ }
56+ UnsupportedOperation ( span) => {
57+ error ! ( span => "This kind of discriminant expression is \
58+ not supported.\n \
59+ hint: Enable the \" not_literal\" feature to \
60+ use a workaround.\n \
61+ note: This is not enabled by default due to the \
62+ high potential for confusing error messages \
63+ (see documentation).")
64+ }
65+ }
2066 }
2167}
2268
23- fn fold_expr ( expr : & syn:: Expr ) -> u64 {
69+ /// Try to evaluate the expression given.
70+ fn fold_expr ( expr : & syn:: Expr ) -> Result < u64 , EvaluationError > {
2471 use syn:: Expr ;
72+ use EvaluationError :: * ;
2573 match expr {
2674 Expr :: Lit ( ref expr_lit) => {
2775 match expr_lit. lit {
28- syn:: Lit :: Int ( ref lit_int) => lit_int. base10_parse ( ) . expect ( "Int literal out of range" ) ,
29- _ => panic ! ( "Only Int literals are supported" )
76+ syn:: Lit :: Int ( ref lit_int) => {
77+ lit_int. base10_parse ( )
78+ . or_else ( |_| Err ( LiteralOutOfRange ( expr. span ( ) ) ) )
79+ }
80+ _ => Err ( UnsupportedOperation ( expr. span ( ) ) )
3081 }
3182 } ,
3283 Expr :: Binary ( ref expr_binary) => {
33- let l = fold_expr ( & expr_binary. left ) ;
34- let r = fold_expr ( & expr_binary. right ) ;
84+ let l = fold_expr ( & expr_binary. left ) ? ;
85+ let r = fold_expr ( & expr_binary. right ) ? ;
3586 match & expr_binary. op {
36- syn:: BinOp :: Shl ( _) => l << r,
37- op => panic ! ( "{} not supported" , op . to_token_stream ( ) )
87+ syn:: BinOp :: Shl ( _) => Ok ( l << r) ,
88+ _ => Err ( UnsupportedOperation ( expr_binary . span ( ) ) )
3889 }
3990 }
40- _ => panic ! ( "Only literals are supported" )
91+ _ => Err ( UnsupportedOperation ( expr . span ( ) ) )
4192 }
4293}
4394
44- fn extract_repr ( attrs : & [ syn:: Attribute ] ) -> Option < syn:: Ident > {
45- attrs
46- . iter ( )
47- . filter_map ( |a| {
48- if let syn:: Meta :: List ( ref meta) = a. parse_meta ( ) . expect ( "Metalist" ) {
49- if meta. path . is_ident ( "repr" ) {
50- return meta. nested
51- . iter ( )
52- . filter_map ( |mi| {
53- if let syn:: NestedMeta :: Meta ( syn:: Meta :: Path ( path) ) =
54- mi
55- {
56- return path. get_ident ( ) . cloned ( ) ;
95+ /// Given a list of attributes, find the `repr`, if any, and return the integer
96+ /// type specified.
97+ fn extract_repr ( attrs : & [ syn:: Attribute ] )
98+ -> Result < Option < syn:: Ident > , TokenStream >
99+ {
100+ use syn:: { Meta , NestedMeta } ;
101+ attrs. iter ( )
102+ . find_map ( |attr| {
103+ match attr. parse_meta ( ) {
104+ Err ( why) => {
105+ let error = format ! ( "Couldn't parse attribute: {}" , why) ;
106+ Some ( Err ( error ! ( attr. span( ) => #error) ) )
107+ }
108+ Ok ( Meta :: List ( ref meta) ) if meta. path . is_ident ( "repr" ) => {
109+ meta. nested . iter ( )
110+ . find_map ( |mi| match mi {
111+ NestedMeta :: Meta ( Meta :: Path ( path) ) => {
112+ path. get_ident ( ) . cloned ( )
113+ . map ( Ok )
57114 }
58- None
115+ _ => None
59116 } )
60- . nth ( 0 ) ;
61117 }
118+ Ok ( _) => None
62119 }
63- None
64120 } )
65- . nth ( 0 )
121+ . transpose ( )
66122}
67123
68- fn gen_enumflags ( ident : & Ident , item : & DeriveInput , data : & DataEnum ) -> TokenStream {
69- let span = Span :: call_site ( ) ;
70- let variants = data. variants . iter ( ) . map ( |v| & v. ident ) ;
71- let flag_values: Vec < _ > =
72- data. variants . iter ( )
73- . map ( |v| v. discriminant . as_ref ( )
74- . map ( |d| fold_expr ( & d. 1 ) ) . expect ( "No discriminant" ) )
75- . collect ( ) ;
76- let variants_len = flag_values. len ( ) ;
77- let names = flag_values. iter ( ) . map ( |_| & ident) ;
78- let ty = extract_repr ( & item. attrs )
79- . unwrap_or_else ( || Ident :: new ( "usize" , span) ) ;
80-
81- let mut flags_seen = 0 ;
82- for ( & flag, variant) in flag_values. iter ( ) . zip ( variants. clone ( ) ) {
83- if flag == 0 || !flag. is_power_of_two ( ) {
84- panic ! ( "Each flag must have exactly one bit set, and {ident}::{variant} = {flag:#b} doesn't" ,
85- ident = ident,
86- variant = variant,
87- flag = flag
88- ) ;
89- } else if flags_seen & flag != 0 {
90- panic ! ( "Flag {} collides with {}" ,
91- variant,
92- flag_values. iter( )
93- . zip( variants. clone( ) )
94- . find( |( & other_flag, _) | flag == other_flag)
95- . unwrap( )
96- . 1
97- ) ;
124+ /// Returns Ok with deferred checks (not_literal), or Err with error!
125+ fn verify_flag_values < ' a > (
126+ // starts with underscore to silence warnings when not_literal
127+ // are disabled
128+ _type_name : & Ident ,
129+ variants : impl Iterator < Item =& ' a syn:: Variant >
130+ ) -> Result < TokenStream , TokenStream > {
131+ #[ cfg_attr( not( feature = "not_literal" ) , allow( unused_mut) ) ]
132+ let mut deferred_checks: Vec < TokenStream > = vec ! [ ] ;
133+ for variant in variants {
134+ let discr = variant. discriminant . as_ref ( )
135+ . ok_or_else ( || error ! ( variant. span( ) =>
136+ "Please add an explicit discriminant" ) ) ?;
137+ match fold_expr ( & discr. 1 ) {
138+ Ok ( flag) => {
139+ if !flag. is_power_of_two ( ) {
140+ return Err ( error ! ( variant. discriminant. as_ref( )
141+ . unwrap( ) . 1 . span( ) =>
142+ "Flags must have exactly one set bit." ) ) ;
143+ }
144+ }
145+ #[ cfg( feature = "not_literal" ) ]
146+ Err ( EvaluationError :: UnsupportedOperation ( _) ) => {
147+ let variant_name = & variant. ident ;
148+ // adapted from static-assertions-rs by nvzqz (MIT/Apache-2.0)
149+ deferred_checks. push ( quote_spanned ! ( variant. span( ) =>
150+ #[ allow( unknown_lints, eq_op) ]
151+ const _: [ ( ) ; 0 - !(
152+ ( #_type_name:: #variant_name as u64 ) . wrapping_sub( 1 ) &
153+ ( #_type_name:: #variant_name as u64 ) == 0 &&
154+ ( #_type_name:: #variant_name as u64 ) != 0
155+ ) as usize ] = [ ] ;
156+ ) ) ;
157+ }
158+ Err ( why) => Err ( why) ?,
98159 }
99-
100- flags_seen |= flag;
101160 }
102161
103- let std_path = quote_spanned ! ( span=> :: enumflags2:: _internal:: core) ;
104- quote_spanned ! {
105- span =>
162+ Ok ( quote ! (
163+ #( #deferred_checks) *
164+ ) )
165+ }
166+
167+ fn gen_enumflags ( ident : & Ident , item : & DeriveInput , data : & DataEnum )
168+ -> Result < TokenStream , TokenStream >
169+ {
170+ let span = Span :: call_site ( ) ;
171+ // for quote! interpolation
172+ let variants = data. variants . iter ( ) . map ( |v| & v. ident ) ;
173+ let variants_len = data. variants . len ( ) ;
174+ let names = std:: iter:: repeat ( & ident) ;
175+ let ty = extract_repr ( & item. attrs ) ?
176+ . unwrap_or_else ( || Ident :: new ( "usize" , span) ) ;
177+
178+ let deferred = verify_flag_values ( ident, data. variants . iter ( ) ) ?;
179+ let std_path = quote_spanned ! ( span => :: enumflags2:: _internal:: core) ;
180+ let all = if variants_len == 0 {
181+ quote ! ( 0 )
182+ } else {
183+ let names = names. clone ( ) ;
184+ let variants = variants. clone ( ) ;
185+ quote ! ( #( #names:: #variants as #ty) |* )
186+ } ;
187+
188+ Ok ( quote_spanned ! {
189+ span => #deferred
106190 impl #std_path:: ops:: Not for #ident {
107191 type Output = :: enumflags2:: BitFlags <#ident>;
108192 fn not( self ) -> Self :: Output {
@@ -138,21 +222,23 @@ fn gen_enumflags(ident: &Ident, item: &DeriveInput, data: &DataEnum) -> TokenStr
138222 type Type = #ty;
139223
140224 fn all( ) -> Self :: Type {
141- ( #( #flag_values) |* ) as #ty
225+ // make sure it's evaluated at compile time
226+ const VALUE : #ty = #all as #ty;
227+ VALUE
142228 }
143229
144230 fn bits( self ) -> Self :: Type {
145231 self as #ty
146232 }
147233
148234 fn flag_list( ) -> & ' static [ Self ] {
149- const VARIANTS : [ #ident; #variants_len] = [ #( #names :: #variants, ) * ] ;
235+ const VARIANTS : [ #ident; #variants_len] = [ #( #names :: #variants) , * ] ;
150236 & VARIANTS
151237 }
152238
153239 fn bitflags_type_name( ) -> & ' static str {
154240 concat!( "BitFlags<" , stringify!( #ident) , ">" )
155241 }
156242 }
157- }
243+ } )
158244}
0 commit comments