Skip to main content

rustc_attr_parsing/attributes/
deprecation.rs

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