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
39#[derive(Copy, Clone, PartialEq)]
44enum DeprecationTarget {
45 Item,
46 Variant,
47}
48
49fn parse_deprecated_meta(
50 crate_: &TokenStream,
51 attr: &syn::Attribute,
52 target: DeprecationTarget,
53) -> Result<TokenStream> {
54 let target = match target {
55 DeprecationTarget::Item =>
56 quote! { #crate_::__private::metadata_ir::ItemDeprecationInfoIR },
57 DeprecationTarget::Variant =>
58 quote! { #crate_::__private::metadata_ir::VariantDeprecationInfoIR },
59 };
60
61 match &attr.meta {
62 Meta::List(meta_list) => {
63 let parsed = meta_list
64 .parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)
65 .map_err(|e| Error::new(attr.span(), e.to_string()))?;
66 let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| {
67 let value = match &item.value {
68 Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit),
69 _ => Err(Error::new(
70 attr.span(),
71 deprecation_msg_formatter(
72 "Invalid deprecation attribute: expected string literal",
73 ),
74 )),
75 }?;
76 if item.path.is_ident("note") {
77 acc.0.replace(value);
78 } else if item.path.is_ident("since") {
79 acc.1.replace(value);
80 }
81 Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc)
82 })?;
83 note.map_or_else(
84 || {
85 Err(Error::new(
86 attr.span(),
87 deprecation_msg_formatter("Invalid deprecation attribute: missing `note`"),
88 ))
89 },
90 |note| {
91 let since = if let Some(str) = since {
92 quote! { Some(#str) }
93 } else {
94 quote! { None }
95 };
96 let doc = quote! { #target::Deprecated { note: #note, since: #since }};
97 Ok(doc)
98 },
99 )
100 },
101 Meta::NameValue(MetaNameValue {
102 value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }),
103 ..
104 }) => {
105 let doc = quote! { #target::Deprecated { note: #lit, since: None } };
107 Ok(doc)
108 },
109 Meta::Path(_) => {
110 Ok(quote! { #target::DeprecatedWithoutNote })
112 },
113 _ => Err(Error::new(
114 attr.span(),
115 deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"),
116 )),
117 }
118}
119
120fn find_deprecation_attr(attrs: &[syn::Attribute]) -> Option<&syn::Attribute> {
121 attrs.iter().find(|a| a.path().is_ident("deprecated"))
122}
123
124fn parse_deprecation(
125 path: &TokenStream,
126 attrs: &[syn::Attribute],
127 target: DeprecationTarget,
128) -> Result<Option<TokenStream>> {
129 find_deprecation_attr(attrs)
130 .map(|a| parse_deprecated_meta(path, a, target))
131 .transpose()
132}
133
134pub fn get_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result<TokenStream> {
136 parse_deprecation(path, attrs, DeprecationTarget::Item).map(|item| {
137 item.unwrap_or_else(|| {
138 quote! {#path::__private::metadata_ir::ItemDeprecationInfoIR::NotDeprecated}
139 })
140 })
141}
142
143pub fn prevent_deprecation_attr_on_outer_enum(parent_attrs: &[syn::Attribute]) -> Result<()> {
145 if let Some(attr) = find_deprecation_attr(parent_attrs) {
146 return Err(Error::new(
147 attr.span(),
148 "The `#[deprecated]` attribute should be applied to individual variants, not the enum as a whole.",
149 ));
150 }
151 Ok(())
152}
153
154pub fn get_deprecation_enum<'a>(
156 path: &TokenStream,
157 children_attrs: impl Iterator<Item = (u8, &'a [syn::Attribute])>,
158) -> Result<TokenStream> {
159 let children = children_attrs
160 .filter_map(|(key, attributes)| {
161 let deprecation_status =
162 parse_deprecation(path, attributes, DeprecationTarget::Variant).transpose();
163 deprecation_status.map(|item| item.map(|item| quote::quote! { (#key, #item) }))
164 })
165 .collect::<Result<Vec<TokenStream>>>()?;
166
167 if children.is_empty() {
168 Ok(
169 quote::quote! { #path::__private::metadata_ir::EnumDeprecationInfoIR::nothing_deprecated() },
170 )
171 } else {
172 let children = quote::quote! { #path::__private::scale_info::prelude::collections::BTreeMap::from([#( #children),*]) };
173 Ok(quote::quote! { #path::__private::metadata_ir::EnumDeprecationInfoIR(#children) })
174 }
175}
176
177pub fn variant_index_for_deprecation(index: u8, item: &Variant) -> u8 {
183 let index: u8 =
184 if let Some((_, Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }))) = &item.discriminant {
185 num_lit.base10_parse::<u8>().unwrap_or(index as u8)
186 } else {
187 index as u8
188 };
189
190 item.attrs
191 .iter()
192 .find(|attr| attr.path().is_ident("codec"))
193 .and_then(|attr| {
194 if let Meta::List(meta_list) = &attr.meta {
195 meta_list
196 .parse_args_with(Punctuated::<MetaNameValue, syn::Token![,]>::parse_terminated)
197 .ok()
198 } else {
199 None
200 }
201 })
202 .and_then(|parsed| {
203 parsed.iter().fold(None, |mut acc, item| {
204 if let Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }) = &item.value {
205 num_lit.base10_parse::<u8>().iter().for_each(|val| {
206 if item.path.is_ident("index") {
207 acc.replace(*val);
208 }
209 })
210 };
211 acc
212 })
213 })
214 .unwrap_or(index)
215}
216
217pub fn extract_or_return_allow_attrs(
222 items: &[syn::Attribute],
223) -> impl Iterator<Item = syn::Attribute> + '_ {
224 items.iter().filter_map(|attr| {
225 attr.path().is_ident("allow").then(|| attr.clone()).or_else(|| {
226 attr.path().is_ident("deprecated").then(|| {
227 syn::parse_quote! {
228 #[allow(deprecated)]
229 }
230 })
231 })
232 })
233}