diff options
Diffstat (limited to 'compiler/rustc_lint/src/reexports.rs')
| -rw-r--r-- | compiler/rustc_lint/src/reexports.rs | 82 |
1 files changed, 82 insertions, 0 deletions
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; + } + } + } + } + } +} |
