diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 3b6dd7a67ed17..e0adbb0900deb 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,6 +77,38 @@ impl SingleAttributeParser for DeprecatedParser { // ok } ArgParser::List(list) => { + // 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(); + + 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; + } + for param in list.mixed() { let Some(param) = param.meta_item() else { cx.adcx().expected_not_literal(param.span()); @@ -133,18 +166,11 @@ impl SingleAttributeParser for DeprecatedParser { } 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 @@ -163,3 +189,17 @@ impl SingleAttributeParser for DeprecatedParser { }) } } + +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 d1061dc1e170b..0ad491186cc38 100644 --- a/tests/ui/deprecation/deprecation-sanity.rs +++ b/tests/ui/deprecation/deprecation-sanity.rs @@ -23,6 +23,9 @@ mod bogus_attribute_types_1 { #[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 427d14d89c19f..2e00b2efea5c5 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -55,21 +55,40 @@ LL | #[deprecated("test")] | ^^^^^^^^^^^^^------^^ | | | didn't expect a literal here + | +help: try using `=` instead + | +LL - #[deprecated("test")] +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")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^ @@ -77,7 +96,7 @@ LL | #[deprecated(since = "a", since = "b", note = "c")] | 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"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -86,7 +105,7 @@ LL | #[deprecated = "hello"] = 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`. diff --git a/tests/ui/error-codes/E0565-1.stderr b/tests/ui/error-codes/E0565-1.stderr index d1aff042e8fb6..0c500d2cbf231 100644 --- a/tests/ui/error-codes/E0565-1.stderr +++ b/tests/ui/error-codes/E0565-1.stderr @@ -5,6 +5,12 @@ LL | #[deprecated("since")] | ^^^^^^^^^^^^^-------^^ | | | didn't expect a literal here + | +help: try using `=` instead + | +LL - #[deprecated("since")] +LL + #[deprecated = "since"] + | error: aborting due to 1 previous error