about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-02-01 16:08:04 +0100
committerGitHub <noreply@github.com>2022-02-01 16:08:04 +0100
commiteb01fe85f721bb669305ff30cb0081d15495919f (patch)
treeb590ff9e86a417f4fb3d041a7a621b7fccb297d9
parent741b62af0749ddf88ebbd7cad6ec7a15ab1fc1cb (diff)
parent94b0a7b8e9affe00c83c7afdb725edd530d855ee (diff)
downloadrust-eb01fe85f721bb669305ff30cb0081d15495919f.tar.gz
rust-eb01fe85f721bb669305ff30cb0081d15495919f.zip
Rollup merge of #93267 - lcnr:auto-trait-lint, r=nikomatsakis
implement a lint for suspicious auto trait impls

cc https://github.com/rust-lang/rust/pull/85048#issuecomment-1019805102

r? ``@nikomatsakis``
-rw-r--r--compiler/rustc_index/src/bit_set.rs6
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs43
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs5
-rw-r--r--compiler/rustc_middle/src/lint.rs47
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs17
-rw-r--r--compiler/rustc_typeck/src/coherence/orphan.rs213
-rw-r--r--src/test/ui/auto-traits/suspicious-impls-lint.rs34
-rw-r--r--src/test/ui/auto-traits/suspicious-impls-lint.stderr52
-rw-r--r--src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs1
-rw-r--r--src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr8
-rw-r--r--src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs1
-rw-r--r--src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr52
12 files changed, 421 insertions, 58 deletions
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index 5aa213cb701..cf86c450a5b 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -938,6 +938,12 @@ pub struct GrowableBitSet<T: Idx> {
     bit_set: BitSet<T>,
 }
 
+impl<T: Idx> Default for GrowableBitSet<T> {
+    fn default() -> Self {
+        GrowableBitSet::new_empty()
+    }
+}
+
 impl<T: Idx> GrowableBitSet<T> {
     /// Ensure that the set can hold at least `min_domain_size` elements.
     pub fn ensure(&mut self, min_domain_size: usize) {
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 4af68233f0d..f4eba25475e 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -1793,6 +1793,10 @@ declare_lint! {
     Warn,
     "detects name collision with an existing but unstable method",
     @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::Custom(
+            "once this associated item is added to the standard library, \
+             the ambiguity may cause an error or change in behavior!"
+        ),
         reference: "issue #48919 <https://github.com/rust-lang/rust/issues/48919>",
         // Note: this item represents future incompatibility of all unstable functions in the
         //       standard library, and thus should never be removed or changed to an error.
@@ -2335,6 +2339,10 @@ declare_lint! {
     Warn,
     "reservation of a two-phased borrow conflicts with other shared borrows",
     @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::Custom(
+            "this borrowing pattern was not meant to be accepted, \
+            and may become a hard error in the future"
+        ),
         reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
     };
 }
@@ -3046,6 +3054,7 @@ declare_lint_pass! {
         DEREF_INTO_DYN_SUPERTRAIT,
         DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
         DUPLICATE_MACRO_ATTRIBUTES,
+        SUSPICIOUS_AUTO_TRAIT_IMPLS,
     ]
 }
 
@@ -3622,3 +3631,37 @@ declare_lint! {
     Warn,
     "duplicated attribute"
 }
+
+declare_lint! {
+    /// The `suspicious_auto_trait_impls` lint checks for potentially incorrect
+    /// implementations of auto traits.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// struct Foo<T>(T);
+    ///
+    /// unsafe impl<T> Send for Foo<*const T> {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A type can implement auto traits, e.g. `Send`, `Sync` and `Unpin`,
+    /// in two different ways: either by writing an explicit impl or if
+    /// all fields of the type implement that auto trait.
+    ///
+    /// The compiler disables the automatic implementation if an explicit one
+    /// exists for given type constructor. The exact rules governing this
+    /// are currently unsound and quite subtle and and will be modified in the future.
+    /// This change will cause the automatic implementation to be disabled in more
+    /// cases, potentially breaking some code.
+    pub SUSPICIOUS_AUTO_TRAIT_IMPLS,
+    Warn,
+    "the rules governing auto traits will change in the future",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange,
+        reference: "issue #93367 <https://github.com/rust-lang/rust/issues/93367>",
+    };
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 3b5d636124d..1f834b7212f 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -163,12 +163,17 @@ pub enum FutureIncompatibilityReason {
     /// This will be an error in a future release, and
     /// Cargo should create a report even for dependencies
     FutureReleaseErrorReportNow,
+    /// Code that changes meaning in some way in a
+    /// future release.
+    FutureReleaseSemanticsChange,
     /// Previously accepted code that will become an
     /// error in the provided edition
     EditionError(Edition),
     /// Code that changes meaning in some way in
     /// the provided edition
     EditionSemanticsChange(Edition),
+    /// A custom reason.
+    Custom(&'static str),
 }
 
 impl FutureIncompatibilityReason {
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index c6226c69f30..17c77c1bbd8 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -221,7 +221,6 @@ pub fn struct_lint_level<'s, 'd>(
         decorate: Box<dyn for<'b> FnOnce(LintDiagnosticBuilder<'b>) + 'd>,
     ) {
         // Check for future incompatibility lints and issue a stronger warning.
-        let lint_id = LintId::of(lint);
         let future_incompatible = lint.future_incompatible;
 
         let has_future_breakage = future_incompatible.map_or(
@@ -345,31 +344,29 @@ pub fn struct_lint_level<'s, 'd>(
         err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });
 
         if let Some(future_incompatible) = future_incompatible {
-            let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
-                "once this associated item is added to the standard library, the ambiguity may \
-                 cause an error or change in behavior!"
-                    .to_owned()
-            } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
-                "this borrowing pattern was not meant to be accepted, and may become a hard error \
-                 in the future"
-                    .to_owned()
-            } else if let FutureIncompatibilityReason::EditionError(edition) =
-                future_incompatible.reason
-            {
-                let current_edition = sess.edition();
-                format!(
-                    "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!",
-                    current_edition, edition
-                )
-            } else if let FutureIncompatibilityReason::EditionSemanticsChange(edition) =
-                future_incompatible.reason
-            {
-                format!("this changes meaning in Rust {}", edition)
-            } else {
-                "this was previously accepted by the compiler but is being phased out; \
-                 it will become a hard error in a future release!"
-                    .to_owned()
+            let explanation = match future_incompatible.reason {
+                FutureIncompatibilityReason::FutureReleaseError
+                | FutureIncompatibilityReason::FutureReleaseErrorReportNow => {
+                    "this was previously accepted by the compiler but is being phased out; \
+                         it will become a hard error in a future release!"
+                        .to_owned()
+                }
+                FutureIncompatibilityReason::FutureReleaseSemanticsChange => {
+                    "this will change its meaning in a future release!".to_owned()
+                }
+                FutureIncompatibilityReason::EditionError(edition) => {
+                    let current_edition = sess.edition();
+                    format!(
+                        "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!",
+                        current_edition, edition
+                    )
+                }
+                FutureIncompatibilityReason::EditionSemanticsChange(edition) => {
+                    format!("this changes meaning in Rust {}", edition)
+                }
+                FutureIncompatibilityReason::Custom(reason) => reason.to_owned(),
             };
+
             if future_incompatible.explain_reason {
                 err.warn(&explanation);
             }
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 9f8053d4a4e..9e32c0162e6 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -144,6 +144,23 @@ impl<'tcx> TyCtxt<'tcx> {
         });
     }
 
+    pub fn non_blanket_impls_for_ty(
+        self,
+        def_id: DefId,
+        self_ty: Ty<'tcx>,
+    ) -> impl Iterator<Item = DefId> + 'tcx {
+        let impls = self.trait_impls_of(def_id);
+        if let Some(simp) =
+            fast_reject::simplify_type(self, self_ty, SimplifyParams::No, StripReferences::No)
+        {
+            if let Some(impls) = impls.non_blanket_impls.get(&simp) {
+                return impls.iter().copied();
+            }
+        }
+
+        [].iter().copied()
+    }
+
     /// Applies function to every impl that could possibly match the self type `self_ty` and returns
     /// the first non-none value.
     pub fn find_map_relevant_impl<T, F: FnMut(DefId) -> Option<T>>(
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index e954b4cf512..777bd640669 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -1,24 +1,33 @@
 //! Orphan checker: every impl either implements a trait defined in this
 //! crate or pertains to a type defined in this crate.
 
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_errors::ErrorReported;
 use rustc_hir as hir;
+use rustc_index::bit_set::GrowableBitSet;
 use rustc_infer::infer::TyCtxtInferExt;
-use rustc_middle::ty::{self, TyCtxt};
-use rustc_span::def_id::LocalDefId;
+use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
+use rustc_middle::ty::{self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeVisitor};
+use rustc_session::lint;
+use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::Span;
 use rustc_trait_selection::traits;
+use std::ops::ControlFlow;
 
 pub(super) fn orphan_check_crate(tcx: TyCtxt<'_>, (): ()) -> &[LocalDefId] {
     let mut errors = Vec::new();
-    for (_trait, impls_of_trait) in tcx.all_local_trait_impls(()) {
+    for (&trait_def_id, impls_of_trait) in tcx.all_local_trait_impls(()) {
         for &impl_of_trait in impls_of_trait {
             match orphan_check_impl(tcx, impl_of_trait) {
                 Ok(()) => {}
                 Err(ErrorReported) => errors.push(impl_of_trait),
             }
         }
+
+        if tcx.trait_is_auto(trait_def_id) {
+            lint_auto_trait_impls(tcx, trait_def_id, impls_of_trait);
+        }
     }
     tcx.arena.alloc_slice(&errors)
 }
@@ -265,3 +274,201 @@ fn emit_orphan_check_error<'tcx>(
 
     Err(ErrorReported)
 }
+
+#[derive(Default)]
+struct AreUniqueParamsVisitor {
+    seen: GrowableBitSet<u32>,
+}
+
+#[derive(Copy, Clone)]
+enum NotUniqueParam<'tcx> {
+    DuplicateParam(GenericArg<'tcx>),
+    NotParam(GenericArg<'tcx>),
+}
+
+impl<'tcx> TypeVisitor<'tcx> for AreUniqueParamsVisitor {
+    type BreakTy = NotUniqueParam<'tcx>;
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+        match t.kind() {
+            ty::Param(p) => {
+                if self.seen.insert(p.index) {
+                    ControlFlow::CONTINUE
+                } else {
+                    ControlFlow::Break(NotUniqueParam::DuplicateParam(t.into()))
+                }
+            }
+            _ => ControlFlow::Break(NotUniqueParam::NotParam(t.into())),
+        }
+    }
+    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+        match r {
+            ty::ReEarlyBound(p) => {
+                if self.seen.insert(p.index) {
+                    ControlFlow::CONTINUE
+                } else {
+                    ControlFlow::Break(NotUniqueParam::DuplicateParam(r.into()))
+                }
+            }
+            _ => ControlFlow::Break(NotUniqueParam::NotParam(r.into())),
+        }
+    }
+    fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+        match c.val {
+            ty::ConstKind::Param(p) => {
+                if self.seen.insert(p.index) {
+                    ControlFlow::CONTINUE
+                } else {
+                    ControlFlow::Break(NotUniqueParam::DuplicateParam(c.into()))
+                }
+            }
+            _ => ControlFlow::Break(NotUniqueParam::NotParam(c.into())),
+        }
+    }
+}
+
+/// Lint impls of auto traits if they are likely to have
+/// unsound or surprising effects on auto impls.
+fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) {
+    let mut non_covering_impls = Vec::new();
+    for &impl_def_id in impls {
+        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+        if trait_ref.references_error() {
+            return;
+        }
+
+        if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive {
+            return;
+        }
+
+        assert_eq!(trait_ref.substs.len(), 1);
+        let self_ty = trait_ref.self_ty();
+        let (self_type_did, substs) = match self_ty.kind() {
+            ty::Adt(def, substs) => (def.did, substs),
+            _ => {
+                // FIXME: should also lint for stuff like `&i32` but
+                // considering that auto traits are unstable, that
+                // isn't too important for now as this only affects
+                // crates using `nightly`, and std.
+                continue;
+            }
+        };
+
+        // Impls which completely cover a given root type are fine as they
+        // disable auto impls entirely. So only lint if the substs
+        // are not a permutation of the identity substs.
+        match substs.visit_with(&mut AreUniqueParamsVisitor::default()) {
+            ControlFlow::Continue(()) => {} // ok
+            ControlFlow::Break(arg) => {
+                // Ideally:
+                //
+                // - compute the requirements for the auto impl candidate
+                // - check whether these are implied by the non covering impls
+                // - if not, emit the lint
+                //
+                // What we do here is a bit simpler:
+                //
+                // - badly check if an auto impl candidate definitely does not apply
+                //   for the given simplified type
+                // - if so, do not lint
+                if fast_reject_auto_impl(tcx, trait_def_id, self_ty) {
+                    // ok
+                } else {
+                    non_covering_impls.push((impl_def_id, self_type_did, arg));
+                }
+            }
+        }
+    }
+
+    for &(impl_def_id, self_type_did, arg) in &non_covering_impls {
+        tcx.struct_span_lint_hir(
+            lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
+            tcx.hir().local_def_id_to_hir_id(impl_def_id),
+            tcx.def_span(impl_def_id),
+            |err| {
+                let mut err = err.build(&format!(
+                    "cross-crate traits with a default impl, like `{}`, \
+                         should not be specialized",
+                    tcx.def_path_str(trait_def_id),
+                ));
+                let item_span = tcx.def_span(self_type_did);
+                let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
+                err.span_note(
+                    item_span,
+                    &format!(
+                        "try using the same sequence of generic parameters as the {} definition",
+                        self_descr,
+                    ),
+                );
+                match arg {
+                    NotUniqueParam::DuplicateParam(arg) => {
+                        err.note(&format!("`{}` is mentioned multiple times", arg));
+                    }
+                    NotUniqueParam::NotParam(arg) => {
+                        err.note(&format!("`{}` is not a generic parameter", arg));
+                    }
+                }
+                err.emit();
+            },
+        );
+    }
+}
+
+fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool {
+    struct DisableAutoTraitVisitor<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        trait_def_id: DefId,
+        self_ty_root: Ty<'tcx>,
+        seen: FxHashSet<DefId>,
+    }
+
+    impl<'tcx> TypeVisitor<'tcx> for DisableAutoTraitVisitor<'tcx> {
+        type BreakTy = ();
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            let tcx = self.tcx;
+            if t != self.self_ty_root {
+                for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) {
+                    match tcx.impl_polarity(impl_def_id) {
+                        ImplPolarity::Negative => return ControlFlow::BREAK,
+                        ImplPolarity::Reservation => {}
+                        // FIXME(@lcnr): That's probably not good enough, idk
+                        //
+                        // We might just want to take the rustdoc code and somehow avoid
+                        // explicit impls for `Self`.
+                        ImplPolarity::Positive => return ControlFlow::CONTINUE,
+                    }
+                }
+            }
+
+            match t.kind() {
+                ty::Adt(def, substs) => {
+                    // @lcnr: This is the only place where cycles can happen. We avoid this
+                    // by only visiting each `DefId` once.
+                    //
+                    // This will be is incorrect in subtle cases, but I don't care :)
+                    if self.seen.insert(def.did) {
+                        for ty in def.all_fields().map(|field| field.ty(tcx, substs)) {
+                            ty.visit_with(self)?;
+                        }
+                    }
+
+                    ControlFlow::CONTINUE
+                }
+                _ => t.super_visit_with(self),
+            }
+        }
+    }
+
+    let self_ty_root = match self_ty.kind() {
+        ty::Adt(def, _) => tcx.mk_adt(def, InternalSubsts::identity_for_item(tcx, def.did)),
+        _ => unimplemented!("unexpected self ty {:?}", self_ty),
+    };
+
+    self_ty_root
+        .visit_with(&mut DisableAutoTraitVisitor {
+            tcx,
+            self_ty_root,
+            trait_def_id,
+            seen: FxHashSet::default(),
+        })
+        .is_break()
+}
diff --git a/src/test/ui/auto-traits/suspicious-impls-lint.rs b/src/test/ui/auto-traits/suspicious-impls-lint.rs
new file mode 100644
index 00000000000..1026a35a455
--- /dev/null
+++ b/src/test/ui/auto-traits/suspicious-impls-lint.rs
@@ -0,0 +1,34 @@
+#![deny(suspicious_auto_trait_impls)]
+
+struct MayImplementSendOk<T>(T);
+unsafe impl<T: Send> Send for MayImplementSendOk<T> {} // ok
+
+struct MayImplementSendErr<T>(T);
+unsafe impl<T: Send> Send for MayImplementSendErr<&T> {}
+//~^ ERROR
+//~| WARNING this will change its meaning
+
+struct ContainsNonSendDirect<T>(*const T);
+unsafe impl<T: Send> Send for ContainsNonSendDirect<&T> {} // ok
+
+struct ContainsPtr<T>(*const T);
+struct ContainsIndirectNonSend<T>(ContainsPtr<T>);
+unsafe impl<T: Send> Send for ContainsIndirectNonSend<&T> {} // ok
+
+struct ContainsVec<T>(Vec<T>);
+unsafe impl Send for ContainsVec<i32> {}
+//~^ ERROR
+//~| WARNING this will change its meaning
+
+struct TwoParams<T, U>(T, U);
+unsafe impl<T: Send, U: Send> Send for TwoParams<T, U> {} // ok
+
+struct TwoParamsFlipped<T, U>(T, U);
+unsafe impl<T: Send, U: Send> Send for TwoParamsFlipped<U, T> {} // ok
+
+struct TwoParamsSame<T, U>(T, U);
+unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
+//~^ ERROR
+//~| WARNING this will change its meaning
+
+fn main() {}
diff --git a/src/test/ui/auto-traits/suspicious-impls-lint.stderr b/src/test/ui/auto-traits/suspicious-impls-lint.stderr
new file mode 100644
index 00000000000..f91aa862271
--- /dev/null
+++ b/src/test/ui/auto-traits/suspicious-impls-lint.stderr
@@ -0,0 +1,52 @@
+error: cross-crate traits with a default impl, like `Send`, should not be specialized
+  --> $DIR/suspicious-impls-lint.rs:7:1
+   |
+LL | unsafe impl<T: Send> Send for MayImplementSendErr<&T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/suspicious-impls-lint.rs:1:9
+   |
+LL | #![deny(suspicious_auto_trait_impls)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this will change its meaning in a future release!
+   = note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
+note: try using the same sequence of generic parameters as the struct definition
+  --> $DIR/suspicious-impls-lint.rs:6:1
+   |
+LL | struct MayImplementSendErr<T>(T);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `&T` is not a generic parameter
+
+error: cross-crate traits with a default impl, like `Send`, should not be specialized
+  --> $DIR/suspicious-impls-lint.rs:19:1
+   |
+LL | unsafe impl Send for ContainsVec<i32> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this will change its meaning in a future release!
+   = note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
+note: try using the same sequence of generic parameters as the struct definition
+  --> $DIR/suspicious-impls-lint.rs:18:1
+   |
+LL | struct ContainsVec<T>(Vec<T>);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `i32` is not a generic parameter
+
+error: cross-crate traits with a default impl, like `Send`, should not be specialized
+  --> $DIR/suspicious-impls-lint.rs:30:1
+   |
+LL | unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this will change its meaning in a future release!
+   = note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
+note: try using the same sequence of generic parameters as the struct definition
+  --> $DIR/suspicious-impls-lint.rs:29:1
+   |
+LL | struct TwoParamsSame<T, U>(T, U);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `T` is mentioned multiple times
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs
index 772ac322032..cc75cd4909a 100644
--- a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs
+++ b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs
@@ -1,4 +1,5 @@
 // aux-build:tdticc_coherence_lib.rs
+#![allow(suspicious_auto_trait_impls)]
 
 // Test that we do not consider associated types to be sendable without
 // some applicable trait bound (and we don't ICE).
diff --git a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr
index 90ab5be016c..cf5c15df705 100644
--- a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr
+++ b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr
@@ -1,5 +1,5 @@
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:13:1
+  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:14:1
    |
 LL | impl DefaultedTrait for (A,) { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^----
@@ -10,7 +10,7 @@ LL | impl DefaultedTrait for (A,) { }
    = note: define and implement a trait or new type instead
 
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:16:1
+  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:17:1
    |
 LL | impl !DefaultedTrait for (B,) { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^----
@@ -21,13 +21,13 @@ LL | impl !DefaultedTrait for (B,) { }
    = note: define and implement a trait or new type instead
 
 error[E0321]: cross-crate traits with a default impl, like `DefaultedTrait`, can only be implemented for a struct/enum type defined in the current crate
-  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:20:1
+  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:21:1
    |
 LL | impl DefaultedTrait for Box<C> { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait for type in another crate
 
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:21:1
+  --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:22:1
    |
 LL | impl DefaultedTrait for lib::Something<C> { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^-----------------
diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
index 828248d922f..514fb25c8cf 100644
--- a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::non_send_fields_in_send_ty)]
+#![allow(suspicious_auto_trait_impls)]
 #![feature(extern_types)]
 
 use std::cell::UnsafeCell;
diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
index 60df4e226e4..b6c904a147a 100644
--- a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
+++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
@@ -1,167 +1,167 @@
 error: some fields in `RingBuffer<T>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:16:1
+  --> $DIR/non_send_fields_in_send_ty.rs:17:1
    |
 LL | unsafe impl<T> Send for RingBuffer<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings`
 note: it is not safe to send field `data` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:11:5
+  --> $DIR/non_send_fields_in_send_ty.rs:12:5
    |
 LL |     data: Vec<UnsafeCell<T>>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
    = help: add bounds on type parameter `T` that satisfy `Vec<UnsafeCell<T>>: Send`
 
 error: some fields in `MvccRwLock<T>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:24:1
+  --> $DIR/non_send_fields_in_send_ty.rs:25:1
    |
 LL | unsafe impl<T> Send for MvccRwLock<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `lock` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:21:5
+  --> $DIR/non_send_fields_in_send_ty.rs:22:5
    |
 LL |     lock: Mutex<Box<T>>,
    |     ^^^^^^^^^^^^^^^^^^^
    = help: add bounds on type parameter `T` that satisfy `Mutex<Box<T>>: Send`
 
 error: some fields in `ArcGuard<RC, T>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:32:1
+  --> $DIR/non_send_fields_in_send_ty.rs:33:1
    |
 LL | unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `head` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:29:5
+  --> $DIR/non_send_fields_in_send_ty.rs:30:5
    |
 LL |     head: Arc<RC>,
    |     ^^^^^^^^^^^^^
    = help: add bounds on type parameter `RC` that satisfy `Arc<RC>: Send`
 
 error: some fields in `DeviceHandle<T>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:48:1
+  --> $DIR/non_send_fields_in_send_ty.rs:49:1
    |
 LL | unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `context` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:44:5
+  --> $DIR/non_send_fields_in_send_ty.rs:45:5
    |
 LL |     context: T,
    |     ^^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
 
 error: some fields in `NoGeneric` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:55:1
+  --> $DIR/non_send_fields_in_send_ty.rs:56:1
    |
 LL | unsafe impl Send for NoGeneric {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `rc_is_not_send` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:52:5
+  --> $DIR/non_send_fields_in_send_ty.rs:53:5
    |
 LL |     rc_is_not_send: Rc<String>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
 
 error: some fields in `MultiField<T>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:63:1
+  --> $DIR/non_send_fields_in_send_ty.rs:64:1
    |
 LL | unsafe impl<T> Send for MultiField<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `field1` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:58:5
+  --> $DIR/non_send_fields_in_send_ty.rs:59:5
    |
 LL |     field1: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
 note: it is not safe to send field `field2` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:59:5
+  --> $DIR/non_send_fields_in_send_ty.rs:60:5
    |
 LL |     field2: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
 note: it is not safe to send field `field3` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:60:5
+  --> $DIR/non_send_fields_in_send_ty.rs:61:5
    |
 LL |     field3: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
 
 error: some fields in `MyOption<T>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:70:1
+  --> $DIR/non_send_fields_in_send_ty.rs:71:1
    |
 LL | unsafe impl<T> Send for MyOption<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `0` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:66:12
+  --> $DIR/non_send_fields_in_send_ty.rs:67:12
    |
 LL |     MySome(T),
    |            ^
    = help: add `T: Send` bound in `Send` impl
 
 error: some fields in `MultiParam<A, B>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:82:1
+  --> $DIR/non_send_fields_in_send_ty.rs:83:1
    |
 LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `vec` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:79:5
+  --> $DIR/non_send_fields_in_send_ty.rs:80:5
    |
 LL |     vec: Vec<(A, B)>,
    |     ^^^^^^^^^^^^^^^^
    = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send`
 
 error: some fields in `HeuristicTest` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:100:1
+  --> $DIR/non_send_fields_in_send_ty.rs:101:1
    |
 LL | unsafe impl Send for HeuristicTest {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `field4` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:95:5
+  --> $DIR/non_send_fields_in_send_ty.rs:96:5
    |
 LL |     field4: (*const NonSend, Rc<u8>),
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
 
 error: some fields in `AttrTest3<T>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:119:1
+  --> $DIR/non_send_fields_in_send_ty.rs:120:1
    |
 LL | unsafe impl<T> Send for AttrTest3<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `0` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:114:11
+  --> $DIR/non_send_fields_in_send_ty.rs:115:11
    |
 LL |     Enum2(T),
    |           ^
    = help: add `T: Send` bound in `Send` impl
 
 error: some fields in `Complex<P, u32>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:127:1
+  --> $DIR/non_send_fields_in_send_ty.rs:128:1
    |
 LL | unsafe impl<P> Send for Complex<P, u32> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `field1` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:123:5
+  --> $DIR/non_send_fields_in_send_ty.rs:124:5
    |
 LL |     field1: A,
    |     ^^^^^^^^^
    = help: add `P: Send` bound in `Send` impl
 
 error: some fields in `Complex<Q, MutexGuard<'static, bool>>` are not safe to be sent to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:130:1
+  --> $DIR/non_send_fields_in_send_ty.rs:131:1
    |
 LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: it is not safe to send field `field2` to another thread
-  --> $DIR/non_send_fields_in_send_ty.rs:124:5
+  --> $DIR/non_send_fields_in_send_ty.rs:125:5
    |
 LL |     field2: B,
    |     ^^^^^^^^^