1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::indent_of;
3 use if_chain::if_chain;
4 use rustc_errors::Applicability;
5 use rustc_hir::{Item, ItemKind};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 declare_clippy_lint! {
12 /// Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
14 /// ### Why is this bad?
15 /// Exhaustive enums are typically fine, but a project which does
16 /// not wish to make a stability commitment around exported enums may wish to
17 /// disable them by default.
36 "detects exported enums that have not been marked #[non_exhaustive]"
39 declare_clippy_lint! {
41 /// Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
43 /// ### Why is this bad?
44 /// Exhaustive structs are typically fine, but a project which does
45 /// not wish to make a stability commitment around exported structs may wish to
46 /// disable them by default.
63 pub EXHAUSTIVE_STRUCTS,
65 "detects exported structs that have not been marked #[non_exhaustive]"
68 declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
70 impl LateLintPass<'_> for ExhaustiveItems {
71 fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
73 if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
74 if cx.access_levels.is_exported(item.def_id);
75 let attrs = cx.tcx.hir().attrs(item.hir_id());
76 if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
78 let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind {
79 if v.fields().iter().any(|f| !f.vis.node.is_pub()) {
80 // skip structs with private fields
83 (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
85 (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
87 let suggestion_span = item.span.shrink_to_lo();
88 let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
95 let sugg = format!("#[non_exhaustive]\n{}", indent);
96 diag.span_suggestion(suggestion_span,
97 "try adding #[non_exhaustive]",
99 Applicability::MaybeIncorrect);