From a1d4ad104f0a1b7cda7a07973faa185364f5105e Mon Sep 17 00:00:00 2001 From: Aria Givens Date: Thu, 28 May 2026 12:56:58 -0400 Subject: [PATCH 1/2] Suggest using NameValue syntax for malformed deprecated attribute --- .../rustc_attr_parsing/src/attributes/deprecation.rs | 10 ++++++++++ tests/ui/deprecation/deprecation-sanity.stderr | 6 ++++++ tests/ui/error-codes/E0565-1.stderr | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 3b6dd7a67ed17..fd8616c4e9ea0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -76,6 +76,16 @@ impl SingleAttributeParser for DeprecatedParser { // 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() { + let mut adcx = cx.adcx(); + 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()); diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index 427d14d89c19f..d0e5a8a3ed4a5 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -55,6 +55,12 @@ LL | #[deprecated("test")] | ^^^^^^^^^^^^^------^^ | | | didn't expect a literal here + | +help: try using `=` instead + | +LL - #[deprecated("test")] +LL + #[deprecated = "test"] + | error: multiple `deprecated` attributes --> $DIR/deprecation-sanity.rs:29:1 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 From 9bfbf3426c06fcb7dd90ae20fe3117ccccb95a88 Mon Sep 17 00:00:00 2001 From: Aria Givens Date: Thu, 28 May 2026 14:04:46 -0400 Subject: [PATCH 2/2] Suggest using since field when malformed deprecated attribute value is version shaped --- .../src/attributes/deprecation.rs | 60 ++++++++++++++----- tests/ui/deprecation/deprecation-sanity.rs | 3 + .../ui/deprecation/deprecation-sanity.stderr | 23 +++++-- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index fd8616c4e9ea0..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,12 +77,34 @@ impl SingleAttributeParser for DeprecatedParser { // 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 @@ 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 @@ -173,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 d0e5a8a3ed4a5..2e00b2efea5c5 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -62,20 +62,33 @@ 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")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^ @@ -83,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"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,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`.