bevy_lint/lints/restriction/
missing_trait_for_unit_struct.rs1use clippy_utils::{diagnostics::span_lint_and_then, sugg::DiagExt, ty::implements_trait};
22use rustc_errors::Applicability;
23use rustc_hir::{Item, ItemKind, VariantData, def_id::DefId};
24use rustc_lint::{LateContext, LateLintPass, Lint};
25
26use crate::{declare_bevy_lint, declare_bevy_lint_pass};
27
28declare_bevy_lint! {
29 pub(crate) MISSING_DEFAULT,
30 super::Restriction,
31 "defined a unit component without a `Default` implementation",
32}
33
34declare_bevy_lint! {
35 pub(crate) MISSING_CLONE,
36 super::Restriction,
37 "defined a unit component without a `Clone` implementation",
38}
39
40declare_bevy_lint! {
41 pub(crate) MISSING_COPY,
42 super::Restriction,
43 "defined a unit component without a `Copy` implementation",
44}
45
46declare_bevy_lint_pass! {
47 pub(crate) MissingTraitForUnitStruct => [MISSING_DEFAULT,MISSING_CLONE,MISSING_COPY],
48}
49
50impl<'tcx> LateLintPass<'tcx> for MissingTraitForUnitStruct {
51 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item) {
52 if item.span.in_external_macro(cx.tcx.sess.source_map()) {
54 return;
55 }
56
57 let ItemKind::Struct(_, _, data) = item.kind else {
59 return;
60 };
61
62 if !matches!(data, VariantData::Unit(..)) {
64 return;
65 }
66
67 let ty = cx.tcx.type_of(item.owner_id.to_def_id()).skip_binder();
69
70 for trait_to_implement in Trait::all() {
71 if let Some(trait_def_id) = trait_to_implement.get_def_id(cx)
73 && !implements_trait(cx, ty, trait_def_id, &[])
75 {
76 span_lint_and_then(
77 cx,
78 trait_to_implement.lint(),
79 item.span,
81 format!(
82 "defined a unit struct without a `{}` implementation",
83 trait_to_implement.name()
84 ),
85 |diag| {
86 diag.suggest_item_with_attr(
87 cx,
88 item.span,
89 format!(
90 "`{}` can be automatically derived",
91 trait_to_implement.name()
92 )
93 .as_str(),
94 format!("#[derive({})]", trait_to_implement.name()).as_str(),
95 Applicability::MachineApplicable,
102 );
103 },
104 );
105 }
106 }
107 }
108}
109
110enum Trait {
111 Copy,
112 Clone,
113 Default,
114}
115
116impl Trait {
117 const fn all() -> [Trait; 3] {
118 use Trait::*;
119
120 [Copy, Clone, Default]
121 }
122
123 const fn name(&self) -> &'static str {
124 match self {
125 Trait::Copy => "Copy",
126 Trait::Clone => "Clone",
127 Trait::Default => "Default",
128 }
129 }
130
131 const fn lint(&self) -> &'static Lint {
132 match self {
133 Trait::Copy => MISSING_COPY,
134 Trait::Clone => MISSING_CLONE,
135 Trait::Default => MISSING_DEFAULT,
136 }
137 }
138
139 fn get_def_id(&self, cx: &LateContext) -> std::option::Option<DefId> {
140 match self {
141 Trait::Copy => cx.tcx.get_diagnostic_item(rustc_span::symbol::sym::Copy),
142 Trait::Clone => cx.tcx.get_diagnostic_item(rustc_span::symbol::sym::Clone),
143 Trait::Default => cx.tcx.get_diagnostic_item(rustc_span::symbol::sym::Default),
144 }
145 }
146}