rustc_attr_parsing/attributes/
deprecation.rs

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