diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 68e62c9789a..c822237413c 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 b3578540516..c2cc2fcdf55 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -74,6 +74,7 @@ mod pass_by_value; mod passes; mod redundant_semicolon; +mod reexports; mod traits; mod types; mod unused; @@ -111,6 +112,7 @@ use opaque_hidden_inferred_bound::*; use pass_by_value::*; use redundant_semicolon::*; +use reexports::*; use traits::*; use types::*; use unused::*; @@ -242,6 +244,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { 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 308c02929ca..46a025f41e0 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1528,3 +1528,11 @@ pub struct UnusedImportBracesDiag { #[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 00000000000..8737a57ea02 --- /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 0e60c73b67a..0f688fa2823 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 031443a1f5d..b433997e53f 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 6bf228b23aa..17ff409324e 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 f7199f1497a..b8979bcb734 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 00000000000..5d56ae6f969 --- /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 00000000000..f4f8b41c417 --- /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 +