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.
34 #[clippy::version = "1.51.0"]
37 "detects exported enums that have not been marked #[non_exhaustive]"
40 declare_clippy_lint! {
42 /// Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
44 /// ### Why is this bad?
45 /// Exhaustive structs are typically fine, but a project which does
46 /// not wish to make a stability commitment around exported structs may wish to
47 /// disable them by default.
64 #[clippy::version = "1.51.0"]
65 pub EXHAUSTIVE_STRUCTS,
67 "detects exported structs that have not been marked #[non_exhaustive]"
70 declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
72 impl LateLintPass<'_> for ExhaustiveItems {
73 fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
75 if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
76 if cx.access_levels.is_exported(item.def_id);
77 let attrs = cx.tcx.hir().attrs(item.hir_id());
78 if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
80 let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind {
81 if v.fields().iter().any(|f| !f.vis.node.is_pub()) {
82 // skip structs with private fields
85 (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
87 (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
89 let suggestion_span = item.span.shrink_to_lo();
90 let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
97 let sugg = format!("#[non_exhaustive]\n{}", indent);
98 diag.span_suggestion(suggestion_span,
99 "try adding #[non_exhaustive]",
101 Applicability::MaybeIncorrect);