frame_support_procedural/
deprecation.rs1use proc_macro2::TokenStream;
19use quote::quote;
20use syn::{
21 punctuated::Punctuated, spanned::Spanned, Error, Expr, ExprLit, Lit, Meta, MetaNameValue,
22 Result, Token, Variant,
23};
24
25fn deprecation_msg_formatter(msg: &str) -> String {
26 format!(
27 r#"{msg}
28 help: the following are the possible correct uses
29|
30| #[deprecated = "reason"]
31|
32| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
33|
34| #[deprecated]
35|"#
36 )
37}
38
39fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result<TokenStream> {
40 match &attr.meta {
41 Meta::List(meta_list) => {
42 let parsed = meta_list
43 .parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)
44 .map_err(|e| Error::new(attr.span(), e.to_string()))?;
45 let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| {
46 let value = match &item.value {
47 Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit),
48 _ => Err(Error::new(
49 attr.span(),
50 deprecation_msg_formatter(
51 "Invalid deprecation attribute: expected string literal",
52 ),
53 )),
54 }?;
55 if item.path.is_ident("note") {
56 acc.0.replace(value);
57 } else if item.path.is_ident("since") {
58 acc.1.replace(value);
59 }
60 Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc)
61 })?;
62 note.map_or_else(
63 || Err(Error::new(attr.span(), deprecation_msg_formatter("Invalid deprecation attribute: missing `note`"))),
64 |note| {
65 let since = if let Some(str) = since {
66 quote! { Some(#str) }
67 } else {
68 quote! { None }
69 };
70 let doc = quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::Deprecated { note: #note, since: #since }};
71 Ok(doc)
72 },
73 )
74 },
75 Meta::NameValue(MetaNameValue {
76 value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }),
77 ..
78 }) => {
79 let doc = quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::Deprecated { note: #lit, since: None } };
81 Ok(doc)
82 },
83 Meta::Path(_) => {
84 Ok(
86 quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote },
87 )
88 },
89 _ => Err(Error::new(
90 attr.span(),
91 deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"),
92 )),
93 }
94}
95
96pub fn get_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result<TokenStream> {
98 parse_deprecation(path, attrs).map(|item| {
99 item.unwrap_or_else(|| {
100 quote! {#path::__private::metadata_ir::DeprecationStatusIR::NotDeprecated}
101 })
102 })
103}
104
105fn parse_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result<Option<TokenStream>> {
106 attrs
107 .iter()
108 .find(|a| a.path().is_ident("deprecated"))
109 .map(|a| parse_deprecated_meta(path, a))
110 .transpose()
111}
112
113pub fn get_deprecation_enum<'a>(
115 path: &TokenStream,
116 parent_attrs: &[syn::Attribute],
117 children_attrs: impl Iterator<Item = (u8, &'a [syn::Attribute])>,
118) -> Result<TokenStream> {
119 let parent_deprecation = parse_deprecation(path, parent_attrs)?;
120
121 let children = children_attrs
122 .filter_map(|(key, attributes)| {
123 let key = quote::quote! { #path::__private::codec::Compact(#key as u8) };
124 let deprecation_status = parse_deprecation(path, attributes).transpose();
125 deprecation_status.map(|item| item.map(|item| quote::quote! { (#key, #item) }))
126 })
127 .collect::<Result<Vec<TokenStream>>>()?;
128 match (parent_deprecation, children.as_slice()) {
129 (None, []) =>
130 Ok(quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::NotDeprecated }),
131 (None, _) => {
132 let children = quote::quote! { #path::__private::scale_info::prelude::collections::BTreeMap::from([#( #children),*]) };
133 Ok(
134 quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::VariantsDeprecated(#children) },
135 )
136 },
137 (Some(depr), _) => Ok(
138 quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::ItemDeprecated(#depr) },
139 ),
140 }
141}
142
143pub fn variant_index_for_deprecation(index: u8, item: &Variant) -> u8 {
149 let index: u8 =
150 if let Some((_, Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }))) = &item.discriminant {
151 num_lit.base10_parse::<u8>().unwrap_or(index as u8)
152 } else {
153 index as u8
154 };
155
156 item.attrs
157 .iter()
158 .find(|attr| attr.path().is_ident("codec"))
159 .and_then(|attr| {
160 if let Meta::List(meta_list) = &attr.meta {
161 meta_list
162 .parse_args_with(Punctuated::<MetaNameValue, syn::Token![,]>::parse_terminated)
163 .ok()
164 } else {
165 None
166 }
167 })
168 .and_then(|parsed| {
169 parsed.iter().fold(None, |mut acc, item| {
170 if let Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }) = &item.value {
171 num_lit.base10_parse::<u8>().iter().for_each(|val| {
172 if item.path.is_ident("index") {
173 acc.replace(*val);
174 }
175 })
176 };
177 acc
178 })
179 })
180 .unwrap_or(index)
181}