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