]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/exhaustive_items.rs
Auto merge of #86336 - camsteffen:char-array-pattern, r=joshtriplett
[rust.git] / src / tools / clippy / clippy_lints / src / exhaustive_items.rs
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};
8 use rustc_span::sym;
9
10 declare_clippy_lint! {
11     /// ### What it does
12     /// Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
13     ///
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.
18     ///
19     /// ### Example
20     /// ```rust
21     /// enum Foo {
22     ///     Bar,
23     ///     Baz
24     /// }
25     /// ```
26     /// Use instead:
27     /// ```rust
28     /// #[non_exhaustive]
29     /// enum Foo {
30     ///     Bar,
31     ///     Baz
32     /// }
33     /// ```
34     pub EXHAUSTIVE_ENUMS,
35     restriction,
36     "detects exported enums that have not been marked #[non_exhaustive]"
37 }
38
39 declare_clippy_lint! {
40     /// ### What it does
41     /// Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
42     ///
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.
47     ///
48     /// ### Example
49     /// ```rust
50     /// struct Foo {
51     ///     bar: u8,
52     ///     baz: String,
53     /// }
54     /// ```
55     /// Use instead:
56     /// ```rust
57     /// #[non_exhaustive]
58     /// struct Foo {
59     ///     bar: u8,
60     ///     baz: String,
61     /// }
62     /// ```
63     pub EXHAUSTIVE_STRUCTS,
64     restriction,
65     "detects exported structs that have not been marked #[non_exhaustive]"
66 }
67
68 declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
69
70 impl LateLintPass<'_> for ExhaustiveItems {
71     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
72         if_chain! {
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));
77             then {
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
81                         return;
82                     }
83                     (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive")
84                 } else {
85                     (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive")
86                 };
87                 let suggestion_span = item.span.shrink_to_lo();
88                 let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
89                 span_lint_and_then(
90                     cx,
91                     lint,
92                     item.span,
93                     msg,
94                     |diag| {
95                         let sugg = format!("#[non_exhaustive]\n{}", indent);
96                         diag.span_suggestion(suggestion_span,
97                                              "try adding #[non_exhaustive]",
98                                              sugg,
99                                              Applicability::MaybeIncorrect);
100                     }
101                 );
102
103             }
104         }
105     }
106 }