use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_span::{Span, Symbol, sym}; use super::SingleAttributeParser; use super::util::parse_version; use crate::context::AcceptContext; use crate::parser::ArgParser; use crate::session_diagnostics; use crate::session_diagnostics::UnsupportedLiteralReason; pub(crate) struct DeprecationParser; fn get( cx: &AcceptContext<'_>, name: Symbol, param_span: Span, arg: &ArgParser<'_>, item: &Option, ) -> Option { if item.is_some() { cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() }); return None; } if let Some(v) = arg.name_value() { if let Some(value_str) = v.value_as_str() { Some(value_str) } else { let lit = v.value_as_lit(); cx.emit_err(session_diagnostics::UnsupportedLiteral { span: v.value_span, reason: UnsupportedLiteralReason::DeprecatedString, is_bytestr: lit.kind.is_bytestr(), start_point_span: cx.sess().source_map().start_point(lit.span), }); None } } else { // FIXME(jdonszelmann): suggestion? cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None }); None } } impl SingleAttributeParser for DeprecationParser { const PATH: &'static [Symbol] = &[sym::deprecated]; fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) { // FIXME(jdonszelmann): merge with errors from check_attrs.rs cx.emit_err(session_diagnostics::UnusedMultiple { this: cx.attr_span, other: first_span, name: sym::deprecated, }); } fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option { let features = cx.features(); let mut since = None; let mut note = None; let mut suggestion = None; let is_rustc = features.staged_api(); if let Some(value) = args.name_value() && let Some(value_str) = value.value_as_str() { note = Some(value_str) } else if let Some(list) = args.list() { for param in list.mixed() { let param_span = param.span(); let Some(param) = param.meta_item() else { cx.emit_err(session_diagnostics::UnsupportedLiteral { span: param_span, reason: UnsupportedLiteralReason::DeprecatedKvPair, is_bytestr: false, start_point_span: cx.sess().source_map().start_point(param_span), }); return None; }; let ident_name = param.path_without_args().word_sym(); match ident_name { Some(name @ sym::since) => { since = Some(get(cx, name, param_span, param.args(), &since)?); } Some(name @ sym::note) => { note = Some(get(cx, name, param_span, param.args(), ¬e)?); } Some(name @ sym::suggestion) => { if !features.deprecated_suggestion() { cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { span: param_span, is_nightly: cx.sess().is_nightly_build(), details: (), }); } suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?); } _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param_span, item: param.path_without_args().to_string(), expected: if features.deprecated_suggestion() { &["since", "note", "suggestion"] } else { &["since", "note"] }, }); return None; } } } } let since = if let Some(since) = since { if since.as_str() == "TBD" { DeprecatedSince::Future } else if !is_rustc { DeprecatedSince::NonStandard(since) } else if let Some(version) = parse_version(since) { DeprecatedSince::RustcVersion(version) } else { cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); DeprecatedSince::Err } } else if is_rustc { cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); DeprecatedSince::Err } else { DeprecatedSince::Unspecified }; if is_rustc && note.is_none() { cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span }); return None; } Some(AttributeKind::Deprecation { deprecation: Deprecation { since, note, suggestion }, span: cx.attr_span, }) } }