Code change:
} fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { <del> // treat PhantomData and positional ZST as public, // we don't want to lint types which only have them, // cause it's a common way to use such types to check things like well-formedness tcx.adt_def(id).all_fields().all(|field| { </del> <ins> let adt_def = tcx.adt_def(id); // skip types contain fields of unit and never type, // it's usually intentional to make the type not constructible let not_require_constructor = adt_def.all_fields().any(|field| { </ins> let field_type = tcx.type_of(field.did).instantiate_identity(); <del> if field_type.is_phantom_data() { return true; } let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit()); if is_positional && tcx .layout_of(tcx.param_env(field.did).and(field_type)) .map_or(true, |layout| layout.is_zst()) { return true; } field.vis.is_public() }) </del> <ins> field_type.is_unit() || field_type.is_never() }); not_require_constructor || adt_def.all_fields().all(|field| { let field_type = tcx.type_of(field.did).instantiate_identity(); // skip fields of PhantomData, // cause it's a common way to check things like well-formedness if field_type.is_phantom_data() { return true; } field.vis.is_public() }) </ins> } /// check struct and its fields are public or not,

Related discussion:
text: The struct is meant to not be constructible. Also the behavior is incoherent between tuple structs (correct behavior) and named structs (incorrect behavior). Also, this is a regression, this didn't use to be the case. No response No response
label +F-never_Type +D-inconsistent
We will also get such warnings for private structs with fields of never type (): The struct is never constructed although it is not constructible. This behavior is expected and is not a regression. Did you meet any cases using this way in real world? For the incoherent behavior, we also have: This because we only skip positional ZST (PhantomData and generics), this policy is also applied for never read fields:
Can you elaborate in which sense it is expected? I can understand that it is expected given the current implementation of Rust. It seems between stable and nightly a new lint was implemented. This lint is expected for inhabited types. However it has false positives on inhabited types, i.e. types that are only meant for type-level usage (for example to implement a trait). I agree we should probably categorize this issue as a false positive of a new lint rather than a regression (technically). Thanks, I can see the idea behind it. I'm not convinced about it though, but it doesn't bother me either. So let's just keep this issue focused on the false positive rather than the inconsistent behavior.
How do you think about private structs with fields of never type?
I would consider it the same as public structs, because they can still be "constructed" at the type-level. So they are dynamic dead--code but not dead-code since they are used statically. It would be dead-code though if the type is never used (both in dynamic code and static code, i.e. types).
Actually, thinking about this issue, I don't think this is a false positive only for the never type. I think the whole lint is wrong. The fact that the type is empty is just a proof that constructing it at term-level is not necessary for the type to be "alive" (aka usable). This could also be the case with non-empty types. A lot of people just use unit types for type-level usage only (instead of empty types).