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