Skip to main content

rustc_attr_parsing/attributes/
deprecation.rs

1use rustc_ast::LitKind;
2use rustc_attr_ir::{DeprecatedSince, Deprecation, RustcVersion, VERSION_PLACEHOLDER};
3use rustc_feature::AttributeStability;
4
5use super::prelude::*;
6use super::util::parse_version;
7use crate::diagnostics::{DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince};
8
9fn get(
10    cx: &mut AcceptContext<'_, '_>,
11    name: Symbol,
12    param_span: Span,
13    arg: &ArgParser,
14    item: Option<Symbol>,
15) -> Option<Ident> {
16    if item.is_some() {
17        cx.adcx().duplicate_key(param_span, name);
18        return None;
19    }
20    let v = cx.expect_name_value(arg, param_span, Some(name))?;
21    if let Some(value_str) = v.value_as_ident() {
22        Some(value_str)
23    } else {
24        cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit()));
25        None
26    }
27}
28
29pub(crate) struct DeprecatedParser;
30impl SingleAttributeParser for DeprecatedParser {
31    const PATH: &[Symbol] = &[sym::deprecated];
32    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
33        Allow(Target::Fn),
34        Allow(Target::Mod),
35        Allow(Target::Struct),
36        Allow(Target::Enum),
37        Allow(Target::Union),
38        Allow(Target::Const),
39        Allow(Target::Static),
40        Allow(Target::MacroDef),
41        Allow(Target::Method(MethodKind::Inherent)),
42        Allow(Target::Method(MethodKind::Trait { body: false })),
43        Allow(Target::Method(MethodKind::Trait { body: true })),
44        Allow(Target::TyAlias),
45        Allow(Target::Use),
46        Allow(Target::ForeignFn),
47        Allow(Target::ForeignStatic),
48        Allow(Target::ForeignTy),
49        Allow(Target::Field),
50        Allow(Target::Trait),
51        Allow(Target::AssocTy),
52        Allow(Target::AssocConst),
53        Allow(Target::Variant),
54        Allow(Target::Impl { of_trait: false }),
55        Allow(Target::Crate),
56        Error(Target::WherePredicate),
57    ]);
58    const TEMPLATE: AttributeTemplate = crate::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!(
59        Word,
60        List: &[r#"since = "version""#, r#"note = "reason""#, r#"since = "version", note = "reason""#],
61        NameValueStr: "reason"
62    );
63    const STABILITY: AttributeStability = AttributeStability::Stable;
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                // If the argument list contains a single string literal:
80                // check whether it may be a version and suggest since field
81                // otherwise, suggest using NameValue syntax
82                if let Some(elem) = list.as_single()
83                    && let Some(lit) = elem.as_lit()
84                    && let LitKind::Str(text, _) = lit.kind
85                {
86                    let mut adcx = cx.adcx();
87
88                    match parse_since(text, true) {
89                        DeprecatedSince::Future | DeprecatedSince::RustcVersion(_) => {
90                            adcx.push_suggestion(
91                                String::from("try specifying a deprecated since version"),
92                                elem.span(),
93                                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("since = {0}", lit.kind))
    })format!("since = {}", lit.kind),
94                            );
95                        }
96                        _ => {
97                            if let Some(span) = args.span() {
98                                adcx.push_suggestion(
99                                    String::from("try using `=` instead"),
100                                    span,
101                                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" = {0}", lit.kind))
    })format!(" = {}", lit.kind),
102                                );
103                            }
104                        }
105                    };
106
107                    adcx.expected_not_literal(elem.span());
108                    return None;
109                }
110
111                for param in list.mixed() {
112                    let Some(param) = param.meta_item() else {
113                        cx.adcx().expected_not_literal(param.span());
114                        return None;
115                    };
116
117                    let ident_name = param.path().word_sym();
118
119                    match ident_name {
120                        Some(name @ sym::since) => {
121                            since = Some(get(cx, name, param.span(), param.args(), since)?.name);
122                        }
123                        Some(name @ sym::note) => {
124                            note = Some(get(
125                                cx,
126                                name,
127                                param.span(),
128                                param.args(),
129                                note.map(|ident| ident.name),
130                            )?);
131                        }
132                        Some(name @ sym::suggestion) => {
133                            if !features.deprecated_suggestion() {
134                                cx.emit_err(DeprecatedItemSuggestion {
135                                    span: param.span(),
136                                    is_nightly: cx.sess().is_nightly_build(),
137                                    details: (),
138                                });
139                            }
140
141                            suggestion =
142                                Some(get(cx, name, param.span(), param.args(), suggestion)?.name);
143                        }
144                        _ => {
145                            cx.adcx().expected_specific_argument(
146                                param.span(),
147                                if features.deprecated_suggestion() {
148                                    &[sym::since, sym::note, sym::suggestion]
149                                } else {
150                                    &[sym::since, sym::note]
151                                },
152                            );
153                            return None;
154                        }
155                    }
156                }
157            }
158            ArgParser::NameValue(v) => {
159                let Some(value) = v.value_as_ident() else {
160                    cx.adcx().expected_string_literal(v.value_span, Some(v.value_as_lit()));
161                    return None;
162                };
163                note = Some(value);
164            }
165        }
166
167        let since = if let Some(since) = since {
168            let since = parse_since(since, is_rustc);
169            if #[allow(non_exhaustive_omitted_patterns)] match since {
    DeprecatedSince::Err => true,
    _ => false,
}matches!(since, DeprecatedSince::Err) {
170                cx.emit_err(InvalidSince { span: cx.attr_span });
171            }
172            since
173        } else if is_rustc {
174            cx.emit_err(MissingSince { span: cx.attr_span });
175            DeprecatedSince::Err
176        } else {
177            DeprecatedSince::Unspecified
178        };
179
180        if is_rustc && note.is_none() {
181            cx.emit_err(MissingNote { span: cx.attr_span });
182            return None;
183        }
184
185        Some(AttributeKind::Deprecated {
186            deprecation: Deprecation { since, note, suggestion },
187            span: cx.attr_span,
188        })
189    }
190}
191
192fn parse_since(since: Symbol, is_rustc: bool) -> DeprecatedSince {
193    if since.as_str() == "TBD" {
194        DeprecatedSince::Future
195    } else if !is_rustc {
196        DeprecatedSince::NonStandard(since)
197    } else if since.as_str() == VERSION_PLACEHOLDER {
198        DeprecatedSince::RustcVersion(RustcVersion::CURRENT)
199    } else if let Some(version) = parse_version(since) {
200        DeprecatedSince::RustcVersion(version)
201    } else {
202        DeprecatedSince::Err
203    }
204}