Auto merge of #76538 - fusion-engineering-forks:check-useless-unstable-trait-impl, r=lcnr
Warn for #[unstable] on trait impls when it has no effect. Earlier today I sent a PR with an `#[unstable]` attribute on a trait `impl`, but was informed that this attribute has no effect there. (comment: https://github.com/rust-lang/rust/pull/76525#issuecomment-689678895, issue: https://github.com/rust-lang/rust/issues/55436) This PR adds a warning for this situation. Trait `impl` blocks with `#[unstable]` where both the type and the trait are stable will result in a warning: ``` warning: An `#[unstable]` annotation here has no effect. See issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information. --> library/std/src/panic.rs:235:1 | 235 | #[unstable(feature = "integer_atomics", issue = "32976")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` --- It detects three problems in the existing code: 1. A few `RefUnwindSafe` implementations for the atomic integer types in `library/std/src/panic.rs`. Example:d92155bf6a/library/std/src/panic.rs (L235-L236)
2. An implementation of `Error` for `LayoutErr` in `library/std/srd/error.rs`:d92155bf6a/library/std/src/error.rs (L392-L397)
3. `From` implementations for `Waker` and `RawWaker` in `library/alloc/src/task.rs`. Example:d92155bf6a/library/alloc/src/task.rs (L36-L37)
Case 3 interesting: It has a bound with an `#[unstable]` trait (`W: Wake`), so appears to have much effect on stable code. It does however break similar blanket implementations. It would also have immediate effect if `Wake` was implemented for any stable type. (Which is not the case right now, but there are no warnings in place to prevent it.) Whether this case is a problem or not is not clear to me. If it isn't, adding a simple `c.visit_generics(..);` to this PR will stop the warning for this case.
This commit is contained in:
commit
989190874f
7 changed files with 129 additions and 16 deletions
|
@ -9,13 +9,14 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{Generics, HirId, Item, StructField, Variant};
|
use rustc_hir::{Generics, HirId, Item, StructField, TraitRef, Ty, TyKind, Variant};
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
use rustc_middle::middle::privacy::AccessLevels;
|
use rustc_middle::middle::privacy::AccessLevels;
|
||||||
use rustc_middle::middle::stability::{DeprecationEntry, Index};
|
use rustc_middle::middle::stability::{DeprecationEntry, Index};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
|
use rustc_session::lint::builtin::INEFFECTIVE_UNSTABLE_TRAIT_IMPL;
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::{sym, Symbol};
|
use rustc_span::symbol::{sym, Symbol};
|
||||||
|
@ -538,7 +539,37 @@ impl Visitor<'tcx> for Checker<'tcx> {
|
||||||
// For implementations of traits, check the stability of each item
|
// For implementations of traits, check the stability of each item
|
||||||
// individually as it's possible to have a stable trait with unstable
|
// individually as it's possible to have a stable trait with unstable
|
||||||
// items.
|
// items.
|
||||||
hir::ItemKind::Impl { of_trait: Some(ref t), items, .. } => {
|
hir::ItemKind::Impl { of_trait: Some(ref t), self_ty, items, .. } => {
|
||||||
|
if self.tcx.features().staged_api {
|
||||||
|
// If this impl block has an #[unstable] attribute, give an
|
||||||
|
// error if all involved types and traits are stable, because
|
||||||
|
// it will have no effect.
|
||||||
|
// See: https://github.com/rust-lang/rust/issues/55436
|
||||||
|
if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) =
|
||||||
|
attr::find_stability(&self.tcx.sess, &item.attrs, item.span)
|
||||||
|
{
|
||||||
|
let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
|
||||||
|
c.visit_ty(self_ty);
|
||||||
|
c.visit_trait_ref(t);
|
||||||
|
if c.fully_stable {
|
||||||
|
let span = item
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.find(|a| a.has_name(sym::unstable))
|
||||||
|
.map_or(item.span, |a| a.span);
|
||||||
|
self.tcx.struct_span_lint_hir(
|
||||||
|
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
||||||
|
item.hir_id,
|
||||||
|
span,
|
||||||
|
|lint| lint
|
||||||
|
.build("an `#[unstable]` annotation here has no effect")
|
||||||
|
.note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information")
|
||||||
|
.emit()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
|
if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
|
||||||
for impl_item_ref in items {
|
for impl_item_ref in items {
|
||||||
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
|
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
|
||||||
|
@ -598,6 +629,44 @@ impl Visitor<'tcx> for Checker<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CheckTraitImplStable<'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
fully_stable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visitor<'tcx> for CheckTraitImplStable<'tcx> {
|
||||||
|
type Map = Map<'tcx>;
|
||||||
|
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) {
|
||||||
|
if let Some(def_id) = path.res.opt_def_id() {
|
||||||
|
if let Some(stab) = self.tcx.lookup_stability(def_id) {
|
||||||
|
self.fully_stable &= stab.level.is_stable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intravisit::walk_path(self, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {
|
||||||
|
if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
|
||||||
|
if let Some(stab) = self.tcx.lookup_stability(trait_did) {
|
||||||
|
self.fully_stable &= stab.level.is_stable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intravisit::walk_trait_ref(self, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
|
||||||
|
if let TyKind::Never = t.kind {
|
||||||
|
self.fully_stable = false;
|
||||||
|
}
|
||||||
|
intravisit::walk_ty(self, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Given the list of enabled features that were not language features (i.e., that
|
/// Given the list of enabled features that were not language features (i.e., that
|
||||||
/// were expected to be library features), and the list of features used from
|
/// were expected to be library features), and the list of features used from
|
||||||
/// libraries, identify activated features that don't exist and error about them.
|
/// libraries, identify activated features that don't exist and error about them.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! lints are all available in `rustc_lint::builtin`.
|
//! lints are all available in `rustc_lint::builtin`.
|
||||||
|
|
||||||
use crate::lint::FutureIncompatibleInfo;
|
use crate::lint::FutureIncompatibleInfo;
|
||||||
use crate::{declare_lint, declare_lint_pass};
|
use crate::{declare_lint, declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
|
@ -555,6 +555,12 @@ declare_lint! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_tool_lint! {
|
||||||
|
pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
||||||
|
Deny,
|
||||||
|
"detects `#[unstable]` on stable trait implementations for stable types"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint_pass! {
|
declare_lint_pass! {
|
||||||
/// Does nothing as a lint pass, but registers some `Lint`s
|
/// Does nothing as a lint pass, but registers some `Lint`s
|
||||||
/// that are used by other parts of the compiler.
|
/// that are used by other parts of the compiler.
|
||||||
|
@ -630,6 +636,7 @@ declare_lint_pass! {
|
||||||
INCOMPLETE_INCLUDE,
|
INCOMPLETE_INCLUDE,
|
||||||
CENUM_IMPL_DROP_CAST,
|
CENUM_IMPL_DROP_CAST,
|
||||||
CONST_EVALUATABLE_UNCHECKED,
|
CONST_EVALUATABLE_UNCHECKED,
|
||||||
|
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub trait Wake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(rustc::ineffective_unstable_trait_impl)]
|
||||||
#[unstable(feature = "wake_trait", issue = "69912")]
|
#[unstable(feature = "wake_trait", issue = "69912")]
|
||||||
impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for Waker {
|
impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for Waker {
|
||||||
fn from(waker: Arc<W>) -> Waker {
|
fn from(waker: Arc<W>) -> Waker {
|
||||||
|
@ -42,6 +43,7 @@ impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for Waker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(rustc::ineffective_unstable_trait_impl)]
|
||||||
#[unstable(feature = "wake_trait", issue = "69912")]
|
#[unstable(feature = "wake_trait", issue = "69912")]
|
||||||
impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for RawWaker {
|
impl<W: Wake + Send + Sync + 'static> From<Arc<W>> for RawWaker {
|
||||||
fn from(waker: Arc<W>) -> RawWaker {
|
fn from(waker: Arc<W>) -> RawWaker {
|
||||||
|
|
|
@ -389,11 +389,7 @@ impl Error for ! {}
|
||||||
)]
|
)]
|
||||||
impl Error for AllocErr {}
|
impl Error for AllocErr {}
|
||||||
|
|
||||||
#[unstable(
|
#[stable(feature = "alloc_layout", since = "1.28.0")]
|
||||||
feature = "allocator_api",
|
|
||||||
reason = "the precise API and guarantees it provides may be tweaked.",
|
|
||||||
issue = "32838"
|
|
||||||
)]
|
|
||||||
impl Error for LayoutErr {}
|
impl Error for LayoutErr {}
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
|
@ -232,16 +232,16 @@ impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
|
||||||
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
|
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicIsize {}
|
impl RefUnwindSafe for atomic::AtomicIsize {}
|
||||||
#[cfg(target_has_atomic_load_store = "8")]
|
#[cfg(target_has_atomic_load_store = "8")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicI8 {}
|
impl RefUnwindSafe for atomic::AtomicI8 {}
|
||||||
#[cfg(target_has_atomic_load_store = "16")]
|
#[cfg(target_has_atomic_load_store = "16")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicI16 {}
|
impl RefUnwindSafe for atomic::AtomicI16 {}
|
||||||
#[cfg(target_has_atomic_load_store = "32")]
|
#[cfg(target_has_atomic_load_store = "32")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicI32 {}
|
impl RefUnwindSafe for atomic::AtomicI32 {}
|
||||||
#[cfg(target_has_atomic_load_store = "64")]
|
#[cfg(target_has_atomic_load_store = "64")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicI64 {}
|
impl RefUnwindSafe for atomic::AtomicI64 {}
|
||||||
#[cfg(target_has_atomic_load_store = "128")]
|
#[cfg(target_has_atomic_load_store = "128")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[unstable(feature = "integer_atomics", issue = "32976")]
|
||||||
|
@ -251,16 +251,16 @@ impl RefUnwindSafe for atomic::AtomicI128 {}
|
||||||
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
|
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicUsize {}
|
impl RefUnwindSafe for atomic::AtomicUsize {}
|
||||||
#[cfg(target_has_atomic_load_store = "8")]
|
#[cfg(target_has_atomic_load_store = "8")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicU8 {}
|
impl RefUnwindSafe for atomic::AtomicU8 {}
|
||||||
#[cfg(target_has_atomic_load_store = "16")]
|
#[cfg(target_has_atomic_load_store = "16")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicU16 {}
|
impl RefUnwindSafe for atomic::AtomicU16 {}
|
||||||
#[cfg(target_has_atomic_load_store = "32")]
|
#[cfg(target_has_atomic_load_store = "32")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicU32 {}
|
impl RefUnwindSafe for atomic::AtomicU32 {}
|
||||||
#[cfg(target_has_atomic_load_store = "64")]
|
#[cfg(target_has_atomic_load_store = "64")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
|
||||||
impl RefUnwindSafe for atomic::AtomicU64 {}
|
impl RefUnwindSafe for atomic::AtomicU64 {}
|
||||||
#[cfg(target_has_atomic_load_store = "128")]
|
#[cfg(target_has_atomic_load_store = "128")]
|
||||||
#[unstable(feature = "integer_atomics", issue = "32976")]
|
#[unstable(feature = "integer_atomics", issue = "32976")]
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
#![feature(staged_api)]
|
||||||
|
|
||||||
|
#[stable(feature = "x", since = "1")]
|
||||||
|
struct StableType;
|
||||||
|
|
||||||
|
#[unstable(feature = "x", issue = "none")]
|
||||||
|
struct UnstableType;
|
||||||
|
|
||||||
|
#[stable(feature = "x", since = "1")]
|
||||||
|
trait StableTrait {}
|
||||||
|
|
||||||
|
#[unstable(feature = "x", issue = "none")]
|
||||||
|
trait UnstableTrait {}
|
||||||
|
|
||||||
|
#[unstable(feature = "x", issue = "none")]
|
||||||
|
impl UnstableTrait for UnstableType {}
|
||||||
|
|
||||||
|
#[unstable(feature = "x", issue = "none")]
|
||||||
|
impl StableTrait for UnstableType {}
|
||||||
|
|
||||||
|
#[unstable(feature = "x", issue = "none")]
|
||||||
|
impl UnstableTrait for StableType {}
|
||||||
|
|
||||||
|
#[unstable(feature = "x", issue = "none")]
|
||||||
|
//~^ ERROR an `#[unstable]` annotation here has no effect [rustc::ineffective_unstable_trait_impl]
|
||||||
|
impl StableTrait for StableType {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,11 @@
|
||||||
|
error: an `#[unstable]` annotation here has no effect
|
||||||
|
--> $DIR/stability-attribute-trait-impl.rs:24:1
|
||||||
|
|
|
||||||
|
LL | #[unstable(feature = "x", issue = "none")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[deny(rustc::ineffective_unstable_trait_impl)]` on by default
|
||||||
|
= note: see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue