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