@@ -31,10 +31,11 @@ use quote::quote;
3131
3232use syn:: {
3333 fold:: { self , Fold } ,
34+ parenthesized,
3435 parse:: { Error , Parse , ParseStream , Result } ,
3536 parse_macro_input, parse_quote,
3637 spanned:: Spanned ,
37- Attribute , Ident , ImplItem , ItemImpl , Path , Signature , Type , TypePath ,
38+ Attribute , Ident , ImplItem , ItemImpl , LitInt , LitStr , Path , Signature , Type , TypePath ,
3839} ;
3940
4041use std:: collections:: HashSet ;
@@ -67,6 +68,7 @@ fn generate_impl_call(
6768 runtime : & Type ,
6869 input : & Ident ,
6970 impl_trait : & Path ,
71+ api_version : & ApiVersion ,
7072) -> Result < TokenStream > {
7173 let params =
7274 extract_parameter_names_types_and_borrows ( signature, AllowSelfRefInParameters :: No ) ?;
@@ -111,11 +113,40 @@ fn generate_impl_call(
111113 )
112114 } ;
113115
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+
114146 Ok ( quote ! (
115147 #decode_params
116148
117- #[ allow( deprecated) ]
118- <#runtime as #impl_trait>:: #fn_name( #( #pborrow #pnames2 ) , * )
149+ #fn_calls
119150 ) )
120151}
121152
@@ -130,7 +161,6 @@ fn generate_impl_calls(
130161 let trait_api_ver = extract_api_version ( & impl_. attrs , impl_. span ( ) ) ?;
131162 let impl_trait_path = extract_impl_trait ( impl_, RequireQualifiedTraitPath :: Yes ) ?;
132163 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) ;
134164 let impl_trait_ident = & impl_trait_path
135165 . segments
136166 . last ( )
@@ -139,14 +169,23 @@ fn generate_impl_calls(
139169
140170 for item in & impl_. items {
141171 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 ) ) ;
144183
145184 impl_calls. push ( (
146185 impl_trait_ident. clone ( ) ,
147186 method. sig . ident . clone ( ) ,
148187 impl_call,
149- filter_cfg_attrs ( & impl_ . attrs ) ,
188+ attrs,
150189 ) ) ;
151190 }
152191 }
@@ -441,6 +480,18 @@ fn extend_with_api_version(mut trait_: Path, version: Option<u64>) -> Path {
441480 trait_
442481}
443482
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+
444495/// Generates the implementations of the apis for the runtime.
445496fn generate_api_impl_for_runtime ( impls : & [ ItemImpl ] ) -> Result < TokenStream > {
446497 let mut impls_prepared = Vec :: new ( ) ;
@@ -451,12 +502,29 @@ fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
451502 let trait_api_ver = extract_api_version ( & impl_. attrs , impl_. span ( ) ) ?;
452503
453504 let mut impl_ = impl_. clone ( ) ;
505+ impl_. attrs = filter_cfg_attrs ( & impl_. attrs ) ;
506+
454507 let trait_ = extract_impl_trait ( & impl_, RequireQualifiedTraitPath :: Yes ) ?. clone ( ) ;
455508 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+ }
457524
525+ // Generate stable trait implementation.
526+ let trait_ = extend_with_api_version ( trait_, trait_api_ver. custom ) ;
458527 impl_. trait_ . as_mut ( ) . unwrap ( ) . 1 = trait_;
459- impl_. attrs = filter_cfg_attrs ( & impl_. attrs ) ;
460528 impls_prepared. push ( impl_) ;
461529 }
462530
@@ -663,7 +731,8 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
663731 let c = generate_crate_access ( ) ;
664732
665733 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 ) ;
667736
668737 let mut path = extend_with_runtime_decl_path (
669738 extract_impl_trait ( impl_, RequireQualifiedTraitPath :: Yes ) ?. clone ( ) ,
@@ -687,11 +756,34 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
687756 }
688757
689758 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+ }
692781
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) ;
695787 }
696788
697789 Ok ( quote ! (
@@ -758,12 +850,64 @@ fn filter_cfg_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
758850 attrs. iter ( ) . filter ( |a| a. path ( ) . is_ident ( "cfg" ) ) . cloned ( ) . collect ( )
759851}
760852
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+
761906// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors.
762907// Returns:
763908// - 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 > {
767911 // First fetch all `API_VERSION_ATTRIBUTE` values (should be only one)
768912 let api_ver = attrs
769913 . iter ( )
@@ -782,7 +926,10 @@ fn extract_api_version(attrs: &Vec<Attribute>, span: Span) -> Result<Option<u64>
782926 }
783927
784928 // 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+ } )
786933}
787934
788935#[ cfg( test) ]
0 commit comments