@@ -31,10 +31,11 @@ use quote::quote;
31
31
32
32
use syn:: {
33
33
fold:: { self , Fold } ,
34
+ parenthesized,
34
35
parse:: { Error , Parse , ParseStream , Result } ,
35
36
parse_macro_input, parse_quote,
36
37
spanned:: Spanned ,
37
- Attribute , Ident , ImplItem , ItemImpl , Path , Signature , Type , TypePath ,
38
+ Attribute , Ident , ImplItem , ItemImpl , LitInt , LitStr , Path , Signature , Type , TypePath ,
38
39
} ;
39
40
40
41
use std:: collections:: HashSet ;
@@ -67,6 +68,7 @@ fn generate_impl_call(
67
68
runtime : & Type ,
68
69
input : & Ident ,
69
70
impl_trait : & Path ,
71
+ api_version : & ApiVersion ,
70
72
) -> Result < TokenStream > {
71
73
let params =
72
74
extract_parameter_names_types_and_borrows ( signature, AllowSelfRefInParameters :: No ) ?;
@@ -111,11 +113,40 @@ fn generate_impl_call(
111
113
)
112
114
} ;
113
115
116
+ let fn_calls = if let Some ( feature_gated) = & api_version. feature_gated {
117
+ let pnames = pnames2;
118
+ let pnames2 = pnames. clone ( ) ;
119
+ let pborrow2 = pborrow. clone ( ) ;
120
+
121
+ let feature_name = & feature_gated. 0 ;
122
+ let impl_trait_fg = extend_with_api_version ( impl_trait. clone ( ) , Some ( feature_gated. 1 ) ) ;
123
+ let impl_trait = extend_with_api_version ( impl_trait. clone ( ) , api_version. custom ) ;
124
+
125
+ quote ! (
126
+ #[ cfg( feature = #feature_name) ]
127
+ #[ allow( deprecated) ]
128
+ let r = <#runtime as #impl_trait_fg>:: #fn_name( #( #pborrow #pnames ) , * ) ;
129
+
130
+ #[ cfg( not( feature = #feature_name) ) ]
131
+ #[ allow( deprecated) ]
132
+ let r = <#runtime as #impl_trait>:: #fn_name( #( #pborrow2 #pnames2 ) , * ) ;
133
+
134
+ r
135
+ )
136
+ } else {
137
+ let pnames = pnames2;
138
+ let impl_trait = extend_with_api_version ( impl_trait. clone ( ) , api_version. custom ) ;
139
+
140
+ quote ! (
141
+ #[ allow( deprecated) ]
142
+ <#runtime as #impl_trait>:: #fn_name( #( #pborrow #pnames ) , * )
143
+ )
144
+ } ;
145
+
114
146
Ok ( quote ! (
115
147
#decode_params
116
148
117
- #[ allow( deprecated) ]
118
- <#runtime as #impl_trait>:: #fn_name( #( #pborrow #pnames2 ) , * )
149
+ #fn_calls
119
150
) )
120
151
}
121
152
@@ -130,7 +161,6 @@ fn generate_impl_calls(
130
161
let trait_api_ver = extract_api_version ( & impl_. attrs , impl_. span ( ) ) ?;
131
162
let impl_trait_path = extract_impl_trait ( impl_, RequireQualifiedTraitPath :: Yes ) ?;
132
163
let impl_trait = extend_with_runtime_decl_path ( impl_trait_path. clone ( ) ) ;
133
- let impl_trait = extend_with_api_version ( impl_trait, trait_api_ver) ;
134
164
let impl_trait_ident = & impl_trait_path
135
165
. segments
136
166
. last ( )
@@ -139,14 +169,23 @@ fn generate_impl_calls(
139
169
140
170
for item in & impl_. items {
141
171
if let ImplItem :: Fn ( method) = item {
142
- let impl_call =
143
- generate_impl_call ( & method. sig , & impl_. self_ty , input, & impl_trait) ?;
172
+ let impl_call = generate_impl_call (
173
+ & method. sig ,
174
+ & impl_. self_ty ,
175
+ input,
176
+ & impl_trait,
177
+ & trait_api_ver,
178
+ ) ?;
179
+ let mut attrs = filter_cfg_attrs ( & impl_. attrs ) ;
180
+
181
+ // Add any `#[cfg(feature = X)]` attributes of the method to result
182
+ attrs. extend ( filter_cfg_attrs ( & method. attrs ) ) ;
144
183
145
184
impl_calls. push ( (
146
185
impl_trait_ident. clone ( ) ,
147
186
method. sig . ident . clone ( ) ,
148
187
impl_call,
149
- filter_cfg_attrs ( & impl_ . attrs ) ,
188
+ attrs,
150
189
) ) ;
151
190
}
152
191
}
@@ -441,6 +480,18 @@ fn extend_with_api_version(mut trait_: Path, version: Option<u64>) -> Path {
441
480
trait_
442
481
}
443
482
483
+ /// Adds a feature guard to `attributes`.
484
+ ///
485
+ /// Depending on `enable`, the feature guard either enables ('feature = "something"`) or disables
486
+ /// (`not(feature = "something")`).
487
+ fn add_feature_guard ( attrs : & mut Vec < Attribute > , feature_name : & str , enable : bool ) {
488
+ let attr = match enable {
489
+ true => parse_quote ! ( #[ cfg( feature = #feature_name) ] ) ,
490
+ false => parse_quote ! ( #[ cfg( not( feature = #feature_name) ) ] ) ,
491
+ } ;
492
+ attrs. push ( attr) ;
493
+ }
494
+
444
495
/// Generates the implementations of the apis for the runtime.
445
496
fn generate_api_impl_for_runtime ( impls : & [ ItemImpl ] ) -> Result < TokenStream > {
446
497
let mut impls_prepared = Vec :: new ( ) ;
@@ -451,12 +502,29 @@ fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
451
502
let trait_api_ver = extract_api_version ( & impl_. attrs , impl_. span ( ) ) ?;
452
503
453
504
let mut impl_ = impl_. clone ( ) ;
505
+ impl_. attrs = filter_cfg_attrs ( & impl_. attrs ) ;
506
+
454
507
let trait_ = extract_impl_trait ( & impl_, RequireQualifiedTraitPath :: Yes ) ?. clone ( ) ;
455
508
let trait_ = extend_with_runtime_decl_path ( trait_) ;
456
- let trait_ = extend_with_api_version ( trait_, trait_api_ver) ;
509
+ // If the trait api contains feature gated version - there are staging methods in it. Handle
510
+ // them explicitly here by adding staging implementation with `#cfg(feature = ...)` and
511
+ // stable implementation with `#[cfg(not(feature = ...))]`.
512
+ if let Some ( feature_gated) = trait_api_ver. feature_gated {
513
+ let mut feature_gated_impl = impl_. clone ( ) ;
514
+ add_feature_guard ( & mut feature_gated_impl. attrs , & feature_gated. 0 , true ) ;
515
+ feature_gated_impl. trait_ . as_mut ( ) . unwrap ( ) . 1 =
516
+ extend_with_api_version ( trait_. clone ( ) , Some ( feature_gated. 1 ) ) ;
517
+
518
+ impls_prepared. push ( feature_gated_impl) ;
519
+
520
+ // Finally add `#[cfg(not(feature = ...))]` for the stable implementation (which is
521
+ // generated outside this if).
522
+ add_feature_guard ( & mut impl_. attrs , & feature_gated. 0 , false ) ;
523
+ }
457
524
525
+ // Generate stable trait implementation.
526
+ let trait_ = extend_with_api_version ( trait_, trait_api_ver. custom ) ;
458
527
impl_. trait_ . as_mut ( ) . unwrap ( ) . 1 = trait_;
459
- impl_. attrs = filter_cfg_attrs ( & impl_. attrs ) ;
460
528
impls_prepared. push ( impl_) ;
461
529
}
462
530
@@ -663,7 +731,8 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
663
731
let c = generate_crate_access ( ) ;
664
732
665
733
for impl_ in impls {
666
- let api_ver = extract_api_version ( & impl_. attrs , impl_. span ( ) ) ?. map ( |a| a as u32 ) ;
734
+ let versions = extract_api_version ( & impl_. attrs , impl_. span ( ) ) ?;
735
+ let api_ver = versions. custom . map ( |a| a as u32 ) ;
667
736
668
737
let mut path = extend_with_runtime_decl_path (
669
738
extract_impl_trait ( impl_, RequireQualifiedTraitPath :: Yes ) ?. clone ( ) ,
@@ -687,11 +756,34 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
687
756
}
688
757
689
758
let id: Path = parse_quote ! ( #path ID ) ;
690
- let version = quote ! ( #path VERSION ) ;
691
- let attrs = filter_cfg_attrs ( & impl_. attrs ) ;
759
+ let mut attrs = filter_cfg_attrs ( & impl_. attrs ) ;
760
+
761
+ // Handle API versioning
762
+ // If feature gated version is set - handle it first
763
+ if let Some ( feature_gated) = versions. feature_gated {
764
+ let feature_gated_version = feature_gated. 1 as u32 ;
765
+ // the attributes for the feature gated staging api
766
+ let mut feature_gated_attrs = attrs. clone ( ) ;
767
+ add_feature_guard ( & mut feature_gated_attrs, & feature_gated. 0 , true ) ;
768
+ populate_runtime_api_versions (
769
+ & mut result,
770
+ & mut sections,
771
+ feature_gated_attrs,
772
+ id. clone ( ) ,
773
+ quote ! ( #feature_gated_version ) ,
774
+ & c,
775
+ ) ;
776
+
777
+ // Add `#[cfg(not(feature ...))]` to the initial attributes. If the staging feature flag
778
+ // is not set we want to set the stable api version
779
+ add_feature_guard ( & mut attrs, & feature_gated. 0 , false ) ;
780
+ }
692
781
693
- let api_ver = api_ver. map ( |a| quote ! ( #a ) ) . unwrap_or_else ( || version) ;
694
- populate_runtime_api_versions ( & mut result, & mut sections, attrs, id, api_ver, & c)
782
+ // Now add the stable api version to the versions list. If the api has got staging functions
783
+ // there might be a `#[cfg(not(feature ...))]` attribute attached to the stable version.
784
+ let base_api_version = quote ! ( #path VERSION ) ;
785
+ let api_ver = api_ver. map ( |a| quote ! ( #a ) ) . unwrap_or_else ( || base_api_version) ;
786
+ populate_runtime_api_versions ( & mut result, & mut sections, attrs, id, api_ver, & c) ;
695
787
}
696
788
697
789
Ok ( quote ! (
@@ -758,12 +850,64 @@ fn filter_cfg_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
758
850
attrs. iter ( ) . filter ( |a| a. path ( ) . is_ident ( "cfg" ) ) . cloned ( ) . collect ( )
759
851
}
760
852
853
+ /// Parse feature flagged api_version.
854
+ /// E.g. `#[cfg_attr(feature = "enable-staging-api", api_version(99))]`
855
+ fn extract_cfg_api_version ( attrs : & Vec < Attribute > , span : Span ) -> Result < Option < ( String , u64 ) > > {
856
+ let cfg_attrs = attrs. iter ( ) . filter ( |a| a. path ( ) . is_ident ( "cfg_attr" ) ) . collect :: < Vec < _ > > ( ) ;
857
+
858
+ let mut cfg_api_version_attr = Vec :: new ( ) ;
859
+ for cfg_attr in cfg_attrs {
860
+ let mut feature_name = None ;
861
+ let mut api_version = None ;
862
+ cfg_attr. parse_nested_meta ( |m| {
863
+ if m. path . is_ident ( "feature" ) {
864
+ let a = m. value ( ) ?;
865
+ let b: LitStr = a. parse ( ) ?;
866
+ feature_name = Some ( b. value ( ) ) ;
867
+ } else if m. path . is_ident ( API_VERSION_ATTRIBUTE ) {
868
+ let content;
869
+ parenthesized ! ( content in m. input) ;
870
+ let ver: LitInt = content. parse ( ) ?;
871
+ api_version = Some ( ver. base10_parse :: < u64 > ( ) ?) ;
872
+ }
873
+ Ok ( ( ) )
874
+ } ) ?;
875
+
876
+ // If there is a cfg attribute containing api_version - save if for processing
877
+ if let ( Some ( feature_name) , Some ( api_version) ) = ( feature_name, api_version) {
878
+ cfg_api_version_attr. push ( ( feature_name, api_version, cfg_attr. span ( ) ) ) ;
879
+ }
880
+ }
881
+
882
+ if cfg_api_version_attr. len ( ) > 1 {
883
+ let mut err = Error :: new ( span, format ! ( "Found multiple feature gated api versions (cfg attribute with nested `{}` attribute). This is not supported." , API_VERSION_ATTRIBUTE ) ) ;
884
+ for ( _, _, attr_span) in cfg_api_version_attr {
885
+ err. combine ( Error :: new ( attr_span, format ! ( "`{}` found here" , API_VERSION_ATTRIBUTE ) ) ) ;
886
+ }
887
+
888
+ return Err ( err)
889
+ }
890
+
891
+ Ok ( cfg_api_version_attr
892
+ . into_iter ( )
893
+ . next ( )
894
+ . map ( |( feature, name, _) | ( feature, name) ) )
895
+ }
896
+
897
+ /// Represents an API version.
898
+ struct ApiVersion {
899
+ /// Corresponds to `#[api_version(X)]` attribute.
900
+ pub custom : Option < u64 > ,
901
+ /// Corresponds to `#[cfg_attr(feature = "enable-staging-api", api_version(99))]`
902
+ /// attribute. `String` is the feature name, `u64` the staging api version.
903
+ pub feature_gated : Option < ( String , u64 ) > ,
904
+ }
905
+
761
906
// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors.
762
907
// Returns:
763
908
// - Err if the version is malformed
764
- // - Some(u64) if the version is set
765
- // - None if the version is not set (this is valid).
766
- fn extract_api_version ( attrs : & Vec < Attribute > , span : Span ) -> Result < Option < u64 > > {
909
+ // - `ApiVersion` on success. If a version is set or not is determined by the fields of `ApiVersion`
910
+ fn extract_api_version ( attrs : & Vec < Attribute > , span : Span ) -> Result < ApiVersion > {
767
911
// First fetch all `API_VERSION_ATTRIBUTE` values (should be only one)
768
912
let api_ver = attrs
769
913
. iter ( )
@@ -782,7 +926,10 @@ fn extract_api_version(attrs: &Vec<Attribute>, span: Span) -> Result<Option<u64>
782
926
}
783
927
784
928
// Parse the runtime version if there exists one.
785
- api_ver. first ( ) . map ( |v| parse_runtime_api_version ( v) ) . transpose ( )
929
+ Ok ( ApiVersion {
930
+ custom : api_ver. first ( ) . map ( |v| parse_runtime_api_version ( v) ) . transpose ( ) ?,
931
+ feature_gated : extract_cfg_api_version ( attrs, span) ?,
932
+ } )
786
933
}
787
934
788
935
#[ cfg( test) ]
0 commit comments