bevy_lint
is a custom linter for the Bevy game engine, similar to Clippy.
Warning
This is an unofficial community project, hacked upon by the Bevy CLI working group until it is eventually upstreamed into the main Bevy Engine organization. Pardon our rough edges, and please consider submitting an issue if you run into trouble!
bevy_lint
depends on a pinned nightly version of Rust with the rustc-dev
Rustup component. This is because bevy_lint
uses internal rustc
crates that can only be imported with the permanently-unstable rustc_private
feature. You can refer to the compatibility table to see which version of the linter requires which toolchain.
You can install the toolchain with:
rustup toolchain install $TOOLCHAIN_VERSION \
--component rustc-dev \
--component llvm-tools-preview
For example, you would replace $TOOLCHAIN_VERSION
with nightly-2024-11-14
if you were installing bevy_lint
0.1.0, based on the compatibility table. Please be aware that you must keep this toolchain installed for bevy_lint
to function1.
Once you have the toolchain installed, you can compile and install bevy_lint
through cargo
:
rustup run $TOOLCHAIN_VERSION cargo install \
--git https://github.com/TheBevyFlock/bevy_cli.git \
--tag $TAG \
--locked \
bevy_lint
Make sure to replace $TOOLCHAIN_VERSION
and $TAG
in the above command. The tag for a specific release can be found in the releases tab. For example, the tag for 0.1.0 is lint-v0.1.0
.
bevy_lint
has the same API as the cargo check
command:
bevy_lint --help
If you have the Bevy CLI installed, the linter is also available through the lint
subcommand:
bevy lint --help
Note
bevy_lint
checks your code with the nightly toolchain it was installed with, meaning you do have access to unstable features when it is called. This is best used when detectingbevy_lint
.
The linter passes --cfg bevy_lint
when it checks your code, allowing you to detect it:
// Conditionally include this function only when `bevy_lint` is used.
#[cfg(bevy_lint)]
fn foo() {
// ...
}
// Conditionally add an attribute only when `bevy_lint` is used.
#[cfg_attr(bevy_lint, ...)]
struct Foo;
If you use this, you may also need to register bevy_lint
as a valid cfg
flag in your Cargo.toml
:
[lints.rust]
unexpected_cfg = { level = "warn", check-cfg = ["cfg(bevy_lint)"] }
When you run bevy_lint
on a project, rustc
knows an exact list of all bevy::
lints registered. With this it can detect that bevy::missing_reflect
is valid and bevy::uh_oh
isn't, and emit a corresponding warning.
When you run normal cargo check
, however, it does not know about any bevy::
lints. In order to avoid erroring on all usages of bevy::
, but to still provide good diagnostics on typos, the #![register_tool(...)]
attribute was introduced.
// Note that this is nightly-only. We'll get to that in a second!
#![register_tool(bevy)]
Using #![register_tool(bevy)]
tells the compiler that bevy
is a valid name in attributes, even if it does not know what bevy
is.2 When cargo check
now runs over a project with #[warn(bevy::lint_name)]
, it will simply skip it instead of emitting an error. (But running bevy_lint
will still detect and check this attribute as normal.)
If you wish to refer to a bevy
lint at all in your code or configuration (usually to toggle it), you must add #![register_tool(bevy)]
to each crate root. Unfortunately, #![register_tool(...)]
is currently unstable, meaning you need to add #![feature(register_tool)]
to your code as well. This isn't an issue if you detect when bevy_lint
is enabled, since it is guaranteed to check your code using nightly Rust.
// When `bevy_lint` is used, enable the `register_tool` feature and register the `bevy` namespace
// as a tool.
#![cfg_attr(bevy_lint, feature(register_tool), register_tool(bevy))]
Tip
If your project already uses nightly Rust, you can forego the
#[cfg_attr(bevy_lint, ...)]
attributes and write#![feature(register_tool)]
and#![register_tool(bevy)]
directly!
If you wish to enable and disable certain lints, you must first register bevy
as a tool. Not doing so will cause #[allow(bevy::lint_name)]
and related attributes to fail to compile.
You can now toggle lints throughout your project, as long as they too are behind #[cfg_attr(bevy_lint, ...)]
:
#![cfg_attr(bevy_lint, feature(register_tool), register_tool(bevy))]
// Enable pedantic lints, which are off by default.
#![cfg_attr(bevy_lint, warn(bevy::pedantic))]
// Deny methods of `World` in this system that can panic when a non-panicking alternative exists.
#[cfg_attr(bevy_lint, deny(bevy::panicking_world_methods))]
fn my_critical_system(world: &mut World) {
// ...
}
There are several other ways to toggle lints, but they have varying levels of support:
Method | Support | Additional Information |
---|---|---|
#[allow(...)] and related |
✅ | Must be behind #[cfg_attr(bevy_lint, ...)] on stable Rust. |
[lints.bevy] in Cargo.toml |
Nightly only because #[register_tool(bevy)] must always be enabled. |
|
[workspace.lints.bevy] in Cargo.toml |
Nightly only (same as [lints.bevy] ) and prints a warning each time cargo is executed. |
|
RUSTFLAGS="-A bevy::lint" |
❌ | RUSTFLAGS applies to dependencies, but they do not have #[register_tool(bevy)] . |
bevy_lint Version |
Rust Version | Rustup Toolchain | Bevy Version |
---|---|---|---|
0.2.0-dev | 1.84.0 | nightly-2024-11-28 |
0.14 |
0.1.0 | 1.84.0 | nightly-2024-11-14 |
0.14 |
The Rust version in the above table specifies what version of the Rust language can be compiled with bevy_lint
. Code written for a later version of Rust may not compile. (This is not usually an issue, though, because bevy_lint
's Rust version is kept 1 to 2 releases ahead of stable Rust.)
The Rustup toolchain specifies which toolchain must be installed in order for bevy_lint
to be installed and used. Please see the installation section for more info.
The Bevy version is a range of Bevy versions that bevy_lint
has been tested with and is guaranteed to work. Newer or older releases may not be linted correctly and may cause the linter to crash. (If this does happen for you, please consider submitting a bug report!)
The Bevy Linter is licensed under either of
- Apache License, Version 2.0 (
LICENSE-APACHE
or http://www.apache.org/licenses/LICENSE-2.0) - MIT license (
LICENSE-MIT
or http://opensource.org/licenses/MIT)
at your option.
Please see CONTRIBUTING.md
for the CLI for more information! There is also a linter-specific contributing guide in the docs
folder.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Footnotes
-
bevy_lint
imports internalrustc
libraries in order to hook into the compiler process. These crates are stored in a dynamic library that is installed with therustc-dev
component and loaded bybevy_lint
at runtime. Uninstalling the nightly toolchain would remove this dynamic library, causingbevy_lint
to fail. ↩ -
If you've ever used
#[rustfmt::skip]
in your code, this is howrustc
avoids erroring on it. However unlike thebevy
namespace,rustfmt
is registered automatically without a need for#![register_tool(rustfmt)]
due to it being an official tool. ↩