From 0e2337a5d683c04004226502d2243ceb89cf4c22 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 31 Oct 2020 15:34:10 +0100 Subject: [PATCH 1/5] Deny #[deprecated] on trait impl blocks. They have no effect there, but were silently accepted. --- compiler/rustc_passes/src/stability.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index c9497f2a5b2..18d9e9f78d6 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -31,6 +31,8 @@ enum AnnotationKind { Required, // Annotation is useless, reject it Prohibited, + // Deprecation annotation is useless, reject it. (Stability attribute is still required.) + DeprecationProhibited, // Annotation itself is useless, but it can be propagated to children Container, } @@ -89,7 +91,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let Some(depr) = &depr { is_deprecated = true; - if kind == AnnotationKind::Prohibited { + if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited { self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless"); } @@ -322,6 +324,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { } hir::ItemKind::Impl { of_trait: Some(_), .. } => { self.in_trait_impl = true; + kind = AnnotationKind::DeprecationProhibited; } hir::ItemKind::Struct(ref sd, _) => { if let Some(ctor_hir_id) = sd.ctor_hir_id() { From 706bc336515581c7559a5ccdfae0a7d93b0508fa Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sun, 1 Nov 2020 12:29:57 +0100 Subject: [PATCH 2/5] Use the right span for errors about #[deprecated] attributes. --- compiler/rustc_attr/src/builtin.rs | 20 +++++++------- compiler/rustc_expand/src/base.rs | 2 +- compiler/rustc_passes/src/stability.rs | 26 ++++++++++++------- src/test/ui/deprecation/deprecation-sanity.rs | 4 +-- .../ui/deprecation/deprecation-sanity.stderr | 12 ++++++--- .../stability-attribute-sanity.rs | 6 ++--- .../stability-attribute-sanity.stderr | 18 ++++++++----- 7 files changed, 53 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 48238c8bbf5..49ac97d833f 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -637,19 +637,15 @@ pub struct Deprecation { } /// Finds the deprecation attribute. `None` if none exists. -pub fn find_deprecation(sess: &Session, attrs: &[Attribute], item_sp: Span) -> Option { - find_deprecation_generic(sess, attrs.iter(), item_sp) +pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(Deprecation, Span)> { + find_deprecation_generic(sess, attrs.iter()) } -fn find_deprecation_generic<'a, I>( - sess: &Session, - attrs_iter: I, - item_sp: Span, -) -> Option +fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(Deprecation, Span)> where I: Iterator, { - let mut depr: Option = None; + let mut depr: Option<(Deprecation, Span)> = None; let diagnostic = &sess.parse_sess.span_diagnostic; 'outer: for attr in attrs_iter { @@ -658,8 +654,10 @@ where continue; } - if depr.is_some() { - struct_span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes").emit(); + if let Some((_, span)) = &depr { + struct_span_err!(diagnostic, attr.span, E0550, "multiple deprecated attributes") + .span_note(*span, "first deprecation attribute here") + .emit(); break; } @@ -780,7 +778,7 @@ where sess.mark_attr_used(&attr); let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated); - depr = Some(Deprecation { since, note, suggestion, is_since_rustc_version }); + depr = Some((Deprecation { since, note, suggestion, is_since_rustc_version }, attr.span)); } depr diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index b0e43a260e9..b435def87ac 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -793,7 +793,7 @@ impl SyntaxExtension { allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe), local_inner_macros, stability, - deprecation: attr::find_deprecation(&sess, attrs, span), + deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), helper_attrs, edition, is_builtin, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 18d9e9f78d6..bacbab1a4f4 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -85,14 +85,22 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { did_error = self.forbid_staged_api_attrs(hir_id, attrs, inherit_deprecation.clone()); } - let depr = - if did_error { None } else { attr::find_deprecation(&self.tcx.sess, attrs, item_sp) }; + let depr = if did_error { None } else { attr::find_deprecation(&self.tcx.sess, attrs) }; let mut is_deprecated = false; - if let Some(depr) = &depr { + if let Some((depr, span)) = &depr { is_deprecated = true; if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited { - self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless"); + self.tcx + .sess + .struct_span_err(*span, "this deprecation annotation is useless") + .span_suggestion( + *span, + "try removing the deprecation attribute", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit(); } // `Deprecation` is just two pointers, no need to intern it @@ -116,7 +124,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } else { self.recurse_with_stability_attrs( - depr.map(|d| DeprecationEntry::local(d, hir_id)), + depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)), None, None, visit_children, @@ -141,11 +149,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - if depr.as_ref().map_or(false, |d| d.is_since_rustc_version) { + if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr { if stab.is_none() { struct_span_err!( self.tcx.sess, - item_sp, + *span, E0549, "rustc_deprecated attribute must be paired with \ either stable or unstable attribute" @@ -168,7 +176,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. if let (&Some(dep_since), &attr::Stable { since: stab_since }) = - (&depr.as_ref().and_then(|d| d.since), &stab.level) + (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { // Explicit version of iter::order::lt to handle parse errors properly for (dep_v, stab_v) in @@ -214,7 +222,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } self.recurse_with_stability_attrs( - depr.map(|d| DeprecationEntry::local(d, hir_id)), + depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)), stab, const_stab, visit_children, diff --git a/src/test/ui/deprecation/deprecation-sanity.rs b/src/test/ui/deprecation/deprecation-sanity.rs index a559908b792..4cbcec9b1d8 100644 --- a/src/test/ui/deprecation/deprecation-sanity.rs +++ b/src/test/ui/deprecation/deprecation-sanity.rs @@ -24,8 +24,8 @@ mod bogus_attribute_types_1 { } #[deprecated(since = "a", note = "b")] -#[deprecated(since = "a", note = "b")] -fn multiple1() { } //~ ERROR multiple deprecated attributes +#[deprecated(since = "a", note = "b")] //~ ERROR multiple deprecated attributes +fn multiple1() { } #[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items fn f1() { } diff --git a/src/test/ui/deprecation/deprecation-sanity.stderr b/src/test/ui/deprecation/deprecation-sanity.stderr index 57143d68105..4f70c55a957 100644 --- a/src/test/ui/deprecation/deprecation-sanity.stderr +++ b/src/test/ui/deprecation/deprecation-sanity.stderr @@ -41,10 +41,16 @@ LL | #[deprecated("test")] | ^^^^^^ error[E0550]: multiple deprecated attributes - --> $DIR/deprecation-sanity.rs:28:1 + --> $DIR/deprecation-sanity.rs:27:1 | -LL | fn multiple1() { } - | ^^^^^^^^^^^^^^^^^^ +LL | #[deprecated(since = "a", note = "b")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first deprecation attribute here + --> $DIR/deprecation-sanity.rs:26:1 + | +LL | #[deprecated(since = "a", note = "b")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0538]: multiple 'since' items --> $DIR/deprecation-sanity.rs:30:27 diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.rs b/src/test/ui/stability-attribute/stability-attribute-sanity.rs index 80d7ae6dc63..abd603b356e 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.rs +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.rs @@ -59,14 +59,14 @@ fn multiple3() { } #[stable(feature = "a", since = "b")] #[rustc_deprecated(since = "b", reason = "text")] -#[rustc_deprecated(since = "b", reason = "text")] +#[rustc_deprecated(since = "b", reason = "text")] //~ ERROR multiple deprecated attributes #[rustc_const_unstable(feature = "c", issue = "none")] #[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels -pub const fn multiple4() { } //~ ERROR multiple deprecated attributes +pub const fn multiple4() { } //~^ ERROR Invalid stability or deprecation version found #[rustc_deprecated(since = "a", reason = "text")] fn deprecated_without_unstable_or_stable() { } -//~^ ERROR rustc_deprecated attribute must be paired with either stable or unstable attribute +//~^^ ERROR rustc_deprecated attribute must be paired with either stable or unstable attribute fn main() { } diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index 134c657c620..f3edd5773cb 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -83,10 +83,16 @@ LL | #[stable(feature = "a", since = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0550]: multiple deprecated attributes - --> $DIR/stability-attribute-sanity.rs:65:1 + --> $DIR/stability-attribute-sanity.rs:62:1 | -LL | pub const fn multiple4() { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[rustc_deprecated(since = "b", reason = "text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first deprecation attribute here + --> $DIR/stability-attribute-sanity.rs:61:1 + | +LL | #[rustc_deprecated(since = "b", reason = "text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0544]: multiple stability levels --> $DIR/stability-attribute-sanity.rs:64:1 @@ -101,10 +107,10 @@ LL | pub const fn multiple4() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0549]: rustc_deprecated attribute must be paired with either stable or unstable attribute - --> $DIR/stability-attribute-sanity.rs:69:1 + --> $DIR/stability-attribute-sanity.rs:68:1 | -LL | fn deprecated_without_unstable_or_stable() { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[rustc_deprecated(since = "a", reason = "text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 18 previous errors From 6f1992a7d60152cd962feb907bfdc03290d9f8a4 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sun, 1 Nov 2020 12:58:47 +0100 Subject: [PATCH 3/5] Turn 'useless #[deprecated]' error into a lint. --- compiler/rustc_lint_defs/src/builtin.rs | 27 +++++++++++++++++++++++++ compiler/rustc_passes/src/stability.rs | 22 ++++++++++---------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 048f096aabe..a1b7c13e4c0 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2705,6 +2705,32 @@ declare_lint! { }; } +declare_lint! { + /// The `useless_deprecated` lint detects deprecation attributes with no effect. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// struct X; + /// + /// #[deprecated = "message"] + /// impl Default for X { + /// fn default() -> Self { + /// X + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Deprecation attributes have no effect on trait implementations. + pub USELESS_DEPRECATED, + Deny, + "detects deprecation attributes with no effect", +} + declare_tool_lint! { pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL, Deny, @@ -2792,6 +2818,7 @@ declare_lint_pass! { INEFFECTIVE_UNSTABLE_TRAIT_IMPL, UNINHABITED_STATIC, FUNCTION_ITEM_REFERENCES, + USELESS_DEPRECATED, ] } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index bacbab1a4f4..f6923b0dd9c 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -15,7 +15,7 @@ use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability::{DeprecationEntry, Index}; use rustc_middle::ty::{self, query::Providers, TyCtxt}; use rustc_session::lint; -use rustc_session::lint::builtin::INEFFECTIVE_UNSTABLE_TRAIT_IMPL; +use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; @@ -91,16 +91,16 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { is_deprecated = true; if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited { - self.tcx - .sess - .struct_span_err(*span, "this deprecation annotation is useless") - .span_suggestion( - *span, - "try removing the deprecation attribute", - String::new(), - rustc_errors::Applicability::MachineApplicable, - ) - .emit(); + self.tcx.struct_span_lint_hir(USELESS_DEPRECATED, hir_id, *span, |lint| { + lint.build("this `#[deprecated]' annotation has no effect") + .span_suggestion( + *span, + "try removing the deprecation attribute", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit() + }); } // `Deprecation` is just two pointers, no need to intern it From 9fc991a0ea1df0a5ef96c50fb8dce115c096d8f5 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sun, 1 Nov 2020 13:09:03 +0100 Subject: [PATCH 4/5] Add test for #[deprecated] attribute on trait impl block. --- src/test/ui/deprecation/deprecation-sanity.rs | 9 +++++++++ src/test/ui/deprecation/deprecation-sanity.stderr | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/test/ui/deprecation/deprecation-sanity.rs b/src/test/ui/deprecation/deprecation-sanity.rs index 4cbcec9b1d8..5fb3f819589 100644 --- a/src/test/ui/deprecation/deprecation-sanity.rs +++ b/src/test/ui/deprecation/deprecation-sanity.rs @@ -30,4 +30,13 @@ fn multiple1() { } #[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items fn f1() { } +struct X; + +#[deprecated = "hello"] //~ ERROR this `#[deprecated]' annotation has no effect +impl Default for X { + fn default() -> Self { + X + } +} + fn main() { } diff --git a/src/test/ui/deprecation/deprecation-sanity.stderr b/src/test/ui/deprecation/deprecation-sanity.stderr index 4f70c55a957..b926a6dc09d 100644 --- a/src/test/ui/deprecation/deprecation-sanity.stderr +++ b/src/test/ui/deprecation/deprecation-sanity.stderr @@ -58,7 +58,15 @@ error[E0538]: multiple 'since' items LL | #[deprecated(since = "a", since = "b", note = "c")] | ^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: this `#[deprecated]' annotation has no effect + --> $DIR/deprecation-sanity.rs:35:1 + | +LL | #[deprecated = "hello"] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the deprecation attribute + | + = note: `#[deny(useless_deprecated)]` on by default + +error: aborting due to 10 previous errors Some errors have detailed explanations: E0538, E0541, E0550, E0551, E0565. For more information about an error, try `rustc --explain E0538`. From 9c647d102168f0344bf895aedf96dd52c193aa25 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 2 Nov 2020 13:17:14 +0100 Subject: [PATCH 5/5] Improve deprecation attribute diagnostic messages. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (From the PR feedback.) Co-authored-by: Esteban Küber --- compiler/rustc_attr/src/builtin.rs | 3 ++- compiler/rustc_passes/src/stability.rs | 6 +++--- src/test/ui/deprecation/deprecation-sanity.rs | 2 +- src/test/ui/deprecation/deprecation-sanity.stderr | 12 ++++-------- .../stability-attribute-sanity.stderr | 8 ++------ 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 49ac97d833f..2fd625c2a6c 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -656,7 +656,8 @@ where if let Some((_, span)) = &depr { struct_span_err!(diagnostic, attr.span, E0550, "multiple deprecated attributes") - .span_note(*span, "first deprecation attribute here") + .span_label(attr.span, "repeated deprecation attribute") + .span_label(*span, "first deprecation attribute") .emit(); break; } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index f6923b0dd9c..04b5c65e464 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -92,10 +92,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited { self.tcx.struct_span_lint_hir(USELESS_DEPRECATED, hir_id, *span, |lint| { - lint.build("this `#[deprecated]' annotation has no effect") - .span_suggestion( + lint.build("this `#[deprecated]` annotation has no effect") + .span_suggestion_short( *span, - "try removing the deprecation attribute", + "remove the unnecessary deprecation attribute", String::new(), rustc_errors::Applicability::MachineApplicable, ) diff --git a/src/test/ui/deprecation/deprecation-sanity.rs b/src/test/ui/deprecation/deprecation-sanity.rs index 5fb3f819589..4fc3fddadb9 100644 --- a/src/test/ui/deprecation/deprecation-sanity.rs +++ b/src/test/ui/deprecation/deprecation-sanity.rs @@ -32,7 +32,7 @@ fn f1() { } struct X; -#[deprecated = "hello"] //~ ERROR this `#[deprecated]' annotation has no effect +#[deprecated = "hello"] //~ ERROR this `#[deprecated]` annotation has no effect impl Default for X { fn default() -> Self { X diff --git a/src/test/ui/deprecation/deprecation-sanity.stderr b/src/test/ui/deprecation/deprecation-sanity.stderr index b926a6dc09d..7e70c35eeab 100644 --- a/src/test/ui/deprecation/deprecation-sanity.stderr +++ b/src/test/ui/deprecation/deprecation-sanity.stderr @@ -44,13 +44,9 @@ error[E0550]: multiple deprecated attributes --> $DIR/deprecation-sanity.rs:27:1 | LL | #[deprecated(since = "a", note = "b")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first deprecation attribute here - --> $DIR/deprecation-sanity.rs:26:1 - | + | -------------------------------------- first deprecation attribute LL | #[deprecated(since = "a", note = "b")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ repeated deprecation attribute error[E0538]: multiple 'since' items --> $DIR/deprecation-sanity.rs:30:27 @@ -58,11 +54,11 @@ error[E0538]: multiple 'since' items LL | #[deprecated(since = "a", since = "b", note = "c")] | ^^^^^^^^^^^ -error: this `#[deprecated]' annotation has no effect +error: this `#[deprecated]` annotation has no effect --> $DIR/deprecation-sanity.rs:35:1 | LL | #[deprecated = "hello"] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the deprecation attribute + | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove the unnecessary deprecation attribute | = note: `#[deny(useless_deprecated)]` on by default diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr index f3edd5773cb..bf2436a535f 100644 --- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr @@ -86,13 +86,9 @@ error[E0550]: multiple deprecated attributes --> $DIR/stability-attribute-sanity.rs:62:1 | LL | #[rustc_deprecated(since = "b", reason = "text")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first deprecation attribute here - --> $DIR/stability-attribute-sanity.rs:61:1 - | + | ------------------------------------------------- first deprecation attribute LL | #[rustc_deprecated(since = "b", reason = "text")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ repeated deprecation attribute error[E0544]: multiple stability levels --> $DIR/stability-attribute-sanity.rs:64:1