Skip to main content

rustc_attr_parsing/attributes/
deprecation.rs

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