bevy_lint/lints/restriction/
schedule.rs1use clippy_utils::diagnostics::span_lint_hir;
56use rustc_lint::{LateContext, LateLintPass, Lint};
57use rustc_middle::ty::Ty;
58
59use crate::{declare_bevy_lint, declare_bevy_lint_pass, sym, utils::hir_parse::MethodCall};
60
61declare_bevy_lint! {
62 pub(crate) UPDATE_SCHEDULE,
63 super::Restriction,
64 "defined a system in the `FixedUpdate` schedule",
65}
66
67declare_bevy_lint! {
68 pub(crate) FIXED_UPDATE_SCHEDULE,
69 super::Restriction,
70 "defined a system in the `Update` schedule",
71}
72
73declare_bevy_lint_pass! {
74 pub(crate) Schedule => [UPDATE_SCHEDULE, FIXED_UPDATE_SCHEDULE],
75}
76
77impl<'tcx> LateLintPass<'tcx> for Schedule {
78 fn check_expr(
79 &mut self,
80 cx: &rustc_lint::LateContext<'tcx>,
81 expr: &'tcx rustc_hir::Expr<'tcx>,
82 ) {
83 if expr.span.in_external_macro(cx.tcx.sess.source_map()) {
84 return;
85 }
86
87 let Some(MethodCall {
88 method_path,
89 args,
90 receiver,
91 ..
92 }) = MethodCall::try_from(cx, expr)
93 else {
94 return;
95 };
96
97 let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs();
98
99 if !crate::paths::APP.matches_ty(cx, receiver_ty)
101 || method_path.ident.name != sym::add_systems
102 {
103 return;
104 }
105
106 let Some(schedule_label) = args.first() else {
108 return;
109 };
110
111 let schedule_ty = cx.typeck_results().expr_ty_adjusted(schedule_label);
112
113 let Some(schedule_type) = ScheduleType::try_from_ty(cx, schedule_ty) else {
114 return;
115 };
116
117 span_lint_hir(
118 cx,
119 schedule_type.lint(),
120 schedule_label.hir_id,
121 schedule_label.span,
122 format!("the `{}` schedule is disallowed", schedule_type.name()),
123 );
124 }
125}
126
127enum ScheduleType {
128 FixedUpdate,
129 Update,
130}
131
132impl ScheduleType {
133 fn try_from_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Self> {
135 if crate::paths::FIXED_UPDATE.matches_ty(cx, ty) {
136 Some(Self::FixedUpdate)
137 } else if crate::paths::UPDATE.matches_ty(cx, ty) {
138 Some(Self::Update)
139 } else {
140 None
141 }
142 }
143
144 fn name(&self) -> &'static str {
145 match self {
146 ScheduleType::FixedUpdate => "FixedUpdate",
147 ScheduleType::Update => "Update",
148 }
149 }
150
151 fn lint(&self) -> &'static Lint {
152 match self {
153 Self::FixedUpdate => FIXED_UPDATE_SCHEDULE,
154 Self::Update => UPDATE_SCHEDULE,
155 }
156 }
157}