rustc_attr_parsing/attributes/
deprecation.rs1use rustc_hir::attrs::{DeprecatedSince, Deprecation};
2use rustc_hir::{RustcVersion, VERSION_PLACEHOLDER};
3
4use super::prelude::*;
5use super::util::parse_version;
6use crate::session_diagnostics::{
7 DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince,
8};
9
10pub(crate) struct DeprecationParser;
11
12fn get<S: Stage>(
13 cx: &AcceptContext<'_, '_, S>,
14 name: Symbol,
15 param_span: Span,
16 arg: &ArgParser,
17 item: Option<Symbol>,
18) -> Option<Ident> {
19 if item.is_some() {
20 cx.duplicate_key(param_span, name);
21 return None;
22 }
23 if let Some(v) = arg.name_value() {
24 if let Some(value_str) = v.value_as_ident() {
25 Some(value_str)
26 } else {
27 cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
28 None
29 }
30 } else {
31 cx.expected_name_value(param_span, Some(name));
32 None
33 }
34}
35
36impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
37 const PATH: &[Symbol] = &[sym::deprecated];
38 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
39 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
40 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
41 Allow(Target::Fn),
42 Allow(Target::Mod),
43 Allow(Target::Struct),
44 Allow(Target::Enum),
45 Allow(Target::Union),
46 Allow(Target::Const),
47 Allow(Target::Static),
48 Allow(Target::MacroDef),
49 Allow(Target::Method(MethodKind::Inherent)),
50 Allow(Target::Method(MethodKind::Trait { body: false })),
51 Allow(Target::Method(MethodKind::Trait { body: true })),
52 Allow(Target::TyAlias),
53 Allow(Target::Use),
54 Allow(Target::ForeignFn),
55 Allow(Target::ForeignStatic),
56 Allow(Target::ForeignTy),
57 Allow(Target::Field),
58 Allow(Target::Trait),
59 Allow(Target::AssocTy),
60 Allow(Target::AssocConst),
61 Allow(Target::Variant),
62 Allow(Target::Impl { of_trait: false }),
63 Allow(Target::Crate),
64 Error(Target::WherePredicate),
65 ]);
66 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!(
67 Word,
68 List: &[r#"since = "version""#, r#"note = "reason""#, r#"since = "version", note = "reason""#],
69 NameValueStr: "reason"
70 );
71
72 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
73 let features = cx.features();
74
75 let mut since = None;
76 let mut note: Option<Ident> = None;
77 let mut suggestion = None;
78
79 let is_rustc = features.staged_api();
80
81 match args {
82 ArgParser::NoArgs => {
83 }
85 ArgParser::List(list) => {
86 for param in list.mixed() {
87 let Some(param) = param.meta_item() else {
88 cx.unexpected_literal(param.span());
89 return None;
90 };
91
92 let ident_name = param.path().word_sym();
93
94 match ident_name {
95 Some(name @ sym::since) => {
96 since = Some(get(cx, name, param.span(), param.args(), since)?.name);
97 }
98 Some(name @ sym::note) => {
99 note = Some(get(
100 cx,
101 name,
102 param.span(),
103 param.args(),
104 note.map(|ident| ident.name),
105 )?);
106 }
107 Some(name @ sym::suggestion) => {
108 if !features.deprecated_suggestion() {
109 cx.emit_err(DeprecatedItemSuggestion {
110 span: param.span(),
111 is_nightly: cx.sess().is_nightly_build(),
112 details: (),
113 });
114 }
115
116 suggestion =
117 Some(get(cx, name, param.span(), param.args(), suggestion)?.name);
118 }
119 _ => {
120 cx.expected_specific_argument(
121 param.span(),
122 if features.deprecated_suggestion() {
123 &[sym::since, sym::note, sym::suggestion]
124 } else {
125 &[sym::since, sym::note]
126 },
127 );
128 return None;
129 }
130 }
131 }
132 }
133 ArgParser::NameValue(v) => {
134 let Some(value) = v.value_as_ident() else {
135 cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
136 return None;
137 };
138 note = Some(value);
139 }
140 }
141
142 let since = if let Some(since) = since {
143 if since.as_str() == "TBD" {
144 DeprecatedSince::Future
145 } else if !is_rustc {
146 DeprecatedSince::NonStandard(since)
147 } else if since.as_str() == VERSION_PLACEHOLDER {
148 DeprecatedSince::RustcVersion(RustcVersion::CURRENT)
149 } else if let Some(version) = parse_version(since) {
150 DeprecatedSince::RustcVersion(version)
151 } else {
152 cx.emit_err(InvalidSince { span: cx.attr_span });
153 DeprecatedSince::Err
154 }
155 } else if is_rustc {
156 cx.emit_err(MissingSince { span: cx.attr_span });
157 DeprecatedSince::Err
158 } else {
159 DeprecatedSince::Unspecified
160 };
161
162 if is_rustc && note.is_none() {
163 cx.emit_err(MissingNote { span: cx.attr_span });
164 return None;
165 }
166
167 Some(AttributeKind::Deprecation {
168 deprecation: Deprecation { since, note, suggestion },
169 span: cx.attr_span,
170 })
171 }
172}