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