Suggest using since field when malformed deprecated attribute value is version shaped
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index fd8616c..e0adbb0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -1,3 +1,4 @@ +use rustc_ast::LitKind; use rustc_hir::attrs::{DeprecatedSince, Deprecation}; use rustc_hir::{RustcVersion, VERSION_PLACEHOLDER}; @@ -76,12 +77,34 @@ fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<Attribute // ok } ArgParser::List(list) => { - // If the argument list contains a single string literal, suggest using NameValue syntax - if let Some(elem) = list.as_single() && let Some(lit) = elem.as_lit() && lit.kind.is_str() { + // If the argument list contains a single string literal: + // check whether it may be a version and suggest since field + // otherwise, suggest using NameValue syntax + if let Some(elem) = list.as_single() + && let Some(lit) = elem.as_lit() + && let LitKind::Str(text, _) = lit.kind + { let mut adcx = cx.adcx(); - if let Some(span) = args.span() { - adcx.push_suggestion(String::from("try using `=` instead"), span, format!(" = {}", lit.kind)); - } + + match parse_since(text, true) { + DeprecatedSince::Future | DeprecatedSince::RustcVersion(_) => { + adcx.push_suggestion( + String::from("try specifying a deprecated since version"), + elem.span(), + format!("since = {}", lit.kind), + ); + } + _ => { + if let Some(span) = args.span() { + adcx.push_suggestion( + String::from("try using `=` instead"), + span, + format!(" = {}", lit.kind), + ); + } + } + }; + adcx.expected_not_literal(elem.span()); return None; } @@ -143,18 +166,11 @@ fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<Attribute } let since = if let Some(since) = since { - if since.as_str() == "TBD" { - DeprecatedSince::Future - } else if !is_rustc { - DeprecatedSince::NonStandard(since) - } else if since.as_str() == VERSION_PLACEHOLDER { - DeprecatedSince::RustcVersion(RustcVersion::CURRENT) - } else if let Some(version) = parse_version(since) { - DeprecatedSince::RustcVersion(version) - } else { + let since = parse_since(since, is_rustc); + if matches!(since, DeprecatedSince::Err) { cx.emit_err(InvalidSince { span: cx.attr_span }); - DeprecatedSince::Err } + since } else if is_rustc { cx.emit_err(MissingSince { span: cx.attr_span }); DeprecatedSince::Err @@ -173,3 +189,17 @@ fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<Attribute }) } } + +fn parse_since(since: Symbol, is_rustc: bool) -> DeprecatedSince { + if since.as_str() == "TBD" { + DeprecatedSince::Future + } else if !is_rustc { + DeprecatedSince::NonStandard(since) + } else if since.as_str() == VERSION_PLACEHOLDER { + DeprecatedSince::RustcVersion(RustcVersion::CURRENT) + } else if let Some(version) = parse_version(since) { + DeprecatedSince::RustcVersion(version) + } else { + DeprecatedSince::Err + } +}
diff --git a/tests/ui/deprecation/deprecation-sanity.rs b/tests/ui/deprecation/deprecation-sanity.rs index d1061dc..0ad4911 100644 --- a/tests/ui/deprecation/deprecation-sanity.rs +++ b/tests/ui/deprecation/deprecation-sanity.rs
@@ -23,6 +23,9 @@ fn f7() { } #[deprecated("test")] //~ ERROR malformed `deprecated` attribute input [E0565] fn f8() { } + + #[deprecated("1.2.3")] //~ ERROR malformed `deprecated` attribute input [E0565] + fn f9() { } } #[deprecated(since = "a", note = "b")]
diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index d0e5a8a..2e00b2e 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr
@@ -62,20 +62,33 @@ LL + #[deprecated = "test"] | +error[E0565]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:27:5 + | +LL | #[deprecated("1.2.3")] + | ^^^^^^^^^^^^^-------^^ + | | + | didn't expect a literal here + | +help: try specifying a deprecated since version + | +LL | #[deprecated(since = "1.2.3")] + | +++++++ + error: multiple `deprecated` attributes - --> $DIR/deprecation-sanity.rs:29:1 + --> $DIR/deprecation-sanity.rs:32:1 | LL | #[deprecated(since = "a", note = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/deprecation-sanity.rs:28:1 + --> $DIR/deprecation-sanity.rs:31:1 | LL | #[deprecated(since = "a", note = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0538]: malformed `deprecated` attribute input - --> $DIR/deprecation-sanity.rs:32:1 + --> $DIR/deprecation-sanity.rs:35:1 | LL | #[deprecated(since = "a", since = "b", note = "c")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^ @@ -83,7 +96,7 @@ | found `since` used as a key more than once error: `#[deprecated]` attribute cannot be used on trait impl blocks - --> $DIR/deprecation-sanity.rs:37:1 + --> $DIR/deprecation-sanity.rs:40:1 | LL | #[deprecated = "hello"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +105,7 @@ = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements = note: `#[deny(useless_deprecated)]` on by default -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors Some errors have detailed explanations: E0538, E0539, E0565. For more information about an error, try `rustc --explain E0538`.