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