rustc_attr_parsing/attributes/
deprecation.rs1use rustc_ast::LitKind;
2use rustc_attr_ir::{DeprecatedSince, Deprecation, RustcVersion, VERSION_PLACEHOLDER};
3use rustc_feature::AttributeStability;
4
5use super::prelude::*;
6use super::util::parse_version;
7use crate::diagnostics::{DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince};
8
9fn get(
10 cx: &mut AcceptContext<'_, '_>,
11 name: Symbol,
12 param_span: Span,
13 arg: &ArgParser,
14 item: Option<Symbol>,
15) -> Option<Ident> {
16 if item.is_some() {
17 cx.adcx().duplicate_key(param_span, name);
18 return None;
19 }
20 let v = cx.expect_name_value(arg, param_span, Some(name))?;
21 if let Some(value_str) = v.value_as_ident() {
22 Some(value_str)
23 } else {
24 cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit()));
25 None
26 }
27}
28
29pub(crate) struct DeprecatedParser;
30impl SingleAttributeParser for DeprecatedParser {
31 const PATH: &[Symbol] = &[sym::deprecated];
32 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
33 Allow(Target::Fn),
34 Allow(Target::Mod),
35 Allow(Target::Struct),
36 Allow(Target::Enum),
37 Allow(Target::Union),
38 Allow(Target::Const),
39 Allow(Target::Static),
40 Allow(Target::MacroDef),
41 Allow(Target::Method(MethodKind::Inherent)),
42 Allow(Target::Method(MethodKind::Trait { body: false })),
43 Allow(Target::Method(MethodKind::Trait { body: true })),
44 Allow(Target::TyAlias),
45 Allow(Target::Use),
46 Allow(Target::ForeignFn),
47 Allow(Target::ForeignStatic),
48 Allow(Target::ForeignTy),
49 Allow(Target::Field),
50 Allow(Target::Trait),
51 Allow(Target::AssocTy),
52 Allow(Target::AssocConst),
53 Allow(Target::Variant),
54 Allow(Target::Impl { of_trait: false }),
55 Allow(Target::Crate),
56 Error(Target::WherePredicate),
57 ]);
58 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: true,
list: Some(&[r#"since = "version""#, r#"note = "reason""#,
r#"since = "version", note = "reason""#]),
one_of: &[],
name_value_str: Some(&["reason"]),
docs: None,
}template!(
59 Word,
60 List: &[r#"since = "version""#, r#"note = "reason""#, r#"since = "version", note = "reason""#],
61 NameValueStr: "reason"
62 );
63 const STABILITY: AttributeStability = AttributeStability::Stable;
64
65 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
66 let features = cx.features();
67
68 let mut since = None;
69 let mut note: Option<Ident> = None;
70 let mut suggestion = None;
71
72 let is_rustc = features.staged_api();
73
74 match args {
75 ArgParser::NoArgs => {
76 }
78 ArgParser::List(list) => {
79 if let Some(elem) = list.as_single()
83 && let Some(lit) = elem.as_lit()
84 && let LitKind::Str(text, _) = lit.kind
85 {
86 let mut adcx = cx.adcx();
87
88 match parse_since(text, true) {
89 DeprecatedSince::Future | DeprecatedSince::RustcVersion(_) => {
90 adcx.push_suggestion(
91 String::from("try specifying a deprecated since version"),
92 elem.span(),
93 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("since = {0}", lit.kind))
})format!("since = {}", lit.kind),
94 );
95 }
96 _ => {
97 if let Some(span) = args.span() {
98 adcx.push_suggestion(
99 String::from("try using `=` instead"),
100 span,
101 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" = {0}", lit.kind))
})format!(" = {}", lit.kind),
102 );
103 }
104 }
105 };
106
107 adcx.expected_not_literal(elem.span());
108 return None;
109 }
110
111 for param in list.mixed() {
112 let Some(param) = param.meta_item() else {
113 cx.adcx().expected_not_literal(param.span());
114 return None;
115 };
116
117 let ident_name = param.path().word_sym();
118
119 match ident_name {
120 Some(name @ sym::since) => {
121 since = Some(get(cx, name, param.span(), param.args(), since)?.name);
122 }
123 Some(name @ sym::note) => {
124 note = Some(get(
125 cx,
126 name,
127 param.span(),
128 param.args(),
129 note.map(|ident| ident.name),
130 )?);
131 }
132 Some(name @ sym::suggestion) => {
133 if !features.deprecated_suggestion() {
134 cx.emit_err(DeprecatedItemSuggestion {
135 span: param.span(),
136 is_nightly: cx.sess().is_nightly_build(),
137 details: (),
138 });
139 }
140
141 suggestion =
142 Some(get(cx, name, param.span(), param.args(), suggestion)?.name);
143 }
144 _ => {
145 cx.adcx().expected_specific_argument(
146 param.span(),
147 if features.deprecated_suggestion() {
148 &[sym::since, sym::note, sym::suggestion]
149 } else {
150 &[sym::since, sym::note]
151 },
152 );
153 return None;
154 }
155 }
156 }
157 }
158 ArgParser::NameValue(v) => {
159 let Some(value) = v.value_as_ident() else {
160 cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit()));
161 return None;
162 };
163 note = Some(value);
164 }
165 }
166
167 let since = if let Some(since) = since {
168 let since = parse_since(since, is_rustc);
169 if #[allow(non_exhaustive_omitted_patterns)] match since {
DeprecatedSince::Err => true,
_ => false,
}matches!(since, DeprecatedSince::Err) {
170 cx.emit_err(InvalidSince { span: cx.attr_span });
171 }
172 since
173 } else if is_rustc {
174 cx.emit_err(MissingSince { span: cx.attr_span });
175 DeprecatedSince::Err
176 } else {
177 DeprecatedSince::Unspecified
178 };
179
180 if is_rustc && note.is_none() {
181 cx.emit_err(MissingNote { span: cx.attr_span });
182 return None;
183 }
184
185 Some(AttributeKind::Deprecated {
186 deprecation: Deprecation { since, note, suggestion },
187 span: cx.attr_span,
188 })
189 }
190}
191
192fn parse_since(since: Symbol, is_rustc: bool) -> DeprecatedSince {
193 if since.as_str() == "TBD" {
194 DeprecatedSince::Future
195 } else if !is_rustc {
196 DeprecatedSince::NonStandard(since)
197 } else if since.as_str() == VERSION_PLACEHOLDER {
198 DeprecatedSince::RustcVersion(RustcVersion::CURRENT)
199 } else if let Some(version) = parse_version(since) {
200 DeprecatedSince::RustcVersion(version)
201 } else {
202 DeprecatedSince::Err
203 }
204}