bevy_lint/lints/suspicious/iter_current_update_messages.rs
1//! Checks for calls to `Messages::<T>::iter_current_update_messages()`.
2//!
3//! # Motivation
4//!
5//! `Messages::<T>::iter_current_update_messages()` lets you read all of the current messages since
6//! `Messages::<T>::update()` was last called, similar to `MessageReader<T>`. Unlike
7//! `MessageReader<T>`, `iter_current_update_messages()` does not track which messages have already
8//! been read. As such, `iter_current_update_messages()` is highly discouraged because it may skip
9//! messages or yield them multiple times.
10//!
11//! # Example
12//!
13//! ```
14//! # use bevy::prelude::*;
15//! #
16//! #[derive(Message)]
17//! struct MyMessage;
18//!
19//! fn my_system(messages: Res<Messages<MyMessage>>) {
20//! for message in messages.iter_current_update_messages() {
21//! // ...
22//! }
23//! }
24//! ```
25//!
26//! Use instead:
27//!
28//! ```
29//! # use bevy::prelude::*;
30//! #
31//! #[derive(Message)]
32//! struct MyMessage;
33//!
34//! fn my_system(mut messages: MessageReader<MyMessage>) {
35//! for message in messages.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::method_call::MethodCall};
46
47declare_bevy_lint! {
48 pub(crate) ITER_CURRENT_UPDATE_MESSAGES,
49 super::Suspicious,
50 "called `Messages::<T>::iter_current_update_messages()`",
51}
52
53declare_bevy_lint_pass! {
54 pub(crate) IterCurrentUpdateMessages => [ITER_CURRENT_UPDATE_MESSAGES],
55}
56
57impl<'tcx> LateLintPass<'tcx> for IterCurrentUpdateMessages {
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<Message<T>>`.
68 //
69 // ```
70 // fn plain(messages: Messages<T>) {
71 // // Original type is `Messages<T>`, adjusted type is `Messages<T>`.
72 // let _ = messages.iter_current_update_messages();
73 // }
74 //
75 // fn res(messages: Res<Messages<T>>) {
76 // // Original type is `Res<Messages<T>>`, adjusted type is `Messages<T>`.
77 // let _ = messages.iter_current_update_messages();
78 // }
79 // ```
80 let src_ty = cx
81 .typeck_results()
82 .expr_ty_adjusted(method_call.receiver)
83 .peel_refs();
84
85 // Events is deprecated in favor of `Messages` in bevy `0.17`
86 if !(crate::paths::EVENTS.matches_ty(cx, src_ty)
87 || crate::paths::MESSAGES.matches_ty(cx, src_ty))
88 {
89 return;
90 }
91
92 if method_call.method_path.ident.name == sym::iter_current_update_messages {
93 span_lint_and_help(
94 cx,
95 ITER_CURRENT_UPDATE_MESSAGES,
96 method_call.span,
97 ITER_CURRENT_UPDATE_MESSAGES.desc,
98 None,
99 "`iter_current_update_messages()` does not track which messages have already been seen, consider using `MessageReader<T>` instead",
100 );
101 }
102 }
103 }
104}