Skip to content

Commit e266595

Browse files
authored
Announcing bevy_lint v0.2.0 (#12)
1 parent 1acdc48 commit e266595

File tree

2 files changed

+308
-1
lines changed

2 files changed

+308
-1
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
---
2+
title: Announcing bevy_lint v0.2.0
3+
---
4+
5+
<script lang="ts">
6+
// The adjective the reader feels best describes themselves. The later 2 have special surprises! :)
7+
let adjective = $state("punctual");
8+
9+
$effect(() => {
10+
document.body.classList.toggle("bright", adjective === "bright");
11+
document.body.classList.toggle("loud", adjective === "questionably loud");
12+
});
13+
</script>
14+
15+
<style>
16+
:global {
17+
/* Clear the gradient background, make it bright yellow instead, and make the text black. 😎 */
18+
body.bright {
19+
background-image: none;
20+
background-color: yellow;
21+
color: black;
22+
}
23+
24+
/* Make all text fully uppercase. (If you're going to yell at me, I'll yell back!) */
25+
body.loud {
26+
text-transform: uppercase;
27+
}
28+
}
29+
</style>
30+
31+
# Announcing `bevy_lint` v0.2.0
32+
33+
<span>
34+
Hello there, my dear
35+
<select bind:value={adjective}>
36+
<option>punctual</option>
37+
<option>splendiforous</option>
38+
<option>bright</option>
39+
<option>questionably loud</option>
40+
</select> reader! I hope your day has been going exceptionally well.
41+
</span>
42+
43+
I have returned from my 6 month[^1] writing hiatus to give you a very special announcement: today is the official release of `bevy_lint` v0.2.0! With it comes some very exciting features that I can't wait to talk about!
44+
45+
[^1]: Oh wow, has it actually been 6 months? I feel like I just wrote [4 Years of Bevy](TODO) yesterday!
46+
47+
`bevy_lint` is a custom linter for the [Bevy game engine](https://bevyengine.org), similar to [Clippy](https://doc.rust-lang.org/clippy/), that can be used to enforce Bevy-specific idioms, catch common mistakes, and help you write better code. In order to avoid repeating myself, I highly recommend you check out [its documentation](https://thebevyflock.github.io/bevy_cli/bevy_lint/index.html) for an extended description, installation guide, and user guide.
48+
49+
[`bevy_lint` v0.1.0](https://github.com/TheBevyFlock/bevy_cli/releases/tag/lint-v0.1.0) released mid-November of 2024, so it's been a good 4 months since then. In that time, I and several others have added many new features and improvements (all of which you can view [in the changelog](github.com/TheBevyFlock/bevy_cli/blob/main/bevy_lint/CHANGELOG.md)). Let's take a look at the highlights!
50+
51+
## Configure lints in `Cargo.toml`
52+
53+
If you were an early adopter of `bevy_lint`, the following header may be familiar to you:
54+
55+
```rust
56+
// Register `bevy` as a tool.
57+
#![cfg_attr(bevy_lint, feature(register_tool), register_tool(bevy))]
58+
59+
// Enable pedantic lints.
60+
#![cfg_attr(bevy_lint, warn(bevy::pedantic)))]
61+
```
62+
63+
In v0.1.0, the only way to toggle lints was to write the above in your crate root (`lib.rs` or `main.rs`). This was clearly a lot of boilerplate, so in [#251](https://github.com/TheBevyFlock/bevy_cli/pull/251) I added support for configuring lints in `Cargo.toml`:
64+
65+
```toml
66+
# Much nicer! :)
67+
[package.metadata.bevy_lint]
68+
pedantic = "warn"
69+
```
70+
71+
This feature was heavily inspired by Cargo's builtin [`[lints]` section](https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section), which lets you configure Rust and Clippy's lints from `Cargo.toml`. I initially wanted to support this table instead of using `[package.metadata]`, but Cargo emits a warning that cannot be silenced when you add `[lints.bevy]` to `Cargo.toml`:
72+
73+
```
74+
warning: /path/to/Cargo.toml: unrecognized lint tool `lints.bevy`, specifying unrecognized tools may break in the future.
75+
supported tools: cargo, clippy, rust, rustdoc
76+
```
77+
78+
On the positive side, however, using `[package.metadata]` means `bevy_lint` has direct control over how lints are applied. I took advantage of this by adding support for merging workspace-level lints with crate-level lints (a feature that Cargo does not natively support yet):
79+
80+
```toml
81+
# This will be applied to all crates in the workspace.
82+
[workspace.metadata.bevy_lint]
83+
pedantic = "warn"
84+
panicking_methods = "deny"
85+
86+
[package.metadata.bevy_lint]
87+
# This enables an extra lint just for this crate.
88+
insert_unit_bundle = "forbid"
89+
# This overrides the workspace lint level.
90+
panicking_methods = "allow"
91+
```
92+
93+
## Bevy 0.15 Support
94+
95+
As of [#191](https://github.com/TheBevyFlock/bevy_cli/pull/191), the linter now officially supports [Bevy 0.15](https://bevyengine.org/news/bevy-0-15/)[^2]. Unfortunately, that also means dropping support for Bevy 0.14. There are plans to eventually [support multiple versions](https://github.com/TheBevyFlock/bevy_cli/issues/138), but as of right now we can only support one.
96+
97+
[^2]: Which is somewhat funny, since the engine has already published the release candidates for Bevy 0.16! I guess we'll have to release v0.3.0 a bit faster next time, so we don't fall behind the rest of the ecosystem :)
98+
99+
## Many New Lints
100+
101+
`bevy_lint` has three new lints! (All of which were implemented by outside contributors. Thank you!)
102+
103+
### `borrowed_reborrowable`
104+
105+
First, `borrowed_reborrowable` warns against creating references to types that themselves are actually references, such as `Commands` and `Mut`. Instead, it recommends you use the convenient `reborrow()` method that many structures provide, which lets you convert `&mut T` into `T` for re-borrowable types:
106+
107+
```rust
108+
fn system(mut commands: Commands) {
109+
// `Commands` internally contains an `&mut T` already, so creating a reference results in
110+
// `&mut &mut T`:
111+
helper_function(&mut commands);
112+
}
113+
114+
fn helper_function(commands: &mut Commands) {
115+
// ...
116+
}
117+
```
118+
119+
Use instead:
120+
121+
```rust
122+
fn system(mut commands: Commands) {
123+
// Convert `&mut Commands` to `Commands`.
124+
helper_function(commands.reborrow());
125+
}
126+
127+
fn helper_function(mut commands: Commands) {
128+
// ...
129+
}
130+
```
131+
132+
### `insert_unit_bundle`
133+
134+
Second, `insert_unit_bundle` warns against spawning a [unit `()`](https://doc.rust-lang.org/stable/std/primitive.unit.html). Even though `()` is [technically a bundle](https://docs.rs/bevy/0.15.3/bevy/ecs/bundle/trait.Bundle.html#impl-Bundle-for-()), trying to spawn it does nothing. (`commands.spawn(())` is equivalent to `commands.spawn_empty()`, although the latter is more efficient.) In practice, this lint catches occurrences where you assume a function returns a component that you can spawn, when in reality it just returns a unit `()`:
135+
136+
```rust
137+
fn spawn(mut commands: Commands) {
138+
commands.spawn(());
139+
140+
commands.spawn((
141+
Name::new("Decal"),
142+
// This is likely a mistake! `Transform::rotate_z()` returns a unit `()`, not a
143+
// `Transform`! As such, no `Transform` will be inserted into the entity.
144+
Transform::from_translation(Vec3::new(0.75, 0.0, 0.0))
145+
.rotate_z(PI / 4.0),
146+
));
147+
}
148+
```
149+
150+
Use instead:
151+
152+
```rust
153+
fn spawn(mut commands: Commands) {
154+
// `Commands::spawn_empty()` is preferred if you do not need any components.
155+
commands.spawn_empty();
156+
157+
commands.spawn((
158+
Name::new("Decal"),
159+
// `Transform::with_rotation()` returns a `Transform`, which was likely the intended
160+
// behavior.
161+
Transform::from_translation(Vec3::new(0.75, 0.0, 0.0))
162+
.with_rotation(Quat::from_rotation_z(PI / 4.0)),
163+
));
164+
}
165+
```
166+
167+
### `duplicate_bevy_dependencies`
168+
169+
Finally, `duplicate_bevy_dependencies` checks if you're depending on multiple versions of Bevy in the same crate. Since [Cargo lets projects use several major versions of the same crate](https://doc.rust-lang.org/cargo/reference/resolver.html#semver-compatibility), it is really easy to accidentally pull in more than one version of `bevy`. A common example of this is when your project depends on a 3rd-party plugin that uses an older version of Bevy:
170+
171+
```toml
172+
[dependencies]
173+
bevy = "0.15"
174+
# This version of `leafwing-input-manager` actually requires Bevy 0.14!
175+
leafwing-input-manager = "0.15"
176+
```
177+
178+
While at first using the above dependencies will appear as if nothing is wrong, trying to mix `leafwing-input-manager`'s types with a newer version of Bevy will result in an error:
179+
180+
```rust
181+
use bevy::prelude::*;
182+
use leafwing_input_manager::plugin::AccumulatorPlugin;
183+
184+
fn main() {
185+
App::new()
186+
// Error: `AccumulatorPlugin` does not implement `Plugin`!
187+
.add_plugins(AccumulatorPlugin)
188+
.run();
189+
}
190+
```
191+
192+
Developers who first run into this error likely think: "That doesn't make since! `AccumulatorPlugin` is definitely a `Plugin`!" While that may be true, `AccumularPlugin` only implements Bevy 0.14's `Plugin` trait, not Bevy 0.15's `Plugin` trait, which was expected. The Rust compiler treats those two traits as distinct, which is why it raised an error.
193+
194+
<details>
195+
<summary>See a real life example of this error...</summary>
196+
197+
```shell
198+
$ cargo check
199+
error[E0277]: the trait bound `AccumulatorPlugin: Plugins<_>` is not satisfied
200+
--> src/main.rs:6:22
201+
|
202+
6 | .add_plugins(AccumulatorPlugin)
203+
| ----------- ^^^^^^^^^^^^^^^^^ the trait `app::plugin::sealed::Plugins<_>` is not implemented for `AccumulatorPlugin`
204+
| |
205+
| required by a bound introduced by this call
206+
|
207+
note: there are multiple different versions of crate `bevy_app` in the dependency graph
208+
--> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_app-0.15.3/src/plugin.rs:136:5
209+
|
210+
136 | pub trait Plugins<Marker> {
211+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ this is the required trait
212+
|
213+
::: src/main.rs:1:5
214+
|
215+
1 | use bevy::prelude::*;
216+
| ---- one version of crate `bevy_app` used here, as a dependency of crate `bevy_internal`
217+
2 | use leafwing_input_manager::plugin::AccumulatorPlugin;
218+
| ---------------------- one version of crate `bevy_app` used here, as a dependency of crate `bevy_internal`
219+
|
220+
::: ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/leafwing-input-manager-0.15.1/src/plugin.rs:320:1
221+
|
222+
320 | pub struct AccumulatorPlugin;
223+
| ---------------------------- this type doesn't implement the required trait
224+
|
225+
::: ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_app-0.14.2/src/app.rs:26:1
226+
|
227+
26 | / bevy_ecs::define_label!(
228+
27 | | /// A strongly-typed class of labels used to identify an [`App`].
229+
28 | | AppLabel,
230+
29 | | APP_LABEL_INTERNER
231+
30 | | );
232+
| |_- this is the found trait
233+
= help: you can use `cargo tree` to explore your dependency tree
234+
= note: required for `AccumulatorPlugin` to implement `Plugins<_>`
235+
note: required by a bound in `bevy::prelude::App::add_plugins`
236+
--> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_app-0.15.3/src/app.rs:548:52
237+
|
238+
548 | pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
239+
| ^^^^^^^^^^ required by this bound in `App::add_plugins`
240+
241+
For more information about this error, try `rustc --explain E0277`.
242+
```
243+
244+
</details>
245+
246+
To fix this, you can update your dependencies to all use the same version of Bevy. Many 3rd-party plugins provide a "compatibility table" that makes it easy to reference which plugins versions work with which Bevy versions:
247+
248+
```toml
249+
[dependencies]
250+
bevy = "0.15"
251+
leafwing-input-manager = "0.16"
252+
```
253+
254+
## Fixed `bevy_lint` on Windows
255+
256+
As you may know, the linter requires [Rustup](https://rustup.rs/) to be installed in order to function. Internally, [it was calling the following command](https://github.com/TheBevyFlock/bevy_cli/blob/61954e35d2cb56beae53325afaaacd21647b7b55/bevy_lint/src/bin/main.rs#L24-L37) to make Cargo check over projects with `bevy_lint`:
257+
258+
```shell
259+
$ export RUSTC_WORKSPACE_WRAPPER=path/to/bevy_lint_driver
260+
$ rustup run nightly-2025-02-20 cargo check
261+
```
262+
263+
`rustup run` is great because it handles setting the `PATH` and `LD_LIBRARY_PATH` variables for us. These environmental variables are crucial in helping `bevy_lint_driver` discover `librustc_driver.so`, the dynamic library that the linter uses to interface with the compiler.
264+
265+
As I daily drive both Linux and MacOS, I made sure to test the linter on those platforms to ensure it worked correctly. Unfortunately I didn't test it on Windows, as I just assumed that it would work the same!
266+
267+
_It did not work the same._[^3]
268+
269+
[^3]: Which, looking back, makes complete sense. Linux and MacOS are much more similar to each other than Windows, so if any of them were going to operate differently, it was going to be Windows. I hoped `rustup run` would hide any of these details so I wouldn't need to worry about it, but unfortunately that isn't the case.
270+
271+
When trying to call `bevy_lint` v0.1.0 on Windows, it raises the following error:
272+
273+
```powershell
274+
> bevy_lint
275+
error: process didn't exit successfully: `\\?\C:\Users\USER\.cargo\bin\bevy_lint_driver.exe C:\Users\USER\.rustup\toolchains\nightly-2024-11-14-aarch64-pc-windows-msvc\bin\rustc.exe -vV` (exit code: 0xc0000135, STATUS_DLL_NOT_FOUND)
276+
Check failed: exit code: 101.
277+
```
278+
279+
The error says `STATUS_DLL_NOT_FOUND`; somehow `bevy_lint_driver` wasn't able to find `rustc_driver.dll`. It took quite some time and a bit of digging for me to discover the issue, but I eventually stumbled upon [this forum post](https://internals.rust-lang.org/t/help-test-windows-behavior-between-rustup-and-cargo/20237). Turns out `rustup run` _does not_ modify the `PATH` variable by default on Windows, since it breaks [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html).
280+
281+
Thankfully, there is a quick fix: setting `RUSTUP_WINDOWS_PATH_ADD_BIN=1` forces Rustup to modify the `PATH`:
282+
283+
```powershell
284+
> set RUSTUP_WINDOWS_PATH_ADD_BIN=1
285+
> bevy_lint
286+
Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.42s
287+
```
288+
289+
This is now automatically set in v0.2.0, so Windows should now work without any extra steps. Nice!
290+
291+
## Conclusion
292+
293+
There are several other changes that I have not covered, but I highly recommend reading them in [the changelog](https://github.com/TheBevyFlock/bevy_cli/blob/main/bevy_lint/CHANGELOG.md). I also recommend looking at [the migration guide](https://github.com/TheBevyFlock/bevy_cli/blob/main/bevy_lint/MIGRATION.md) if you used to use v0.1.0 and are planning on upgrading.
294+
295+
I would also like to thank several contributors who helped develop the v0.2.0 release of the linter:
296+
297+
- [DaAlbrecht](https://github.com/DaAlbrecht), who [implemented `insert_unit_bundle`](https://github.com/TheBevyFlock/bevy_cli/pull/210), added support for [linting qualified methods](https://github.com/TheBevyFlock/bevy_cli/pull/253), merged [`panicking_query_methods` and `panicking_world_methods`](https://github.com/TheBevyFlock/bevy_cli/pull/271), reviewed several PRs, and helped many on Discord
298+
- [TimJentzsch](https://github.com/TimJentzsch), who has been hard at work building the linter's sibling project, the [Bevy CLI](https://github.com/TheBevyFlock/bevy_cli). Tim has provided valuable feedback and is a consistent reviewer of PRs.
299+
- [MrGVSV](https://github.com/MrGVSV), who [wrote the `borrowed_reborrowable`](https://github.com/TheBevyFlock/bevy_cli/pull/164) lint. (He's also the driving force behind `bevy_reflect`!)
300+
301+
If you would like to try contributing yourself, please check out the [Contributor's Guide](https://github.com/TheBevyFlock/bevy_cli/tree/main/bevy_lint/docs)! We're super welcome to new contributors, and love any help we can get!
302+
303+
That's all for today. Thank you for your time!
304+
305+
\- BD103 :)

src/styles/main.css

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
}
4040

4141
pre {
42-
max-width: 40em;
42+
background-color: var(--turquoise-darker);
43+
font-size: 11pt;
44+
padding: 1em;
4345
overflow-x: scroll;
4446
}
4547
}

0 commit comments

Comments
 (0)