diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 68e62c9789aed..c822237413c70 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -508,3 +508,6 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass
     .specifically = this associated type bound is unsatisfied for `{$proj_ty}`
 
 lint_opaque_hidden_inferred_bound_sugg = add this bound
+
+lint_useless_anonymous_reexport = useless anonymous re-export
+    .note = only anonymous re-exports of traits are useful, this is {$article} `{$desc}`
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index b3578540516d0..c2cc2fcdf551a 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -74,6 +74,7 @@ mod opaque_hidden_inferred_bound;
 mod pass_by_value;
 mod passes;
 mod redundant_semicolon;
+mod reexports;
 mod traits;
 mod types;
 mod unused;
@@ -111,6 +112,7 @@ use noop_method_call::*;
 use opaque_hidden_inferred_bound::*;
 use pass_by_value::*;
 use redundant_semicolon::*;
+use reexports::*;
 use traits::*;
 use types::*;
 use unused::*;
@@ -242,6 +244,7 @@ late_lint_methods!(
             OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
             MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
             MapUnitFn: MapUnitFn,
+            UselessAnonymousReexport: UselessAnonymousReexport,
         ]
     ]
 );
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 20ab0af585651..8d058517118c4 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1530,3 +1530,11 @@ pub struct UnusedAllocationDiag;
 #[derive(LintDiagnostic)]
 #[diag(lint_unused_allocation_mut)]
 pub struct UnusedAllocationMutDiag;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_useless_anonymous_reexport)]
+#[note]
+pub struct UselessAnonymousReexportDiag {
+    pub article: &'static str,
+    pub desc: &'static str,
+}
diff --git a/compiler/rustc_lint/src/reexports.rs b/compiler/rustc_lint/src/reexports.rs
new file mode 100644
index 0000000000000..8737a57ea0264
--- /dev/null
+++ b/compiler/rustc_lint/src/reexports.rs
@@ -0,0 +1,82 @@
+use crate::lints::UselessAnonymousReexportDiag;
+use crate::{LateContext, LateLintPass, LintContext};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Item, ItemKind, UseKind};
+use rustc_middle::ty::Visibility;
+use rustc_span::symbol::kw;
+use rustc_span::Span;
+
+declare_lint! {
+    /// The `useless_anonymous_reexport` lint checks if anonymous re-exports
+    /// are re-exports of traits.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(useless_anonymous_reexport)]
+    ///
+    /// mod sub {
+    ///     pub struct Bar;
+    /// }
+    ///
+    /// pub use self::sub::Bar as _;
+    /// # fn main() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Anonymous re-exports are only useful if it's a re-export of a trait
+    /// in case you want to give access to it. If you re-export any other kind,
+    /// you won't be able to use it since its name won't be accessible.
+    pub USELESS_ANONYMOUS_REEXPORT,
+    Warn,
+    "useless anonymous re-export"
+}
+
+declare_lint_pass!(UselessAnonymousReexport => [USELESS_ANONYMOUS_REEXPORT]);
+
+fn emit_err(cx: &LateContext<'_>, span: Span, def_id: DefId) {
+    let article = cx.tcx.def_descr_article(def_id);
+    let desc = cx.tcx.def_descr(def_id);
+    cx.emit_spanned_lint(
+        USELESS_ANONYMOUS_REEXPORT,
+        span,
+        UselessAnonymousReexportDiag { article, desc },
+    );
+}
+
+impl<'tcx> LateLintPass<'tcx> for UselessAnonymousReexport {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if let ItemKind::Use(path, kind) = item.kind &&
+            !matches!(kind, UseKind::Glob) &&
+            item.ident.name == kw::Underscore &&
+            // We only want re-exports. If it's just a `use X;`, then we ignore it.
+            match cx.tcx.local_visibility(item.owner_id.def_id) {
+                Visibility::Public => true,
+                Visibility::Restricted(level) => {
+                    level != cx.tcx.parent_module_from_def_id(item.owner_id.def_id)
+                }
+            }
+        {
+            for def_id in path.res.iter().filter_map(|r| r.opt_def_id()) {
+                match cx.tcx.def_kind(def_id) {
+                    DefKind::Trait | DefKind::TraitAlias => {}
+                    DefKind::TyAlias => {
+                        let ty = cx.tcx.type_of(def_id);
+                        if !ty.0.is_trait() {
+                            emit_err(cx, item.span, def_id);
+                            break;
+                        }
+                    }
+                    _ => {
+                        emit_err(cx, item.span, def_id);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ui/imports/issue-99695-b.fixed b/tests/ui/imports/issue-99695-b.fixed
index 0e60c73b67a44..0f688fa282356 100644
--- a/tests/ui/imports/issue-99695-b.fixed
+++ b/tests/ui/imports/issue-99695-b.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(unused, nonstandard_style)]
+#![allow(unused, nonstandard_style, useless_anonymous_reexport)]
 mod m {
 
     mod p {
diff --git a/tests/ui/imports/issue-99695-b.rs b/tests/ui/imports/issue-99695-b.rs
index 031443a1f5df8..b433997e53f6a 100644
--- a/tests/ui/imports/issue-99695-b.rs
+++ b/tests/ui/imports/issue-99695-b.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(unused, nonstandard_style)]
+#![allow(unused, nonstandard_style, useless_anonymous_reexport)]
 mod m {
 
     mod p {
diff --git a/tests/ui/imports/issue-99695.fixed b/tests/ui/imports/issue-99695.fixed
index 6bf228b23aad2..17ff409324e34 100644
--- a/tests/ui/imports/issue-99695.fixed
+++ b/tests/ui/imports/issue-99695.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(unused, nonstandard_style)]
+#![allow(unused, nonstandard_style, useless_anonymous_reexport)]
 mod m {
     #[macro_export]
     macro_rules! nu {
diff --git a/tests/ui/imports/issue-99695.rs b/tests/ui/imports/issue-99695.rs
index f7199f1497ab0..b8979bcb7345c 100644
--- a/tests/ui/imports/issue-99695.rs
+++ b/tests/ui/imports/issue-99695.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(unused, nonstandard_style)]
+#![allow(unused, nonstandard_style, useless_anonymous_reexport)]
 mod m {
     #[macro_export]
     macro_rules! nu {
diff --git a/tests/ui/lint/anonymous-reexport.rs b/tests/ui/lint/anonymous-reexport.rs
new file mode 100644
index 0000000000000..5d56ae6f969b5
--- /dev/null
+++ b/tests/ui/lint/anonymous-reexport.rs
@@ -0,0 +1,21 @@
+#![deny(useless_anonymous_reexport)]
+#![crate_type = "rlib"]
+
+mod my_mod {
+    pub trait Foo {}
+    pub type TyFoo = dyn Foo;
+    pub struct Bar;
+    pub type TyBar = Bar;
+}
+
+pub use self::my_mod::Foo as _;
+pub use self::my_mod::TyFoo as _;
+pub use self::my_mod::Bar as _; //~ ERROR
+pub use self::my_mod::TyBar as _; //~ ERROR
+pub use self::my_mod::{Bar as _}; //~ ERROR
+pub use self::my_mod::{Bar as _, Foo as _}; //~ ERROR
+pub use self::my_mod::{Bar as _, TyBar as _};
+//~^ ERROR
+//~| ERROR
+#[allow(unused_imports)]
+use self::my_mod::TyBar as _;
diff --git a/tests/ui/lint/anonymous-reexport.stderr b/tests/ui/lint/anonymous-reexport.stderr
new file mode 100644
index 0000000000000..f4f8b41c417a4
--- /dev/null
+++ b/tests/ui/lint/anonymous-reexport.stderr
@@ -0,0 +1,55 @@
+error: useless anonymous re-export
+  --> $DIR/anonymous-reexport.rs:13:1
+   |
+LL | pub use self::my_mod::Bar as _;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only anonymous re-exports of traits are useful, this is a `struct`
+note: the lint level is defined here
+  --> $DIR/anonymous-reexport.rs:1:9
+   |
+LL | #![deny(useless_anonymous_reexport)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: useless anonymous re-export
+  --> $DIR/anonymous-reexport.rs:14:1
+   |
+LL | pub use self::my_mod::TyBar as _;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: only anonymous re-exports of traits are useful, this is a `type alias`
+
+error: useless anonymous re-export
+  --> $DIR/anonymous-reexport.rs:15:24
+   |
+LL | pub use self::my_mod::{Bar as _};
+   |                        ^^^^^^^^
+   |
+   = note: only anonymous re-exports of traits are useful, this is a `struct`
+
+error: useless anonymous re-export
+  --> $DIR/anonymous-reexport.rs:16:24
+   |
+LL | pub use self::my_mod::{Bar as _, Foo as _};
+   |                        ^^^^^^^^
+   |
+   = note: only anonymous re-exports of traits are useful, this is a `struct`
+
+error: useless anonymous re-export
+  --> $DIR/anonymous-reexport.rs:17:24
+   |
+LL | pub use self::my_mod::{Bar as _, TyBar as _};
+   |                        ^^^^^^^^
+   |
+   = note: only anonymous re-exports of traits are useful, this is a `struct`
+
+error: useless anonymous re-export
+  --> $DIR/anonymous-reexport.rs:17:34
+   |
+LL | pub use self::my_mod::{Bar as _, TyBar as _};
+   |                                  ^^^^^^^^^^
+   |
+   = note: only anonymous re-exports of traits are useful, this is a `type alias`
+
+error: aborting due to 6 previous errors
+