rustc_attr_parsing/attributes/
deprecation.rs

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