rustc_attr_parsing/attributes/
deprecation.rs

1use 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                // ok
59            }
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(), &note)?);
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}