]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/exhaustive_items.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.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 }