]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::{indent_of, span_lint_and_then}; |
2 | use if_chain::if_chain; | |
3 | use rustc_errors::Applicability; | |
4 | use rustc_hir::{Item, ItemKind}; | |
5 | use rustc_lint::{LateContext, LateLintPass}; | |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
7 | use rustc_span::sym; | |
8 | ||
9 | declare_clippy_lint! { | |
10 | /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` | |
11 | /// | |
12 | /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does | |
13 | /// not wish to make a stability commitment around exported enums may wish to | |
14 | /// disable them by default. | |
15 | /// | |
16 | /// **Known problems:** None. | |
17 | /// | |
18 | /// **Example:** | |
19 | /// | |
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:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]` | |
41 | /// | |
42 | /// **Why is this bad?** Exhaustive structs are typically fine, but a project which does | |
43 | /// not wish to make a stability commitment around exported structs may wish to | |
44 | /// disable them by default. | |
45 | /// | |
46 | /// **Known problems:** None. | |
47 | /// | |
48 | /// **Example:** | |
49 | /// | |
50 | /// ```rust | |
51 | /// struct Foo { | |
52 | /// bar: u8, | |
53 | /// baz: String, | |
54 | /// } | |
55 | /// ``` | |
56 | /// Use instead: | |
57 | /// ```rust | |
58 | /// #[non_exhaustive] | |
59 | /// struct Foo { | |
60 | /// bar: u8, | |
61 | /// baz: String, | |
62 | /// } | |
63 | /// ``` | |
64 | pub EXHAUSTIVE_STRUCTS, | |
65 | restriction, | |
66 | "detects exported structs that have not been marked #[non_exhaustive]" | |
67 | } | |
68 | ||
69 | declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); | |
70 | ||
71 | impl LateLintPass<'_> for ExhaustiveItems { | |
72 | fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { | |
73 | if_chain! { | |
74 | if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; | |
75 | if cx.access_levels.is_exported(item.hir_id()); | |
76 | let attrs = cx.tcx.hir().attrs(item.hir_id()); | |
77 | if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); | |
78 | then { | |
79 | let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { | |
80 | if v.fields().iter().any(|f| !f.vis.node.is_pub()) { | |
81 | // skip structs with private fields | |
82 | return; | |
83 | } | |
84 | (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") | |
85 | } else { | |
86 | (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") | |
87 | }; | |
88 | let suggestion_span = item.span.shrink_to_lo(); | |
89 | let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); | |
90 | span_lint_and_then( | |
91 | cx, | |
92 | lint, | |
93 | item.span, | |
94 | msg, | |
95 | |diag| { | |
96 | let sugg = format!("#[non_exhaustive]\n{}", indent); | |
97 | diag.span_suggestion(suggestion_span, | |
98 | "try adding #[non_exhaustive]", | |
99 | sugg, | |
100 | Applicability::MaybeIncorrect); | |
101 | } | |
102 | ); | |
103 | ||
104 | } | |
105 | } | |
106 | } | |
107 | } |