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