86 lines
3.0 KiB
Rust
86 lines
3.0 KiB
Rust
use clippy_config::types::PubUnderscoreFieldsBehaviour;
|
|
use clippy_utils::attrs::is_doc_hidden;
|
|
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
|
use clippy_utils::is_path_lang_item;
|
|
use rustc_hir::{FieldDef, Item, ItemKind, LangItem};
|
|
use rustc_lint::{LateContext, LateLintPass};
|
|
use rustc_session::impl_lint_pass;
|
|
|
|
declare_clippy_lint! {
|
|
/// ### What it does
|
|
/// Checks whether any field of the struct is prefixed with an `_` (underscore) and also marked
|
|
/// `pub` (public)
|
|
///
|
|
/// ### Why is this bad?
|
|
/// Fields prefixed with an `_` are inferred as unused, which suggests it should not be marked
|
|
/// as `pub`, because marking it as `pub` infers it will be used.
|
|
///
|
|
/// ### Example
|
|
/// ```rust
|
|
/// struct FileHandle {
|
|
/// pub _descriptor: usize,
|
|
/// }
|
|
/// ```
|
|
/// Use instead:
|
|
/// ```rust
|
|
/// struct FileHandle {
|
|
/// _descriptor: usize,
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// OR
|
|
///
|
|
/// ```rust
|
|
/// struct FileHandle {
|
|
/// pub descriptor: usize,
|
|
/// }
|
|
/// ```
|
|
#[clippy::version = "1.77.0"]
|
|
pub PUB_UNDERSCORE_FIELDS,
|
|
pedantic,
|
|
"struct field prefixed with underscore and marked public"
|
|
}
|
|
|
|
pub struct PubUnderscoreFields {
|
|
pub behavior: PubUnderscoreFieldsBehaviour,
|
|
}
|
|
impl_lint_pass!(PubUnderscoreFields => [PUB_UNDERSCORE_FIELDS]);
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields {
|
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
|
// This lint only pertains to structs.
|
|
let ItemKind::Struct(variant_data, _) = &item.kind else {
|
|
return;
|
|
};
|
|
|
|
let is_visible = |field: &FieldDef<'_>| match self.behavior {
|
|
PubUnderscoreFieldsBehaviour::PubliclyExported => cx.effective_visibilities.is_reachable(field.def_id),
|
|
PubUnderscoreFieldsBehaviour::AllPubFields => {
|
|
// If there is a visibility span then the field is marked pub in some way.
|
|
!field.vis_span.is_empty()
|
|
},
|
|
};
|
|
|
|
for field in variant_data.fields() {
|
|
// Only pertains to fields that start with an underscore, and are public.
|
|
if field.ident.as_str().starts_with('_') && is_visible(field)
|
|
// We ignore fields that have `#[doc(hidden)]`.
|
|
&& !is_doc_hidden(cx.tcx.hir().attrs(field.hir_id))
|
|
// We ignore fields that are `PhantomData`.
|
|
&& !is_path_lang_item(cx, field.ty, LangItem::PhantomData)
|
|
{
|
|
span_lint_hir_and_then(
|
|
cx,
|
|
PUB_UNDERSCORE_FIELDS,
|
|
field.hir_id,
|
|
field.vis_span.to(field.ident.span),
|
|
"field marked as public but also inferred as unused because it's prefixed with `_`",
|
|
|diag| {
|
|
diag.help("consider removing the underscore, or making the field private");
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|