bevy_lint/lints/suspicious/iter_current_update_events.rs
1//! Checks for calls to `Events::<T>::iter_current_update_events()`.
2//!
3//! # Motivation
4//!
5//! `Events::<T>::iter_current_update_events()` lets you read all of the current events since
6//! `Events::<T>::update()` was last called, similar to `EventReader<T>`. Unlike `EventReader<T>`,
7//! `iter_current_update_events()` does not track which events have already been read. As such,
8//! `iter_current_update_events()` is highly discouraged because it may skip events or yield them
9//! multiple times.
10//!
11//! # Example
12//!
13//! ```
14//! # use bevy::prelude::*;
15//! #
16//! #[derive(Event)]
17//! struct MyEvent;
18//!
19//! fn my_system(events: Res<Events<MyEvent>>) {
20//! for event in events.iter_current_update_events() {
21//! // ...
22//! }
23//! }
24//! ```
25//!
26//! Use instead:
27//!
28//! ```
29//! # use bevy::prelude::*;
30//! #
31//! #[derive(Event)]
32//! struct MyEvent;
33//!
34//! fn my_system(mut events: EventReader<MyEvent>) {
35//! for event in events.read() {
36//! // ...
37//! }
38//! }
39//! ```
40
41use clippy_utils::diagnostics::span_lint_and_help;
42use rustc_hir::Expr;
43use rustc_lint::{LateContext, LateLintPass};
44
45use crate::{declare_bevy_lint, declare_bevy_lint_pass, sym, utils::hir_parse::MethodCall};
46
47declare_bevy_lint! {
48 pub(crate) ITER_CURRENT_UPDATE_EVENTS,
49 super::Suspicious,
50 "called `Events::<T>::iter_current_update_events()`",
51}
52
53declare_bevy_lint_pass! {
54 pub(crate) IterCurrentUpdateEvents => [ITER_CURRENT_UPDATE_EVENTS],
55}
56
57impl<'tcx> LateLintPass<'tcx> for IterCurrentUpdateEvents {
58 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
59 // If this expression was generated by an external macro, don't lint it.
60 if expr.span.in_external_macro(cx.tcx.sess.source_map()) {
61 return;
62 }
63
64 if let Some(method_call) = MethodCall::try_from(cx, expr) {
65 // Find the adjusted type of the receiver. Type adjustment does things like
66 // auto-dereference and type coercion. In this example, we use the adjusted type so
67 // that we can also handle `Res<Events<T>>`.
68 //
69 // ```
70 // fn plain(events: Events<T>) {
71 // // Original type is `Events<T>`, adjusted type is `Events<T>`.
72 // let _ = events.iter_current_update_events();
73 // }
74 //
75 // fn res(events: Res<Events<T>>) {
76 // // Original type is `Res<Events<T>>`, adjusted type is `Events<T>`.
77 // let _ = events.iter_current_update_events();
78 // }
79 // ```
80 let src_ty = cx
81 .typeck_results()
82 .expr_ty_adjusted(method_call.receiver)
83 .peel_refs();
84
85 if !crate::paths::EVENTS.matches_ty(cx, src_ty) {
86 return;
87 }
88
89 if method_call.method_path.ident.name == sym::iter_current_update_events {
90 span_lint_and_help(
91 cx,
92 ITER_CURRENT_UPDATE_EVENTS,
93 method_call.span,
94 ITER_CURRENT_UPDATE_EVENTS.desc,
95 None,
96 "`iter_current_update_events()` does not track which events have already been seen, consider using `EventReader<T>` instead",
97 );
98 }
99 }
100 }
101}