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