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 Error(Target::Param),
67 ]);
68 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!(
69 Word,
70 List: &[r#"since = "version""#, r#"note = "reason""#, r#"since = "version", note = "reason""#],
71 NameValueStr: "reason"
72 );
73 const STABILITY: AttributeStability = AttributeStability::Stable;
74
75 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
76 let features = cx.features();
77
78 let mut since = None;
79 let mut note: Option<Ident> = None;
80 let mut suggestion = None;
81
82 let is_rustc = features.staged_api();
83
84 match args {
85 ArgParser::NoArgs => {
86 }
88 ArgParser::List(list) => {
89 if let Some(elem) = list.as_single()
93 && let Some(lit) = elem.as_lit()
94 && let LitKind::Str(text, _) = lit.kind
95 {
96 let mut adcx = cx.adcx();
97
98 match parse_since(text, true) {
99 DeprecatedSince::Future | DeprecatedSince::RustcVersion(_) => {
100 adcx.push_suggestion(
101 String::from("try specifying a deprecated since version"),
102 elem.span(),
103 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("since = {0}", lit.kind))
})format!("since = {}", lit.kind),
104 );
105 }
106 _ => {
107 if let Some(span) = args.span() {
108 adcx.push_suggestion(
109 String::from("try using `=` instead"),
110 span,
111 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" = {0}", lit.kind))
})format!(" = {}", lit.kind),
112 );
113 }
114 }
115 };
116
117 adcx.expected_not_literal(elem.span());
118 return None;
119 }
120
121 for param in list.mixed() {
122 let Some(param) = param.meta_item() else {
123 cx.adcx().expected_not_literal(param.span());
124 return None;
125 };
126
127 let ident_name = param.path().word_sym();
128
129 match ident_name {
130 Some(name @ sym::since) => {
131 since = Some(get(cx, name, param.span(), param.args(), since)?.name);
132 }
133 Some(name @ sym::note) => {
134 note = Some(get(
135 cx,
136 name,
137 param.span(),
138 param.args(),
139 note.map(|ident| ident.name),
140 )?);
141 }
142 Some(name @ sym::suggestion) => {
143 if !features.deprecated_suggestion() {
144 cx.emit_err(DeprecatedItemSuggestion {
145 span: param.span(),
146 is_nightly: cx.sess().is_nightly_build(),
147 details: (),
148 });
149 }
150
151 suggestion =
152 Some(get(cx, name, param.span(), param.args(), suggestion)?.name);
153 }
154 _ => {
155 cx.adcx().expected_specific_argument(
156 param.span(),
157 if features.deprecated_suggestion() {
158 &[sym::since, sym::note, sym::suggestion]
159 } else {
160 &[sym::since, sym::note]
161 },
162 );
163 return None;
164 }
165 }
166 }
167 }
168 ArgParser::NameValue(v) => {
169 let Some(value) = v.value_as_ident() else {
170 cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit()));
171 return None;
172 };
173 note = Some(value);
174 }
175 }
176
177 let since = if let Some(since) = since {
178 let since = parse_since(since, is_rustc);
179 if #[allow(non_exhaustive_omitted_patterns)] match since {
DeprecatedSince::Err => true,
_ => false,
}matches!(since, DeprecatedSince::Err) {
180 cx.emit_err(InvalidSince { span: cx.attr_span });
181 }
182 since
183 } else if is_rustc {
184 cx.emit_err(MissingSince { span: cx.attr_span });
185 DeprecatedSince::Err
186 } else {
187 DeprecatedSince::Unspecified
188 };
189
190 if is_rustc && note.is_none() {
191 cx.emit_err(MissingNote { span: cx.attr_span });
192 return None;
193 }
194
195 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!(
198 cx.target,
199 Target::Method(MethodKind::TraitImpl)
200 | Target::AssocConst(AssocCtxt::Impl { of_trait: true })
201 | Target::AssocTy(AssocCtxt::Impl { of_trait: true })
202 ) {
203 let attr_span = cx.attr_span;
204 cx.emit_lint(
205 UNUSED_ATTRIBUTES,
206 DeprecatedAnnotationHasNoEffect { span: attr_span },
207 attr_span,
208 );
209 }
210
211 Some(AttributeKind::Deprecated {
212 deprecation: Deprecation { since, note, suggestion },
213 span: cx.attr_span,
214 is_inner: #[allow(non_exhaustive_omitted_patterns)] match cx.attr_style {
rustc_ast::AttrStyle::Inner => true,
_ => false,
}matches!(cx.attr_style, rustc_ast::AttrStyle::Inner),
215 })
216 }
217}
218
219fn parse_since(since: Symbol, is_rustc: bool) -> DeprecatedSince {
220 if since.as_str() == "TBD" {
221 DeprecatedSince::Future
222 } else if !is_rustc {
223 DeprecatedSince::NonStandard(since)
224 } else if since.as_str() == VERSION_PLACEHOLDER {
225 DeprecatedSince::RustcVersion(RustcVersion::CURRENT)
226 } else if let Some(version) = parse_version(since) {
227 DeprecatedSince::RustcVersion(version)
228 } else {
229 DeprecatedSince::Err
230 }
231}