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}