rustc_attr_parsing/attributes/
deprecation.rs

1use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
2use rustc_span::{Span, Symbol, sym};
3
4use super::SingleAttributeParser;
5use super::util::parse_version;
6use crate::context::AcceptContext;
7use crate::parser::ArgParser;
8use crate::session_diagnostics;
9use crate::session_diagnostics::UnsupportedLiteralReason;
10
11pub(crate) struct DeprecationParser;
12
13fn get(
14    cx: &AcceptContext<'_>,
15    name: Symbol,
16    param_span: Span,
17    arg: &ArgParser<'_>,
18    item: &Option<Symbol>,
19) -> Option<Symbol> {
20    if item.is_some() {
21        cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() });
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            let lit = v.value_as_lit();
29            cx.emit_err(session_diagnostics::UnsupportedLiteral {
30                span: v.value_span,
31                reason: UnsupportedLiteralReason::DeprecatedString,
32                is_bytestr: lit.kind.is_bytestr(),
33                start_point_span: cx.sess().source_map().start_point(lit.span),
34            });
35            None
36        }
37    } else {
38        // FIXME(jdonszelmann): suggestion?
39        cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
40        None
41    }
42}
43
44impl SingleAttributeParser for DeprecationParser {
45    const PATH: &'static [Symbol] = &[sym::deprecated];
46
47    fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) {
48        // FIXME(jdonszelmann): merge with errors from check_attrs.rs
49        cx.emit_err(session_diagnostics::UnusedMultiple {
50            this: cx.attr_span,
51            other: first_span,
52            name: sym::deprecated,
53        });
54    }
55
56    fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
57        let features = cx.features();
58
59        let mut since = None;
60        let mut note = None;
61        let mut suggestion = None;
62
63        let is_rustc = features.staged_api();
64
65        if let Some(value) = args.name_value()
66            && let Some(value_str) = value.value_as_str()
67        {
68            note = Some(value_str)
69        } else if let Some(list) = args.list() {
70            for param in list.mixed() {
71                let param_span = param.span();
72                let Some(param) = param.meta_item() else {
73                    cx.emit_err(session_diagnostics::UnsupportedLiteral {
74                        span: param_span,
75                        reason: UnsupportedLiteralReason::DeprecatedKvPair,
76                        is_bytestr: false,
77                        start_point_span: cx.sess().source_map().start_point(param_span),
78                    });
79                    return None;
80                };
81
82                let ident_name = param.path_without_args().word_sym();
83
84                match ident_name {
85                    Some(name @ sym::since) => {
86                        since = Some(get(cx, name, param_span, param.args(), &since)?);
87                    }
88                    Some(name @ sym::note) => {
89                        note = Some(get(cx, name, param_span, param.args(), &note)?);
90                    }
91                    Some(name @ sym::suggestion) => {
92                        if !features.deprecated_suggestion() {
93                            cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
94                                span: param_span,
95                                is_nightly: cx.sess().is_nightly_build(),
96                                details: (),
97                            });
98                        }
99
100                        suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?);
101                    }
102                    _ => {
103                        cx.emit_err(session_diagnostics::UnknownMetaItem {
104                            span: param_span,
105                            item: param.path_without_args().to_string(),
106                            expected: if features.deprecated_suggestion() {
107                                &["since", "note", "suggestion"]
108                            } else {
109                                &["since", "note"]
110                            },
111                        });
112                        return None;
113                    }
114                }
115            }
116        }
117
118        let since = if let Some(since) = since {
119            if since.as_str() == "TBD" {
120                DeprecatedSince::Future
121            } else if !is_rustc {
122                DeprecatedSince::NonStandard(since)
123            } else if let Some(version) = parse_version(since) {
124                DeprecatedSince::RustcVersion(version)
125            } else {
126                cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
127                DeprecatedSince::Err
128            }
129        } else if is_rustc {
130            cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
131            DeprecatedSince::Err
132        } else {
133            DeprecatedSince::Unspecified
134        };
135
136        if is_rustc && note.is_none() {
137            cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
138            return None;
139        }
140
141        Some(AttributeKind::Deprecation {
142            deprecation: Deprecation { since, note, suggestion },
143            span: cx.attr_span,
144        })
145    }
146}