Introduction

This is the combined documentation for the prototype Bevy CLI and Bevy Linter projects, tools designed to improve the experience developing apps using the Bevy game engine. To get started, take a look about the about pages for one of the tools:

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 or reaching out in the Bevy Discord if you run into trouble!

Bevy CLI (Alpha)

The CLI is a prototype tool intended to streamline common tasks when working on projects. Please see the initial scope document and original issue for history and motivation. The CLI's current features include:

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 or reaching out in the Bevy Discord if you run into trouble!

Installation

As the CLI is currently an unofficial tool, it is not yet published to https://crates.io. It is available on Github, however.

Precompiled Binary

The CLI is precompiled for Linux, Windows, and MacOS. You may install the latest precompiled binary using cargo-binstall:

cargo binstall --git https://github.com/TheBevyFlock/bevy_cli --locked bevy_cli

You can manually download the precompiled binaries from the release page.

Build from Source

You may compile the CLI from scratch using cargo install. To install the latest release, make sure to specify the version you wish in the tag (ex. --tag cli-v0.1.0-alpha.1).

cargo install --git https://github.com/TheBevyFlock/bevy_cli --tag cli-vX.Y.Z --locked bevy_cli

Bleeding Edge

Here be dragons! 🐉

You may run into bugs when using the unstable version of the CLI. You've been warned, and have fun! :)

If you want to try out the newest unstable features, you may install the CLI from the main branch:

cargo install --git https://github.com/TheBevyFlock/bevy_cli --branch main --locked bevy_cli

Quick Start

With the following steps, you can create a new 2D app with Bevy and run it in your browser:

  1. Create a new Bevy app using the 2D template:

    bevy new -t=2d my_bevy_app
    
  2. Navigate into the folder:

    cd my_bevy_app
    
  3. Check the code quality with the linter:

    bevy lint
    
  4. Run the app in the browser:

    bevy run web --open
    

Scaffolding

The bevy new command lets you easily scaffold a new Bevy project using a custom template or a minimal template provided by Bevy. Templates are just GitHub repositories and can be specified with the -t flag.

Usage

If the template is omitted, the default minimal template will be chosen.

bevy new my-project

Other built-in templates from TheBevyFlock will be usable via its shortcut form i.e. -t 2d will use the template bevy_new_2d.

bevy new my-project -t 2d

To use a specific template, provide the full GitHub URL:

bevy new my-project -t https://github.com/TheBevyFlock/bevy_new_2d.git

Web Apps

The CLI makes it easy to build and run web apps made with Bevy, using bevy build web and bevy run web. It takes care of compiling the app to Wasm, creating JavaScript bindings and serving it on a local web server to test it out. If you are missing any required tools, the CLI will ask you to install them for you automatically.

Note

The arguments you know from cargo (like --release) must be placed before the web subcommand, while the web-specific options (like --open) must be placed afterwards, e.g.

bevy run --release web --open

Running in the browser

Use the bevy run web command to run your app in the browser. The app will be automatically served on a local web server, use the --open flag to automatically open it in the browser.

The server will provide a default index.html serving as entrypoint for your app. It features a loading screen and some other utilities. If you want to customize it, simply create a web/index.html file to override the default behavior. Other files in the web folder will also be included in your application. You can view the default index.html here.

Creating web bundles

To deploy your app on a web server, it's often necessary to bundle the binary, assets and web files into a single folder. Using bevy build web --bundle, the CLI can create this bundle for you automatically. It will be available in the target/bevy_web folder, see the command's output for the full file path.

Compilation profiles

Web apps have different needs than native builds when it comes to compilation. For example, binary size is a lot more important for web apps, as it has a big influence on the loading times.

The Bevy CLI provides custom web and web-release compilation profiles, which are optimized for web apps. They are used by default for the web sub-commands (depending on the --release flag).

The profiles can be customized as usual in Cargo.toml:

[profile.web-release]
inherits = "release"
opt-level = "z"

Alternatively, you can change the profile entirely, e.g. bevy run --profile=foo web.

Feature configuration

Often, you want to enable certain features only in development mode or only for native and not web builds. In your Cargo.toml you can enable features or disable default features depending on platform (native/web) and profile (dev/release):

[package.metadata.bevy_cli.native.dev]
features = [
    # Enable asset hot reloading for native dev builds.
    "bevy/file_watcher",
    # Enable embedded asset hot reloading for native dev builds.
    "bevy/embedded_watcher",
]

[package.metadata.bevy_cli.web]
# Disable default features for web builds
default-features = false

Usage in CI

The CLI may include interactive prompts if parts of the required tooling is not installed on the system. These prompts will break your pipeline if they are triggered in CI.

To avoid this problem, use the --yes flag to automatically confirm the prompts:

bevy build --yes web

Default index.html

This is the default index.html that bevy build web and bevy run web uses to load your application. You may customize index.html by creating a web/index.html to override the default. The default is provided below, so you can copy it instead of writing your own from scratch:

Warning

The default index.html has the following line:

import init from "./build/bevy_app.js";

You will need to replace bevy_app with the name of your compiled binary. This is usually your crate's name, but it can be customized in Cargo.toml with the [[bin]] table, so be careful!

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <style>
      /* Styles for the loading screen */
      :root {
        --web-bg-color: #2b2c2f;
        --web-color: white;
      }

      * {
        margin: 0;
        padding: 0;
        border: 0;
      }

      html,
      body {
        width: 100%;
        height: 100%;
      }

      .center {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
      }

      body {
        background-color: var(--web-bg-color);
        color: var(--web-color);
      }

      .spinner {
        width: 128px;
        height: 128px;
        border: 64px solid transparent;
        border-bottom-color: #ececec;
        border-right-color: #b2b2b2;
        border-top-color: #787878;
        border-radius: 50%;
        box-sizing: border-box;
        animation: spin 1.2s linear infinite;
      }

      @keyframes spin {
        0% {
          transform: rotate(0deg);
        }

        100% {
          transform: rotate(360deg);
        }
      }
    </style>
  </head>

  <body class="center">
    <noscript>JavaScript support is required to run this app</noscript>
    <div id="loading-screen" class="center">
      <span class="spinner"></span>
    </div>

    <script type="module">
      // Automatically restart the audio context after user interaction
      // Needs to be executed _before_ the game is loaded
      // Taken from https://developer.chrome.com/blog/web-audio-autoplay/#moving-forward
      (function () {
        // An array of all contexts to resume on the page
        const audioContextList = [];

        // An array of various user interaction events we should listen for
        const userInputEventNames = [
          "click",
          "contextmenu",
          "auxclick",
          "dblclick",
          "mousedown",
          "mouseup",
          "pointerup",
          "touchend",
          "keydown",
          "keyup",
        ];

        // A proxy object to intercept AudioContexts and
        // add them to the array for tracking and resuming later
        self.AudioContext = new Proxy(self.AudioContext, {
          construct(target, args) {
            const result = new target(...args);
            audioContextList.push(result);
            return result;
          },
        });

        // To resume all AudioContexts being tracked
        function resumeAllContexts(event) {
          let count = 0;

          audioContextList.forEach((context) => {
            if (context.state !== "running") {
              context.resume();
            } else {
              count++;
            }
          });

          // If all the AudioContexts have now resumed then we
          // unbind all the event listeners from the page to prevent
          // unnecessary resume attempts
          if (count == audioContextList.length) {
            userInputEventNames.forEach((eventName) => {
              document.removeEventListener(eventName, resumeAllContexts);
            });
          }
        }

        // We bind the resume function for each user interaction
        // event on the page
        userInputEventNames.forEach((eventName) => {
          document.addEventListener(eventName, resumeAllContexts);
        });
      })();
    </script>

    <script type="module">
      // Starting the game

      // When this file is used as the default `index.html`, the CLI will automatically replace
      // `bevy_app.js` with the name of the generated JS entrypoint. If you copy this file and
      // customize it, you will need to manually change the name. For more information, please see
      // <https://thebevyflock.github.io/bevy_cli/cli/web/default-index-html.html>!
      import init from "./build/bevy_app.js";
      init().catch((error) => {
        if (
          !error.message.startsWith(
            "Using exceptions for control flow, don't mind me. This isn't actually an error!"
          )
        ) {
          throw error;
        }
      });
    </script>

    <script type="module">
      // Hide loading screen when the game starts.
      const loading_screen = document.getElementById("loading-screen");
      const observer = new MutationObserver((records) => {
        for (const record of records) {
          for (const addedNode of record.addedNodes) {
            if (addedNode instanceof HTMLCanvasElement) {
              if (addedNode.innerText.trim().length === 0) {
                // Add compatibility note
                addedNode.innerText =
                  "Canvas support is required to run this app";
              }

              // A new canvas has been created, which means that the game has been loaded
              // Hide the loading screen!
              loading_screen.style.display = "none";
              observer.disconnect();
              return;
            }
          }
        }
      });

      observer.observe(document.body, {
        subtree: false,
        childList: true,
        attributes: false,
        characterData: false,
      });
    </script>
  </body>
</html>

Linter

The CLI has 1st-party support for bevy_lint, the static analysis tool that checks over your code.

bevy lint

If you do not have the linter installed already, the CLI can install it for you when you first run the command. Calling the lint subcommand is equivalent to calling bevy_lint, and supports the same arguments as cargo check:

bevy lint --workspace --all-features

You can view a full list of options with:

bevy lint -- --help

Configuration

The CLI can be configured on a per project basis in the Cargo.toml under the metadata section.

[package.metadata.bevy_cli]

The CLI supports two targets:

  • native: [package.metadata.bevy_cli.native]
  • web: [package.metadata.bevy_cli.web]

For both these targets a release and dev profile exists that will automatically be chosen by the CLI:

Profile NameConfiguration Section
release[package.metadata.bevy_cli.native.release]
dev[package.metadata.bevy_cli.native.dev]
web-release[package.metadata.bevy_cli.web.release]
web[package.metadata.bevy_cli.web.dev]

Note

The Web profiles inherits from their native counterpart

Configuration Merging

Configuration is merged in the following order (from general to specific):

  1. Base config: [package.metadata.bevy_cli]
  2. Profile config [package.metadata.bevy_cli.{dev|release)]
  3. Target config: [package.metadata.bevy_cli.{native|web}]
  4. Target + Profile config: [package.metadata.bevy_cli.{native|web}.{dev|release}]

Example

The following Cargo.toml

[package.metadata.bevy_cli]
default-features = true

[package.metadata.bevy_cli.web]
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]

[package.metadata.bevy_cli.web.release]
wasm-opt = true

[package.metadata.bevy_cli.release]
default-features = false

When building for web in release mode, the final merged configuration will be:

# merged from `[package.metadata.bevy_cli.web]`
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]
# merged from `[package.metadata.bevy_cli.web.release]`
wasm-opt = true
# merged from `[package.metadata.bevy_cli.release]`
default-features = false

When building for native dev, the final merged configuration will be:

# merged from [package.metadata.bevy_cli] (default-features are enabled by default if not explicitly turned off)
default-features = true

Configuration Reference

The following fields exist and can be configured:

features

  • Type: array of strings
  • Default: none
  • Note: Which features to use.

default-features

rustflags

  • Type: string or array of strings
  • Default: none
  • Note: Extra command-line flags to pass to rustc. The value may be an array of strings or a space-separated string.

wasm-opt

  • Type: boolean
  • Default: true for release web builds
  • Note: Whether or not to use wasm-opt to optimize the web binary.

Troubleshooting

If you encounter issues or don't understand what the CLI is doing, try the --verbose flag. Every command that the CLI executes will be logged, making it easy to understand what's going on!

Changelog

All notable user-facing changes to the Bevy CLI will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

All Changes: cli-v0.1.0-alpha.1...main

v0.1.0-alpha.1 - 2025-05-23

All Changes: cli-v0.1.0-alpha.1

Added

  • bevy new: create new projects from a template using cargo-generate (#2)
  • bevy lint: invoke the linter if bevy_lint is installed (#4)
  • bevy build and bevy run: build and run your program with Bevy-specific configuration (#76, #103, #102, #120)
    • You can use bevy build web and bevy run web to build and run your program for the web using Wasm.
    • Web binaries can be optimized with wasm-opt (#206, #430)
    • You can pass --bundle to pack all files needed for the web into a single folder (#195)
  • bevy completions: generate terminal auto-complete scripts for a variety of shells (#265)
  • The CLI can be configured with [package.metadata.bevy_cli] (#331, #355, #351)

Migration Guide

Occasionally changes are made to the Bevy CLI that may break existing projects, or majorly change how it is intended to be used. This document provides a guide recommending how to upgrade your existing project to a newer version of the CLI.

To actually install the new version of the CLI, please see the docs and the releases page. Note that some changes listed here are optional (and will be explicitly marked as such). If you ever run into issues while upgrading, please feel free to submit an issue.

Note

This document is empty for now, and will be until v0.2.0 of the CLI is released with actual breaking changes. Check back later :)

About

bevy_lint is a custom linter for the Bevy game engine, similar to Clippy.

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 or reaching out in the Bevy Discord if you run into trouble!

Installation

CLI

The CLI supports automatically installing the latest released version of the linter if you do not have it installed already. Make sure you have the CLI first, then simply run the lint subcommand:

bevy lint

The CLI will prompt you if you wish to install the linter. Type y and press enter to accept:

warning: failed to run bevy_lint, trying to find automatic fix...
`bevy_lint` is missing, should I install it for you? [y/n]

If you want to auto-confirm the prompt, you may pass --yes to the command. Note that if you are installing the linter in CI, you may wish to use the dedicated Github Action instead:

bevy lint --yes

Manual

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 v0.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 v0.1.0 is lint-v0.1.0.

Uninstall

If you wish to uninstall the linter at any time, you may use Cargo and Rustup to do so:

cargo uninstall bevy_lint
rustup toolchain uninstall $TOOLCHAIN_VERSION

Upgrade

To upgrade to a newer version of the linter, first uninstall it then follow the CLI or manual installation instructions.


  1. bevy_lint imports internal rustc libraries in order to hook into the compiler process. These crates are stored in a dynamic library that is installed with the rustc-dev component and loaded by bevy_lint at runtime. Uninstalling the nightly toolchain would remove this dynamic library, causing bevy_lint to fail.

Usage

bevy_lint has the same API as the cargo check command:

bevy_lint --help

If you have the prototype 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 detecting bevy_lint.

Toggling Lints in Cargo.toml

You can set the default level for lints in a Cargo.toml using the [package.metadata.bevy_lint] table:

[package.metadata.bevy_lint]
# Make the `missing_reflect` lint a warning.
missing_reflect = "warn"
# Make the `panicking_methods` lint an error that cannot be `#[allow(...)]`d.
panicking_methods = { level = "forbid" }

You can configure lints for an entire workspace by using [workspace.metadata.bevy_lint] in the root Cargo.toml instead:

[workspace.metadata.bevy_lint]
# Enable the entire `pedantic` lint group, and make them all warnings.
pedantic = "warn"

Crate lint configuration is merged with workspace lint configuration, with crate lint configuration taking priority.

Note that unlike with Cargo's [lints] table, the priority field is not supported. Furthermore, if you wish to use #[allow(...)] and related attributes inside your code for Bevy-specific lints, please see Toggling Lints in Code.

Detecting bevy_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)"] }

Registering bevy as a Tool

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.1 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 (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 `bevy` 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! Cool!


  1. If you've ever used #[rustfmt::skip] in your code, this is how rustc avoids erroring on it. However unlike the bevy namespace, rustfmt is registered automatically without a need for #![register_tool(rustfmt)] due to it being an official tool.

Toggling Lints in Code

It is possible to set lint levels on a case-by-case basis inside your code, but it requires a few more steps than setting the levels for the entire crate in Cargo.toml. First, you must register bevy as a tool. Not doing so will cause #[allow(bevy::lint_name)] and related attributes to fail to compile.

Once bevy is registered, you can toggle lints throughout your code, as long as they too are behind #[cfg_attr(bevy_lint, ...)]:

#![cfg_attr(bevy_lint, feature(register_tool), register_tool(bevy))]

// Enable the `pedantic` lint group, which is off by default.
#![cfg_attr(bevy_lint, warn(bevy::pedantic))]

// Deny panicking Bevy methods in this system when a non-panicking alternatives exist.
#[cfg_attr(bevy_lint, deny(bevy::panicking_methods))]
fn my_critical_system(world: &mut World) {
    // ...
}

There are several other ways to toggle lints, although some have varying levels of support:

MethodSupportAdditional Information
[package.metadata.bevy_lint] in Cargo.tomlSee Toggling Lints in Cargo.toml.
[workspace.metadata.bevy_lint] in Cargo.tomlSee Toggling Lints in Cargo.toml.
#[allow(...)] and relatedMust be behind #[cfg_attr(bevy_lint, ...)] on stable Rust.
[lints.bevy] in Cargo.toml⚠️Nightly only because #[register_tool(bevy)] must always be enabled. Prints a warning each time cargo is run.
[workspace.lints.bevy] in Cargo.toml⚠️Same as [lints.bevy].
RUSTFLAGS="-A bevy::lint"RUSTFLAGS applies to dependencies, but they do not have #[register_tool(bevy)].

Compatibility

bevy_lint VersionRust VersionRustup ToolchainBevy Version
0.4.0-dev1.88.0nightly-2025-04-030.16
0.3.01.88.0nightly-2025-04-030.16
0.2.01.87.0nightly-2025-02-200.15
0.1.01.84.0nightly-2024-11-140.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!)

Github Actions

bevy_lint provides an action to conveniently install the linter in CI:

# Replace `lint-vX.Y.Z` with the tag of the version installed, such as `lint-v0.3.0`.
- name: Install `bevy_lint`
  uses: TheBevyFlock/bevy_cli/bevy_lint@lint-vX.Y.Z

- name: Run `bevy_lint`
  run: bevy_lint --workspace

You may install the unstable, bleeding-edge version from the main branch:

- name: Install `bevy_lint`
  uses: TheBevyFlock/bevy_cli/bevy_lint@main

Note that this action overrides the default toolchain and configures it to be the nightly version specified in the compatibility table. If you previously installed another Rustup toolchain, you may wish to reconfigure it to be the default:

# Sets the default toolchain to be stable Rust.
- name: Install stable Rust
  uses: dtolnay/rust-toolchain@stable

# Overrides the default toolchain to be nightly Rust.
- name: Install `bevy_lint`
  uses: TheBevyFlock/bevy_cli/bevy_lint@lint-vX.Y.Z

# Resets the default toolchain back to stable Rust.
- name: Configure the default Rust toolchain
  run: rustup default stable

Important

The action is only available for versions v0.3.0 and onward. v0.2.0 and v0.1.0 will not work, however you may emulate it by manually running the installation commands in your workflow.

Changelog

All notable user-facing changes to the Bevy Linter will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

All Changes: lint-v0.3.0...main

v0.3.0 - 2025-04-30

All Changes: lint-v0.2.0...lint-v0.3.0

Added

  • Lint iter_current_update_events to suspicious (#314)
  • Lint unconventional_naming to style (#345)
    • plugin_not_ending_in_plugin has been merged into this new lint.
  • A Github Action to automatically install the linter (#380)

Changed

  • The linter now supports Bevy 0.16, but no longer supports Bevy 0.15 (#323)
  • Bumped nightly toolchain to nightly-2025-04-03 (#373)
    • The linter now supports Rust 1.88.0.
  • Moved lints into submodules for their corresponding lint groups (#321)
    • This makes it easier to see what lint group a lint is under in the documentation. For example, in v0.2.0 if you wanted to view the insert_unit_bundle lint you would go to bevy_lint::lints::insert_unit_bundle, but in v0.3.0 you would go to bevy_lint::lints::suspicious::insert_unit_bundle. This signals that insert_unit_bundle is a suspicious lint.
  • Moved lint group docs from bevy_lint::groups to their associated bevy_lint::lints submodules (#328)
  • Code generated from external macros are no longer linted (#263)
    • External macros are macros that are defined in a separate crate from the one being linted. The output of these macros is skipped for all lints, as it was previously impossible to fix the warnings without an #[allow(...)] attribute.
  • missing_reflect now emits machine-applicable suggestions if all fields in a type implement PartialReflect (#389)

Removed

  • Lint plugin_not_ending_in_plugin (#345)
    • This lint has been merged into the new unconventional_naming lint.

Fixed

  • main_return_without_appexit no longer fires if the AppExit is used (#346)
    • The goal of the lint is to encourage the AppExit to be handled, although returning it from main() is just one solution. This fix prevents the lint from yelling at you if you choose to handle it a different way, or simply choose to discard it with let _ = app.run();.
  • Fixed the Rust version in the compatibility table for v0.2.0 (#363)

v0.2.0 - 2025-03-19

All Changes: lint-v0.1.0...lint-v0.2.0

Added

  • Lint borrowed_reborrowable to pedantic (#164)
  • Lint insert_unit_bundle to suspicious (#210)
  • Lint configuration in Cargo.toml (#251)
  • Support for bevy_lint --version (#257)
  • Support for qualified method syntax in several lints (#253)
  • Lint duplicate_bevy_dependencies (#280)

Changed

  • The linter now supports Bevy 0.15, but no longer supports Bevy 0.14 (#191)
    • Eventually the linter will support multiple versions of Bevy at the same time. Please see #138 for more information.
  • Bumped nightly toolchain to nightly-2025-02-20 (#278)
  • Lowered zst_query lint from restriction to nursery (#261)
    • zst_query does not respect QueryData::Item, meaning it is broken for queries like Has<T> and AnyOf<T>. Please see #279 for more information.
  • Merged panicking_query_methods and panicking_world_methods into a single lint: panicking_methods (#271)

Fixed

  • rustc_driver.dll not found on Windows (#281)
    • bevy_lint should now work on Windows, as it was previously broken by this bug.

v0.1.0 - 2024-11-17

All Changes: lint-v0.1.0

Added

  • Lint main_return_without_appexit to pedantic (#84)
  • Lint insert_event_resource to suspicious (#86)
  • Lint groups correctness, suspicious, complexity, performance, style, pedantic, restriction, and nursery (#98)
  • Lints panicking_query_methods and panicking_world_methods to restriction (#95)
  • Lint plugin_not_ending_in_plugin to style (#111)
  • Lint missing_reflect to restriction (#139)
  • Lint zst_query to restriction (#168)

Migration Guide

Occasionally changes are made to the Bevy Linter that may break existing projects, or majorly change how it is intended to be used. This document provides a guide recommending how to upgrade your existing project to a newer version of the linter.

To actually install the new version of the linter, please see the docs and the releases page. Note that some changes listed here are optional (and will be explicitly marked as such). If you ever run into issues while upgrading, please feel free to submit an issue.

v0.2.0 to v0.3.0

Bevy 0.16 Support

The linter now supports Bevy 0.16, but no longer supports Bevy 0.15. You may still be able to run the linter successfully on Bevy 0.15 projects, but no guarantee is made on stability or correctness.

To migrate your code base to Bevy 0.16, please see the release post and migration guide.

Bumped Nightly Toolchain to nightly-2025-04-03

The linter now requires the nightly-2025-04-03 Rustup toolchain to be installed, instead of nightly-2025-02-20. The supported Rust language version is now 1.88.0 instead of the previous 1.87.0.

For more information on how to install this toolchain and its required components, please see the linter docs.

Removed plugin_not_ending_in_plugin

The plugin_not_ending_in_plugin lint has been removed in favor of the new unconventional_naming lint. unconventional_naming offers the same checks as plugin_not_ending_in_plugin, but now supports checking SystemSets as well.

If you reference plugin_not_ending_in_plugin in your code, a new warning will be emitted suggesting you rename it to unconventional_naming.

Created Github Action to install bevy_lint (Optional)

If you were manually installing bevy_lint in CI by following the installation instructions, you can now replace it with the new action:

- name: Install `bevy_lint`
  uses: TheBevyFlock/bevy_cli/bevy_lint@lint-v0.3.0

- name: Run `bevy_lint`
  run: bevy_lint --workspace

v0.1.0 to v0.2.0

Bevy 0.15 Support

The linter now supports Bevy 0.15, but no longer supports Bevy 0.14. You may still be able to run the linter successfully on Bevy 0.14 projects, but no guarantee is made on stability or correctness.

To migrate your code base to Bevy 0.15, please see the release post and migration guide.

Bumped Nightly Toolchain to nightly-2025-02-20

The linter now requires the nightly-2025-02-20 Rustup toolchain to be installed, instead of nightly-2024-11-14. The supported Rust language version is now 1.87.01 instead of the previous 1.84.0.

For more information on how to install this toolchain and its required components, please see the linter docs.

Merged panicking_query_methods and panicking_world_methods

The panicking_query_methods and panicking_world_methods lints have been merged into a single lint: panicking_methods. This new lint has the same functionality as the two previous lints combined.

#![allow(unused)]
fn main() {
// v0.1.0
#[cfg_attr(bevy_lint, deny(bevy::panicking_query_methods))]
fn critical_system(query: Query<&MyComponent>) {
    // ...
}

// v0.2.0
#[cfg_attr(bevy_lint, deny(bevy::panicking_methods))]
fn critical_system(query: Query<&MyComponent>) {
    // ...
}
}

Lowered zst_query from restriction to nursery

A critical bug was found in zst_query where it would incorrectly warn on certain queries that do actually query data, such as Has<T> and AnyOf<T>. As such, it has been temporarily moved to the nursery lint group, meaning that it is marked as unstable and may be removed.

Until #279 is fixed, it is recommended to remove references of this lint from your project.

Lint Configuration in Cargo.toml (Optional)

It is now possible to configure lints in a crate's Cargo.toml instead of directly in its code.

#![allow(unused)]
#![cfg_attr(bevy_lint, feature(register_tool), register_tool(bevy))]

fn main() {
// You can delete these attributes from your crate root.
#![cfg_attr(bevy_lint, warn(bevy::pedantic))]
#![cfg_attr(bevy_lint, deny(bevy::panicking_methods))]
}
# And add them to `Cargo.toml` instead.
[package.metadata.bevy_lint]
pedantic = "warn"
panicking_methods = "deny"

Lint levels specified in Cargo.toml will be applied for the entire crate, but can still be overridden in your code.


  1. As a nice side-effect, bevy_lint now officially supports Rust 2024.

How to Release the CLI

Kick-off Pull Request

  1. Review the changelog (CHANGELOG.md) and ensure that all notable changes have been documented.
  2. Replace Unreleased heading with the version with the format vX.Y.Z - YYYY-MM-DD.
  3. Update the **All Changes** link to compare from main to the new tag cli-vX.Y.Z. (E.g. cli-v0.1.0...main to cli-v0.1.0...cli-v0.2.0.)
  4. Review the migration guide (MIGRATION.md) and ensure all breaking / significant changes from the previous version are documented.
  5. Remove the -dev suffix from the version in Cargo.toml.
    • Please ensure that Cargo.lock also updates!
  6. Commit your changes and open a pull request.
  7. Merge the PR once a core Bevy maintainer approves it with no outstanding issues from other contributors.
    • This starts the release process, enacting a freeze on all other changes until the release has finished. While maintainers need to be aware of this so they do not merge PRs during this time, the release process should take less than an hour, so it's unlikely to ever be an issue.

Release on Github

  1. Create a new Github release.
  2. Set the tag to cli-vX.Y.Z.
  3. Set the title to `bevy_cli` - vX.Y.Z.
  4. Paste and fill out the following template into the release documentation:
<!-- One-sentence summary of changes. What awesome features can we spotlight? What critical bugs were fixed? -->

You can find the live documentation for this release [here](https://thebevyflock.github.io/bevy_cli/cli/index.html). You may also be interested in [the changelog] and [the migration guide].

<!-- Make sure to update the tags in these links to point to the correct version. -->

[the changelog]: https://github.com/TheBevyFlock/bevy_cli/blob/cli-vX.Y.Z/CHANGELOG.md
[the migration guide]: https://github.com/TheBevyFlock/bevy_cli/blob/cli-vX.Y.Z/MIGRATION.md

> [!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](https://github.com/bevyengine). Pardon our rough edges, and please consider [submitting an issue](https://github.com/TheBevyFlock/bevy_cli/issues) if you run into trouble!

You can install the precompiled CLI using `cargo-binstall`:

<!-- Update `X.Y.Z` with the correct version. -->

```sh
cargo binstall --git https://github.com/TheBevyFlock/bevy_cli --version X.Y.Z --locked bevy_cli
```

You may also compile the CLI yourself with `cargo install`:

<!-- Update `cli-vX.Y.Z` with the correct tag. -->

```sh
cargo install --git https://github.com/TheBevyFlock/bevy_cli --tag cli-vX.Y.Z --locked bevy_cli
```
  1. Check the pre-release box if this is an alpha release, then click "Save draft".
  2. Run the "Build CLI" workflow, and make sure to check the "Upload to release" box.
  3. Ensure that the workflow has successfully uploaded all executables to the draft release, then press "Publish release"!
  4. Announce the release on Discord and other social medias. Congrats!

Post-Release

  1. Add a new unreleased section to the top of the changelog (CHANGELOG.md) from the following template:
## Unreleased

<!-- Update `cli-vX.Y.Z` in the link to point to the latest release tag. -->

**All Changes**: [`cli-vX.Y.Z...main`](https://github.com/TheBevyFlock/bevy_cli/compare/cli-vX.Y.Z...main)
  1. Bump the version in Cargo.toml to the next -dev version, and ensure Cargo.lock also updates.
  2. Commit your changes and open a pull request.
  3. Merge the PR once it has been approved, unblocking the feature freeze.

About

Thank you for your interest in contributing to bevy_lint! Please feel free to take a peek at any of the articles in the table of context. These docs follow the Diátaxis documentation system1, meaning pages are organized based on how they're meant to be read.

  • Tutorial: Practice by doing something.
  • How-to Guides: Learn how to solve a specific problem.
  • Reference: Refer to technical knowledge while working.
  • Explanation: Learn the reasons behind certain decisions.

Important

This is the documentation for contributing to bevy_lint. If you want to learn how to use bevy_lint instead, please take a look at the user guide.

Additional Resources

⭐️ = Recommended Reading


  1. You may also know Diátaxis as the Divio documentation system. Diátaxis was developed while the author was working at Divio.

Tutorial

A tutorial is an experience that takes place under the guidance of a tutor. A tutorial is always learning-oriented.

A tutorial is a practical activity, in which the student learns by doing something meaningful, towards some achievable goal.

A tutorial serves the user's acquisition of skills and knowledge - their study. Its purpose is not to help the user get something done, but to help them learn.

A tutorial in other words is a lesson.

Diátaxis

How-To

How-to guides are directions that guide the reader through a problem or towards a result. How-to guides are goal-oriented.

A how-to guide helps the user get something done, correctly and safely; it guides the user’s action.

It's concerned with work - navigating from one side to the other of a real-world problem-field.

Diátaxis

Setup Your Editor

There can be a few extra steps required to get code completion and syntax highlighting setup with your editor.

Note

Can't find your editor here? Open an issue here! The rustc Development Guide may be a useful starting point, though several points won't apply to bevy_lint.

VSCode

bevy_lint works out-of-the-box with VSCode's rust-analyzer extension. The settings are specified in .vscode/settings.json:

{
    // Enables Rust-Analyzer support for `rustc` crates.
    "rust-analyzer.rustc.source": "discover",
    // Show dependency types while fuzzy searching, including types from `rustc` crates.
    "rust-analyzer.workspace.symbol.search.scope": "workspace_and_dependencies",
}

Neovim

First, setup rust-analyzer by following the instructions here. Next, install the neoconf.nvim plugin, which will automatically import the settings from .vscode/settings.json.

RustRover

As of December 2024, RustRover and the JetBrains Rust plugin do not work with rustc's internal crates. If you manage to get it working, make sure to submit an issue!

Work with Types

Note

This document assumes you are working with ty::Ty, not rustc_hir::Ty, unless explicitly stated otherwise.

Getting the Type of an Expression

It is possible to get the type of an expression (Expr) through TypeckResults:

#![allow(unused)]
fn main() {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
    let ty = cx.typeck_results().expr_ty(expr);
}
}

Peeling References

Often you have the type that may be behind one or more references, such as &&str or *mut [T], but you need to extract the underlying type (str and [T] in this case). You can do this by "peeling" references:

#![allow(unused)]
fn main() {
let peeled_ty = ty.peel_refs();
}

See Ty::peel_refs() for more information.

Getting the Adjusted Type of an Expression

The Rust compiler occasionally makes adjustments to types in order to support automatic dereferencing and type coercion. TypeckResults::expr_ty() ignores these adjustments, returning the original type. Sometimes this isn't desired, as you may want the adjusted type, in which case you should use TypeckResults::expr_ty_adjusted() instead:

#![allow(unused)]
fn main() {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
    let ty = cx.typeck_results().expr_ty_adjusted(expr);
}
}

For example, you may be writing a lint to check when a user calls str::contains(). In order to catch the most cases, you want to also check for method calls on types that dereference into str, such as String and Box<str>. expr_ty_adjusted() lets you treat String and Box<str> as str:

#![allow(unused)]
fn main() {
// `expr_ty()` is `&str`, `expr_ty_adjusted()` is `&str`.
let a = "Hello, world!".contains("Hello");

// `expr_ty()` is `String`, `expr_ty_adjusted()` is `&str`.
let b = String::from("Hello, world!").contains("Hello");

// `expr_ty()` is `Box<&str>`, `expr_ty_adjusted()` is `&str`.
let c = Box::new("Hello, world!").contains("Hello");
}

For more information, see Adjustment, Type coercions, and Method lookup.

Checking for a Specific Type

Often you have a Ty, and want to check if it matches a specific hardcoded type, such as Bevy's App. You can do this with clippy_utils's match_type() function:

#![allow(unused)]
fn main() {
use clippy_utils::ty::match_type;

// The absolute path to `App`'s definition.
const APP: [&str; 3] = ["bevy_app", "app", "App"];

if match_type(cx, ty, &APP) {
    // ...
}
}

All path constants are defined in paths.rs. If you add a new constant, place it there.

Important

bevy_app::app is a private module, but we still have to refer to it by name because struct App is within bevy_app/src/app.rs. Do not be tricked by re-exported types, such as bevy::prelude::App!

Getting ty::Ty from rustc_hir::Ty

Often you'll have an rustc_hir::Ty, but you need ty::Ty. This is a process known as lowering, and it is accomplished through the ty_from_hir_ty() function:

#![allow(unused)]
fn main() {
use clippy_utils::ty::ty_from_hir_ty;

fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &rustc_hir::Ty<'tcx, AmbigArg>) {
    let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty());
}
}

Also note that this conversion is one-directional and cannot be easily reversed. While rustc_hir::Tys are associated with a specific span of code, ty::Tys are not. For more information, please see rustc_hir::Ty vs ty::Ty from the rustc Dev Guide.

Parse Method Calls

A method call is a kind of expression, and can come in both receiver and qualified form:

#![allow(unused)]
fn main() {
// Both of these do the same thing!
receiver.method(args);
Struct::method(&receiver, args);
}

In order to parse a method call, you must first have an Expr. In this case we'll first implement LateLintPass's check_expr() method, but you can get an Expr through several other means:

#![allow(unused)]
fn main() {
impl<'tcx> LateLintPass<'tcx> for MyLintPass {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
        // ...
    }
}
}

Once you have an Expr, you can parse method calls with the MethodCall utility:

#![allow(unused)]
fn main() {
use crate::utils::hir_parse::MethodCall;

fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
    // Extract a `MethodCall` from the `Expr` if it is a method call.
    if let Some(MethodCall { receiver, args, .. }) = MethodCall::try_from(cx, expr) {
        // ...
    }
}
}

MethodCall::try_from() returns an Option, and will only be Some if the expression was actually a method call. Take a look at MethodCall's fields to see what properties are available.

Caution

Although ExprKind::MethodCall does exist, it does not support qualified method syntax. You should avoid it if possible.

Release bevy_lint

Kick-off Pull Request

  1. Review the changelog (bevy_lint/CHANGELOG.md) and ensure that all notable changes have been documented.
  2. Replace Unreleased heading with the version with the format vX.Y.Z - YYYY-MM-DD.
  3. Update the **All Changes** link to compare from main to the new tag lint-vX.Y.Z. (E.g. lint-v0.1.0...main to lint-v0.1.0...lint-v0.2.0.)
  4. Review the migration guide (bevy_lint/MIGRATION.md) and ensure all breaking / significant changes from the previous version are documented.
  5. Remove the -dev suffix from the version in Cargo.toml and the compatibility table in bevy_lint/README.md.
    • Please ensure that Cargo.lock also updates!
  6. Replace --branch main in action.yml with --tag lint-vX.Y.Z.
    • The linter-action.yml workflow may fail as the tag does not exist yet. This is fine!
  7. Commit all of these changes and open a pull request.
  8. Merge the PR once a core Bevy maintainer approves it with no outstanding issues from other contributors.
    • This starts the release process, enacting a freeze on all other changes until the release has finished. While maintainers need to be aware of this so they do not merge PRs during this time, the release process should take less than an hour, so it's unlikely to ever be an issue.

Release on Github

  1. Create a new Github release.
  2. Set the tag to lint-vX.Y.Z.
  3. Set the title to `bevy_lint` - vX.Y.Z
  4. Paste and fill out the following template into the release description:
<!-- One-sentence summary of changes. What awesome features can we spotlight? What critical bugs were fixed? -->

You can find the live documentation for this release [here](https://thebevyflock.github.io/bevy_cli/linter/index.html). You may also be interested in [the changelog] and [the migration guide].

<!-- Make sure to update the tags in these links to point to the correct version. -->

[the changelog]: https://github.com/TheBevyFlock/bevy_cli/blob/lint-vX.Y.Z/bevy_lint/CHANGELOG.md
[the migration guide]: https://github.com/TheBevyFlock/bevy_cli/blob/lint-vX.Y.Z/bevy_lint/MIGRATION.md

> [!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](https://github.com/bevyengine). Pardon our rough edges, and please consider [submitting an issue](https://github.com/TheBevyFlock/bevy_cli/issues) if you run into trouble!

<!-- You can refer to the compatibility table in `bevy_lint/README.md` for the following two values. -->

This release uses the <!-- `nightly-YYYY-MM-DD` --> toolchain, based on Rust <!-- 1.XX.Y -->. You can install it from Git with the following commands:

<!-- Update `nightly-YYYY-MM-DD` and `lint-vX.Y.Z` in the following code block. -->

```bash
rustup toolchain install nightly-YYYY-MM-DD \
    --component rustc-dev \
    --component llvm-tools-preview

rustup run nightly-YYYY-MM-DD cargo install \
    --git https://github.com/TheBevyFlock/bevy_cli.git \
    --tag lint-vX.Y.Z \
    --locked \
    bevy_lint
```

<!-- Paste the changelog for this release here. Make sure to include the "All Changes" link. :) -->
  1. Check the pre-release box if this is an alpha release, then click "Publish release"!
  2. Announce the release on Discord! Congrats!

Post-Release

  1. Add a new unreleased section to the top of the changelog (bevy_lint/CHANGELOG.md) from the following template:
## [Unreleased]

<!-- Update `lint-vX.Y.Z` in the link to point to the latest release tag. -->

**All Changes**: [`lint-vX.Y.Z...main`](https://github.com/TheBevyFlock/bevy_cli/compare/lint-vX.Y.Z...main)
  1. Bump the version in Cargo.toml to the next -dev version, and ensure Cargo.lock also updates.
  2. Add a new row to the compatibility table for the new -dev version in README.md.
  3. Replace --tag lint-vX.Y.Z in action.yml with --branch main.
  4. Commit all of these changes and open a pull request.
  5. Merge the PR after it has been approved, unblocking frozen pull requests.

Upgrade the Rust Toolchain

bevy_lint matches nightly Rust versions with clippy_utils. A new version of clippy_utils is released with each version of Rust, which bevy_lint should keep up to date with.

  1. Go to clippy_utils's page on crates.io and find the nightly toolchain it requires. For example:

    This crate is only guaranteed to build with this nightly toolchain:

    nightly-2025-01-09
    
  2. Change the channel field in rust-toolchain.toml to the version specified by clippy_utils.

  3. Update the compatibility table for the latest -dev version.

  4. Increase the version of clippy_utils in Cargo.toml to the latest version.

  5. Change the two occurrences of the toolchain in action.yml to the new version.

Once you've finished upgrading the Rust toolchain and clippy_utils, there are a few extra steps that can verify bevy_lint still functions the same.

  1. Read over the release notes for potentially breaking changes.

  2. Skim through diff.rs for clippy_utils to see if anything the linter uses may have changed.

    • clippy_utils doesn't provide a user-facing changelog, unfortunately. You may find the Git history useful, though!
  3. Verify you've installed the latest pinned Rust toolchain. If you use Rustup, it should be automatically installed the first time you run rustc or cargo in the workspace.

    rustc --version
    
  4. Test that the linter still compiles and passes all tests.

    cargo clean
    cargo build
    cargo test
    
  5. After opening the pull request, verify that the linter-action.yml workflow passes in CI.

Reference

Reference guides are technical descriptions of the machinery and how to operate it. Reference material is information-oriented.

Reference material contains propositional or theoretical knowledge that a user looks to in their work.

The only purpose of a reference guide is to describe, as succinctly as possible, and in an orderly way. Whereas the content of tutorials and how-to guides are led by needs of the user, reference material is led by the product it describes.

Diátaxis

Lint Module Docs

All lints, which can be found here, place their documentation in their module. For example, missing_reflect's docs are in src/lints/missing_reflect.rs. These docs must adhere to the following format:

A single sentence description of what this lint checks for.

In the rare case this needs to be elaborated, an optional paragraph beneath the first sentence is
allowed. Use this to go into more detail on what is checked for, as well as what not is checked
for.

# Motivation

One to several paragraphs describing why this lint exists. For example: does this lint help catch
slow code, or does it suggest a more idiomatic version? Try to motivate why a user may want to
enable this lint.

Be careful to only answer _why_ this lint exists, not _what_ it does or _how_ it works. That is the
responsibility of the extended description.

# Known Issues

This is an optional section that describes what false negatives and false positives this lint may
have. (Usually to justify why a lint is in the `nursery` group, though not always.) If possible,
make sure to link to the issue in the
[issue tracker](https://github.com/TheBevyFlock/bevy_cli/issues) so users can comment on it.

# Example

```
// A snippet of code that would cause the lint to trigger. If a specific line would cause the lint,
// make sure to point it out with a comment.

// Note how this variable is not used:
let x = 10;

// If bodies of functions are not relevant to the lint, use `// ...` to signal that there may be
// other code there.
fn foo() {
    // ...
}
```

Use instead:

```
// The second code snippet should be a "fixed" version of the original. Comments from the original
// do not need to be copied over, but it may be useful to add a note on how or why the lint was
// fixed a specific way.

// `_x` will silence the lint, but `_` also works. You can also delete the line, if you truly do
// not want `10`.
let _x = 10;

fn foo() {
    // ...
}
```

If you wish to elaborate further on how to fix the lint or supply further examples, you may do so
here. If this is a Cargo lint, switch out the Rust code block with a TOML one for `Cargo.toml`.

Macro-Generated Code

When a developer uses a macro from a 3rd-party crate, it generates code that the developer cannot easily change or fix. Macro-generated code will still be linted, but if that code has any issues the developer cannot do anything to fix it beyond adding an #[allow(...)] attribute.

Because of this, all bevy_lint lint passes should skip code generated by an external macro. This can be done with Span::in_external_macro():

#![allow(unused)]
fn main() {
impl<'tcx> LateLintPass<'tcx> for MyLint {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
        // Do not lint this expression if it was generated by an external macro.
        if expr.span.in_external_macro(cx.tcx.sess.source_map()) {
            return;
        }

        // ...
    }
}
}

For more information, please see Clippy's docs on macros, the issue about macros, and the PR that first incorporated these checks.

Coding Conventions and Best Practices

This document contains a list of conventions that bevy_lint's code follows to ensure consistency across the project. Beyond this document, the project uses Clippy, rustfmt, and typos in CI, and it follows the majority of the Rust API Guidelines.

Avoid HasSession

HasSession is a trait in clippy_utils intended to make their API more friendly. HasSession provides the sess() method on TyCtxt, LateContext, and a few other types. HasSession::sess() should not be used within bevy_lint because Session is trivial to get without it, and HasSession requires an extra import.

#![allow(unused)]
fn main() {
use clippy_utils::source::HasSession;

fn late_context<'tcx>(cx: LateContext<'tcx>) {
    let sess = cx.sess();
}

fn ty_ctxt<'tcx>(tcx: TyCtxt<'tcx>) {
    let sess = tcx.sess();
}
}

Use instead:

#![allow(unused)]
fn main() {
fn late_context<'tcx>(cx: LateContext<'tcx>) {
    let sess = cx.tcx.sess;
}

fn ty_ctxt<'tcx>(tcx: TyCtxt<'tcx>) {
    let sess = tcx.sess;
}
}

Explanation

Explanation is a discursive treatment of a subject, that permits reflection. Explanation is understanding-oriented.

Explanation deepens and broadens the reader’s understanding of a subject. It brings clarity, light and context.

The concept of reflection is important. Reflection occurs after something else, and depends on something else, yet at the same time brings something new - shines a new light - on the subject matter.

The perspective of explanation is higher and wider than that of the other three types. It does not take the user’s eye-level view, as in a how-to guide, or a close-up view of the machinery, like reference material. Its scope in each case is a topic - "an area of knowledge", that somehow has to be bounded in a reasonable, meaningful way.

Diátaxis