summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
blob: f60d9d65b1207a345698bde417aa7094ee1deae3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Item, ItemKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// ### What it does
    /// Checks whether partial fields of a struct are public.
    ///
    /// Either make all fields of a type public, or make none of them public
    ///
    /// ### Why is this bad?
    /// Most types should either be:
    /// * Abstract data types: complex objects with opaque implementation which guard
    /// interior invariants and expose intentionally limited API to the outside world.
    /// * Data: relatively simple objects which group a bunch of related attributes together.
    ///
    /// ### Example
    /// ```rust
    /// pub struct Color {
    ///     pub r: u8,
    ///     pub g: u8,
    ///     b: u8,
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// pub struct Color {
    ///     pub r: u8,
    ///     pub g: u8,
    ///     pub b: u8,
    /// }
    /// ```
    #[clippy::version = "1.66.0"]
    pub PARTIAL_PUB_FIELDS,
    restriction,
    "partial fields of a struct are public"
}
declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]);

impl EarlyLintPass for PartialPubFields {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        let ItemKind::Struct(ref st, _) = item.kind else {
            return;
        };

        let mut fields = st.fields().iter();
        let Some(first_field) = fields.next() else {
            // Empty struct.
            return;
        };
        let all_pub = first_field.vis.kind.is_pub();
        let all_priv = !all_pub;

        let msg = "mixed usage of pub and non-pub fields";

        for field in fields {
            if all_priv && field.vis.kind.is_pub() {
                span_lint_and_help(
                    cx,
                    PARTIAL_PUB_FIELDS,
                    field.vis.span,
                    msg,
                    None,
                    "consider using private field here",
                );
                return;
            } else if all_pub && !field.vis.kind.is_pub() {
                span_lint_and_help(
                    cx,
                    PARTIAL_PUB_FIELDS,
                    field.vis.span,
                    msg,
                    None,
                    "consider using public field here",
                );
                return;
            }
        }
    }
}