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