rustc_attr_parsing/attributes/
deprecation.rs1use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
2use rustc_feature::{AttributeTemplate, template};
3use rustc_span::{Span, Symbol, sym};
4
5use super::util::parse_version;
6use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
7use crate::context::{AcceptContext, Stage};
8use crate::parser::ArgParser;
9use crate::session_diagnostics;
10
11pub(crate) struct DeprecationParser;
12
13fn get<S: Stage>(
14 cx: &AcceptContext<'_, '_, S>,
15 name: Symbol,
16 param_span: Span,
17 arg: &ArgParser<'_>,
18 item: &Option<Symbol>,
19) -> Option<Symbol> {
20 if item.is_some() {
21 cx.duplicate_key(param_span, name);
22 return None;
23 }
24 if let Some(v) = arg.name_value() {
25 if let Some(value_str) = v.value_as_str() {
26 Some(value_str)
27 } else {
28 cx.expected_string_literal(v.value_span, Some(&v.value_as_lit()));
29 None
30 }
31 } else {
32 cx.expected_name_value(param_span, Some(name));
33 None
34 }
35}
36
37impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
38 const PATH: &[Symbol] = &[sym::deprecated];
39 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
40 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
41 const TEMPLATE: AttributeTemplate = template!(
42 Word,
43 List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
44 NameValueStr: "reason"
45 );
46
47 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
48 let features = cx.features();
49
50 let mut since = None;
51 let mut note = None;
52 let mut suggestion = None;
53
54 let is_rustc = features.staged_api();
55
56 match args {
57 ArgParser::NoArgs => {
58 }
60 ArgParser::List(list) => {
61 for param in list.mixed() {
62 let Some(param) = param.meta_item() else {
63 cx.unexpected_literal(param.span());
64 return None;
65 };
66
67 let ident_name = param.path().word_sym();
68
69 match ident_name {
70 Some(name @ sym::since) => {
71 since = Some(get(cx, name, param.span(), param.args(), &since)?);
72 }
73 Some(name @ sym::note) => {
74 note = Some(get(cx, name, param.span(), param.args(), ¬e)?);
75 }
76 Some(name @ sym::suggestion) => {
77 if !features.deprecated_suggestion() {
78 cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
79 span: param.span(),
80 is_nightly: cx.sess().is_nightly_build(),
81 details: (),
82 });
83 }
84
85 suggestion =
86 Some(get(cx, name, param.span(), param.args(), &suggestion)?);
87 }
88 _ => {
89 cx.unknown_key(
90 param.span(),
91 param.path().to_string(),
92 if features.deprecated_suggestion() {
93 &["since", "note", "suggestion"]
94 } else {
95 &["since", "note"]
96 },
97 );
98 return None;
99 }
100 }
101 }
102 }
103 ArgParser::NameValue(v) => {
104 let Some(value) = v.value_as_str() else {
105 cx.expected_string_literal(v.value_span, Some(v.value_as_lit()));
106 return None;
107 };
108 note = Some(value);
109 }
110 }
111
112 let since = if let Some(since) = since {
113 if since.as_str() == "TBD" {
114 DeprecatedSince::Future
115 } else if !is_rustc {
116 DeprecatedSince::NonStandard(since)
117 } else if let Some(version) = parse_version(since) {
118 DeprecatedSince::RustcVersion(version)
119 } else {
120 cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
121 DeprecatedSince::Err
122 }
123 } else if is_rustc {
124 cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
125 DeprecatedSince::Err
126 } else {
127 DeprecatedSince::Unspecified
128 };
129
130 if is_rustc && note.is_none() {
131 cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
132 return None;
133 }
134
135 Some(AttributeKind::Deprecation {
136 deprecation: Deprecation { since, note, suggestion },
137 span: cx.attr_span,
138 })
139 }
140}