Skip to content

Commit 11f0a2d

Browse files
bushrat011899Themayualice-i-cecileBD103
authored
Add on_unimplemented Diagnostics to Most Public Traits (#13347)
# Objective - Fixes #12377 ## Solution Added simple `#[diagnostic::on_unimplemented(...)]` attributes to some critical public traits providing a more approachable initial error message. Where appropriate, a `note` is added indicating that a `derive` macro is available. ## Examples <details> <summary>Examples hidden for brevity</summary> Below is a collection of examples showing the new error messages produced by this change. In general, messages will start with a more Bevy-centric error message (e.g., _`MyComponent` is not a `Component`_), and a note directing the user to an available derive macro where appropriate. ### Missing `#[derive(Resource)]` <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; struct MyResource; fn main() { App::new() .insert_resource(MyResource) .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: `MyResource` is not a `Resource` --> examples/app/empty.rs:7:26 | 7 | .insert_resource(MyResource) | --------------- ^^^^^^^^^^ invalid `Resource` | | | required by a bound introduced by this call | = help: the trait `Resource` is not implemented for `MyResource` = note: consider annotating `MyResource` with `#[derive(Resource)]` = help: the following other types implement trait `Resource`: AccessibilityRequested ManageAccessibilityUpdates bevy::bevy_a11y::Focus DiagnosticsStore FrameCount bevy::prelude::State<S> SystemInfo bevy::prelude::Axis<T> and 141 others note: required by a bound in `bevy::prelude::App::insert_resource` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:419:31 | 419 | pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self { | ^^^^^^^^ required by this bound in `App::insert_resource` ``` </details> ### Putting A `QueryData` in a `QueryFilter` Slot <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; #[derive(Component)] struct A; #[derive(Component)] struct B; fn my_system(_query: Query<&A, &B>) {} fn main() { App::new() .add_systems(Update, my_system) .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: `&B` is not a valid `Query` filter --> examples/app/empty.rs:9:22 | 9 | fn my_system(_query: Query<&A, &B>) {} | ^^^^^^^^^^^^^ invalid `Query` filter | = help: the trait `QueryFilter` is not implemented for `&B` = help: the following other types implement trait `QueryFilter`: With<T> Without<T> bevy::prelude::Or<()> bevy::prelude::Or<(F0,)> bevy::prelude::Or<(F0, F1)> bevy::prelude::Or<(F0, F1, F2)> bevy::prelude::Or<(F0, F1, F2, F3)> bevy::prelude::Or<(F0, F1, F2, F3, F4)> and 28 others note: required by a bound in `bevy::prelude::Query` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_ecs\src\system\query.rs:349:51 | 349 | pub struct Query<'world, 'state, D: QueryData, F: QueryFilter = ()> { | ^^^^^^^^^^^ required by this bound in `Query` ``` </details> ### Missing `#[derive(Component)]` <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; struct A; fn my_system(mut commands: Commands) { commands.spawn(A); } fn main() { App::new() .add_systems(Startup, my_system) .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: `A` is not a `Bundle` --> examples/app/empty.rs:6:20 | 6 | commands.spawn(A); | ----- ^ invalid `Bundle` | | | required by a bound introduced by this call | = help: the trait `bevy::prelude::Component` is not implemented for `A`, which is required by `A: Bundle` = note: consider annotating `A` with `#[derive(Component)]` or `#[derive(Bundle)]` = help: the following other types implement trait `Bundle`: TransformBundle SceneBundle DynamicSceneBundle AudioSourceBundle<Source> SpriteBundle SpriteSheetBundle Text2dBundle MaterialMesh2dBundle<M> and 34 others = note: required for `A` to implement `Bundle` note: required by a bound in `bevy::prelude::Commands::<'w, 's>::spawn` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_ecs\src\system\commands\mod.rs:243:21 | 243 | pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands { | ^^^^^^ required by this bound in `Commands::<'w, 's>::spawn` ``` </details> ### Missing `#[derive(Asset)]` <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; struct A; fn main() { App::new() .init_asset::<A>() .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: `A` is not an `Asset` --> examples/app/empty.rs:7:23 | 7 | .init_asset::<A>() | ---------- ^ invalid `Asset` | | | required by a bound introduced by this call | = help: the trait `Asset` is not implemented for `A` = note: consider annotating `A` with `#[derive(Asset)]` = help: the following other types implement trait `Asset`: Font AnimationGraph DynamicScene Scene AudioSource Pitch bevy::bevy_gltf::Gltf GltfNode and 17 others note: required by a bound in `init_asset` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_asset\src\lib.rs:307:22 | 307 | fn init_asset<A: Asset>(&mut self) -> &mut Self; | ^^^^^ required by this bound in `AssetApp::init_asset` ``` </details> ### Mismatched Input and Output on System Piping <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; fn producer() -> u32 { 123 } fn consumer(_: In<u16>) {} fn main() { App::new() .add_systems(Update, producer.pipe(consumer)) .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: `fn(bevy::prelude::In<u16>) {consumer}` is not a valid system with input `u32` and output `_` --> examples/app/empty.rs:11:44 | 11 | .add_systems(Update, producer.pipe(consumer)) | ---- ^^^^^^^^ invalid system | | | required by a bound introduced by this call | = help: the trait `bevy::prelude::IntoSystem<u32, _, _>` is not implemented for fn item `fn(bevy::prelude::In<u16>) {consumer}` = note: expecting a system which consumes `u32` and produces `_` note: required by a bound in `pipe` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_ecs\src\system\mod.rs:168:12 | 166 | fn pipe<B, Final, MarkerB>(self, system: B) -> PipeSystem<Self::System, B::System> | ---- required by a bound in this associated function 167 | where 168 | B: IntoSystem<Out, Final, MarkerB>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `IntoSystem::pipe` ``` </details> ### Missing Reflection <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; #[derive(Component)] struct MyComponent; fn main() { App::new() .register_type::<MyComponent>() .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: `MyComponent` does not provide type registration information --> examples/app/empty.rs:8:26 | 8 | .register_type::<MyComponent>() | ------------- ^^^^^^^^^^^ the trait `GetTypeRegistration` is not implemented for `MyComponent` | | | required by a bound introduced by this call | = note: consider annotating `MyComponent` with `#[derive(Reflect)]` = help: the following other types implement trait `GetTypeRegistration`: bool char isize i8 i16 i32 i64 i128 and 443 others note: required by a bound in `bevy::prelude::App::register_type` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:619:29 | 619 | pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `App::register_type` ``` </details> ### Missing `#[derive(States)]` Implementation <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; #[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash)] enum AppState { #[default] Menu, InGame { paused: bool, turbo: bool, }, } fn main() { App::new() .init_state::<AppState>() .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: the trait bound `AppState: FreelyMutableState` is not satisfied --> examples/app/empty.rs:15:23 | 15 | .init_state::<AppState>() | ---------- ^^^^^^^^ the trait `FreelyMutableState` is not implemented for `AppState` | | | required by a bound introduced by this call | = note: consider annotating `AppState` with `#[derive(States)]` note: required by a bound in `bevy::prelude::App::init_state` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:282:26 | 282 | pub fn init_state<S: FreelyMutableState + FromWorld>(&mut self) -> &mut Self { | ^^^^^^^^^^^^^^^^^^ required by this bound in `App::init_state` ``` </details> ### Adding a `System` with Unhandled Output <details> <summary>Example Code</summary> ```rust use bevy::prelude::*; fn producer() -> u32 { 123 } fn main() { App::new() .add_systems(Update, consumer) .run(); } ``` </details> <details> <summary>Error Generated</summary> ```error error[E0277]: `fn() -> u32 {producer}` does not describe a valid system configuration --> examples/app/empty.rs:9:30 | 9 | .add_systems(Update, producer) | ----------- ^^^^^^^^ invalid system configuration | | | required by a bound introduced by this call | = help: the trait `IntoSystem<(), (), _>` is not implemented for fn item `fn() -> u32 {producer}`, which is required by `fn() -> u32 {producer}: IntoSystemConfigs<_>` = help: the following other types implement trait `IntoSystemConfigs<Marker>`: <Box<(dyn bevy::prelude::System<In = (), Out = ()> + 'static)> as IntoSystemConfigs<()>> <NodeConfigs<Box<(dyn bevy::prelude::System<In = (), Out = ()> + 'static)>> as IntoSystemConfigs<()>> <(S0,) as IntoSystemConfigs<(SystemConfigTupleMarker, P0)>> <(S0, S1) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1)>> <(S0, S1, S2) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2)>> <(S0, S1, S2, S3) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2, P3)>> <(S0, S1, S2, S3, S4) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2, P3, P4)>> <(S0, S1, S2, S3, S4, S5) as IntoSystemConfigs<(SystemConfigTupleMarker, P0, P1, P2, P3, P4, P5)>> and 14 others = note: required for `fn() -> u32 {producer}` to implement `IntoSystemConfigs<_>` note: required by a bound in `bevy::prelude::App::add_systems` --> C:\Users\Zac\Documents\GitHub\bevy\crates\bevy_app\src\app.rs:342:23 | 339 | pub fn add_systems<M>( | ----------- required by a bound in this associated function ... 342 | systems: impl IntoSystemConfigs<M>, | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `App::add_systems` ``` </details> </details> ## Testing CI passed locally. ## Migration Guide Upgrade to version 1.78 (or higher) of Rust. ## Future Work - Currently, hints are not supported in this diagnostic. Ideally, suggestions like _"consider using ..."_ would be in a hint rather than a note, but that is the best option for now. - System chaining and other `all_tuples!(...)`-based traits have bad error messages due to the slightly different error message format. --------- Co-authored-by: Jamie Ridding <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: BD103 <[email protected]>
1 parent 104dcf5 commit 11f0a2d

30 files changed

+129
-12
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ keywords = ["game", "engine", "gamedev", "graphics", "bevy"]
1010
license = "MIT OR Apache-2.0"
1111
repository = "https://github.com/bevyengine/bevy"
1212
documentation = "https://docs.rs/bevy"
13-
rust-version = "1.77.0"
13+
rust-version = "1.78.0"
1414

1515
[workspace]
1616
exclude = [

crates/bevy_app/src/plugin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ use std::any::Any;
5454
/// }
5555
/// }
5656
/// # fn damp_flickering() {}
57-
/// ````
57+
/// ```
5858
pub trait Plugin: Downcast + Any + Send + Sync {
5959
/// Configures the [`App`] to which this plugin is added.
6060
fn build(&self, app: &mut App);

crates/bevy_asset/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ impl Plugin for AssetPlugin {
223223
}
224224
}
225225

226+
#[diagnostic::on_unimplemented(
227+
message = "`{Self}` is not an `Asset`",
228+
label = "invalid `Asset`",
229+
note = "consider annotating `{Self}` with `#[derive(Asset)]`"
230+
)]
226231
pub trait Asset: VisitAssetDependencies + TypePath + Send + Sync + 'static {}
227232

228233
pub trait VisitAssetDependencies {

crates/bevy_ecs/src/bundle.rs

+5
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ use std::ptr::NonNull;
141141
// bundle, in the _exact_ order that [`DynamicBundle::get_components`] is called.
142142
// - [`Bundle::from_components`] must call `func` exactly once for each [`ComponentId`] returned by
143143
// [`Bundle::component_ids`].
144+
#[diagnostic::on_unimplemented(
145+
message = "`{Self}` is not a `Bundle`",
146+
label = "invalid `Bundle`",
147+
note = "consider annotating `{Self}` with `#[derive(Component)]` or `#[derive(Bundle)]`"
148+
)]
144149
pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
145150
/// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s
146151
#[doc(hidden)]

crates/bevy_ecs/src/component.rs

+5
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ use std::{
150150
///
151151
/// [`SyncCell`]: bevy_utils::synccell::SyncCell
152152
/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html
153+
#[diagnostic::on_unimplemented(
154+
message = "`{Self}` is not a `Component`",
155+
label = "invalid `Component`",
156+
note = "consider annotating `{Self}` with `#[derive(Component)]`"
157+
)]
153158
pub trait Component: Send + Sync + 'static {
154159
/// A constant indicating the storage type used for this component.
155160
const STORAGE_TYPE: StorageType;

crates/bevy_ecs/src/entity/map_entities.rs

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use super::EntityHashMap;
3737
/// }
3838
/// }
3939
/// ```
40-
///
4140
pub trait MapEntities {
4241
/// Updates all [`Entity`] references stored inside using `entity_mapper`.
4342
///

crates/bevy_ecs/src/event.rs

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ use std::{
2828
/// You can conveniently access events using the [`EventReader`] and [`EventWriter`] system parameter.
2929
///
3030
/// Events must be thread-safe.
31+
#[diagnostic::on_unimplemented(
32+
message = "`{Self}` is not an `Event`",
33+
label = "invalid `Event`",
34+
note = "consider annotating `{Self}` with `#[derive(Event]`"
35+
)]
3136
pub trait Event: Send + Sync + 'static {}
3237

3338
/// An `EventId` uniquely identifies an event stored in a specific [`World`].

crates/bevy_ecs/src/query/fetch.rs

+4
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,10 @@ use std::{cell::UnsafeCell, marker::PhantomData};
268268
///
269269
/// [`Query`]: crate::system::Query
270270
/// [`ReadOnly`]: Self::ReadOnly
271+
#[diagnostic::on_unimplemented(
272+
message = "`{Self}` is not valid to request as data in a `Query`",
273+
label = "invalid `Query` data"
274+
)]
271275
pub unsafe trait QueryData: WorldQuery {
272276
/// The read-only variant of this [`QueryData`], which satisfies the [`ReadOnlyQueryData`] trait.
273277
type ReadOnly: ReadOnlyQueryData<State = <Self as WorldQuery>::State>;

crates/bevy_ecs/src/query/filter.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ use std::{cell::UnsafeCell, marker::PhantomData};
7070
/// [`matches_component_set`]: Self::matches_component_set
7171
/// [`Query`]: crate::system::Query
7272
/// [`State`]: Self::State
73-
73+
#[diagnostic::on_unimplemented(
74+
message = "`{Self}` is not a valid `Query` filter",
75+
label = "invalid `Query` filter",
76+
note = "a `QueryFilter` typically uses a combination of `With<T>` and `Without<T>` statements"
77+
)]
7478
pub trait QueryFilter: WorldQuery {
7579
/// Returns true if (and only if) this Filter relies strictly on archetypes to limit which
7680
/// components are accessed by the Query.
@@ -938,6 +942,11 @@ impl<T: Component> QueryFilter for Changed<T> {
938942
///
939943
/// [`Added`] and [`Changed`] works with entities, and therefore are not archetypal. As such
940944
/// they do not implement [`ArchetypeFilter`].
945+
#[diagnostic::on_unimplemented(
946+
message = "`{Self}` is not a valid `Query` filter based on archetype information",
947+
label = "invalid `Query` filter",
948+
note = "an `ArchetypeFilter` typically uses a combination of `With<T>` and `Without<T>` statements"
949+
)]
941950
pub trait ArchetypeFilter: QueryFilter {}
942951

943952
impl<T: Component> ArchetypeFilter for With<T> {}

crates/bevy_ecs/src/schedule/config.rs

+8
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,10 @@ impl<T> NodeConfigs<T> {
288288
/// )
289289
/// );
290290
/// ```
291+
#[diagnostic::on_unimplemented(
292+
message = "`{Self}` does not describe a valid system configuration",
293+
label = "invalid system configuration"
294+
)]
291295
pub trait IntoSystemConfigs<Marker>
292296
where
293297
Self: Sized,
@@ -562,6 +566,10 @@ impl SystemSetConfig {
562566
pub type SystemSetConfigs = NodeConfigs<InternedSystemSet>;
563567

564568
/// Types that can convert into a [`SystemSetConfigs`].
569+
#[diagnostic::on_unimplemented(
570+
message = "`{Self}` does not describe a valid system set configuration",
571+
label = "invalid system set configuration"
572+
)]
565573
pub trait IntoSystemSetConfigs
566574
where
567575
Self: Sized,

crates/bevy_ecs/src/schedule/set.rs

+4
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ impl SystemSet for AnonymousSet {
149149
}
150150

151151
/// Types that can be converted into a [`SystemSet`].
152+
#[diagnostic::on_unimplemented(
153+
message = "`{Self}` is not a system set",
154+
label = "invalid system set"
155+
)]
152156
pub trait IntoSystemSet<Marker>: Sized {
153157
/// The type of [`SystemSet`] this instance converts into.
154158
type Set: SystemSet;

crates/bevy_ecs/src/system/adapter_system.rs

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ use crate::{schedule::InternedSystemSet, world::unsafe_world_cell::UnsafeWorldCe
3939
/// # system.initialize(&mut world);
4040
/// # assert!(system.run((), &mut world));
4141
/// ```
42+
#[diagnostic::on_unimplemented(
43+
message = "`{Self}` can not adapt a system of type `{S}`",
44+
label = "invalid system adapter"
45+
)]
4246
pub trait Adapt<S: System>: Send + Sync + 'static {
4347
/// The [input](System::In) type for an [`AdapterSystem`].
4448
type In;

crates/bevy_ecs/src/system/combinator.rs

+5
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ use super::{ReadOnlySystem, System};
8383
/// # assert!(world.resource::<RanFlag>().0);
8484
/// # world.resource_mut::<RanFlag>().0 = false;
8585
/// ```
86+
#[diagnostic::on_unimplemented(
87+
message = "`{Self}` can not combine systems `{A}` and `{B}`",
88+
label = "invalid system combination",
89+
note = "the inputs and outputs of `{A}` and `{B}` are not compatible with this combiner"
90+
)]
8691
pub trait Combine<A: System, B: System> {
8792
/// The [input](System::In) type for a [`CombinatorSystem`].
8893
type In;

crates/bevy_ecs/src/system/exclusive_function_system.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,18 @@ where
161161
///
162162
/// This trait can be useful for making your own systems which accept other systems,
163163
/// sometimes called higher order systems.
164+
#[diagnostic::on_unimplemented(
165+
message = "`{Self}` is not an exclusive system",
166+
label = "invalid system"
167+
)]
164168
pub trait ExclusiveSystemParamFunction<Marker>: Send + Sync + 'static {
165169
/// The input type to this system. See [`System::In`].
166170
type In;
167171

168172
/// The return type of this system. See [`System::Out`].
169173
type Out;
170174

171-
/// The [`ExclusiveSystemParam`]/s defined by this system's `fn` parameters.
175+
/// The [`ExclusiveSystemParam`]'s defined by this system's `fn` parameters.
172176
type Param: ExclusiveSystemParam;
173177

174178
/// Executes this system once. See [`System::run`].

crates/bevy_ecs/src/system/exclusive_system_param.rs

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ use std::marker::PhantomData;
1010

1111
/// A parameter that can be used in an exclusive system (a system with an `&mut World` parameter).
1212
/// Any parameters implementing this trait must come after the `&mut World` parameter.
13+
#[diagnostic::on_unimplemented(
14+
message = "`{Self}` can not be used as a parameter for an exclusive system",
15+
label = "invalid system parameter"
16+
)]
1317
pub trait ExclusiveSystemParam: Sized {
1418
/// Used to store data which persists across invocations of a system.
1519
type State: Send + Sync + 'static;

crates/bevy_ecs/src/system/function_system.rs

+4
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,10 @@ where
626626
/// ```
627627
/// [`PipeSystem`]: crate::system::PipeSystem
628628
/// [`ParamSet`]: crate::system::ParamSet
629+
#[diagnostic::on_unimplemented(
630+
message = "`{Self}` is not a valid system",
631+
label = "invalid system"
632+
)]
629633
pub trait SystemParamFunction<Marker>: Send + Sync + 'static {
630634
/// The input type to this system. See [`System::In`].
631635
type In;

crates/bevy_ecs/src/system/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ use crate::world::World;
147147
// This trait has to be generic because we have potentially overlapping impls, in particular
148148
// because Rust thinks a type could impl multiple different `FnMut` combinations
149149
// even though none can currently
150+
#[diagnostic::on_unimplemented(
151+
message = "`{Self}` is not a valid system with input `{In}` and output `{Out}`",
152+
label = "invalid system"
153+
)]
150154
pub trait IntoSystem<In, Out, Marker>: Sized {
151155
/// The type of [`System`] that this instance converts into.
152156
type System: System<In = In, Out = Out>;

crates/bevy_ecs/src/system/system.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use super::IntoSystem;
2222
/// Systems are executed in parallel, in opportunistic order; data access is managed automatically.
2323
/// It's possible to specify explicit execution order between specific systems,
2424
/// see [`IntoSystemConfigs`](crate::schedule::IntoSystemConfigs).
25+
#[diagnostic::on_unimplemented(message = "`{Self}` is not a system", label = "invalid system")]
2526
pub trait System: Send + Sync + 'static {
2627
/// The system's input. See [`In`](crate::system::In) for
2728
/// [`FunctionSystem`](crate::system::FunctionSystem)s.

crates/bevy_ecs/src/system/system_param.rs

+5
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,11 @@ impl_param_set!();
432432
/// ```
433433
///
434434
/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html
435+
#[diagnostic::on_unimplemented(
436+
message = "`{Self}` is not a `Resource`",
437+
label = "invalid `Resource`",
438+
note = "consider annotating `{Self}` with `#[derive(Resource)]`"
439+
)]
435440
pub trait Resource: Send + Sync + 'static {}
436441

437442
// SAFETY: Res only reads a single World resource

crates/bevy_reflect/compile_fail/tests/reflect_derive/generics_fail.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ struct NoReflect(f32);
1212

1313
fn main() {
1414
let mut foo: Box<dyn Struct> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
15-
//~^ ERROR: not satisfied
15+
//~^ ERROR: `NoReflect` does not provide type registration information
1616

1717
// foo doesn't implement Reflect because NoReflect doesn't implement Reflect
1818
foo.get_field::<NoReflect>("a").unwrap();
19-
//~^ ERROR: not satisfied
19+
//~^ ERROR: `NoReflect` can not be reflected
2020
}

crates/bevy_reflect/compile_fail/tests/reflect_derive/generics_fail.stderr

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied
1+
error[E0277]: `NoReflect` can not be reflected
22
--> tests/reflect_derive/generics_fail.rs:18:21
33
|
44
18 | foo.get_field::<NoReflect>("a").unwrap();
55
| --------- ^^^^^^^^^ the trait `Reflect` is not implemented for `NoReflect`
66
| |
77
| required by a bound introduced by this call
88
|
9+
= note: Try using `#[derive(Reflect)]`
910
= help: the following other types implement trait `Reflect`:
1011
bool
1112
char
@@ -17,17 +18,19 @@ error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied
1718
i128
1819
and 74 others
1920
note: required by a bound in `bevy_reflect::GetField::get_field`
20-
--> $BEVY_ROOT/crates/bevy_reflect/src/struct_trait.rs:242:21
21+
--> $BEVY_ROOT/crates/bevy_reflect/src/struct_trait.rs:244:21
2122
|
22-
242 | fn get_field<T: Reflect>(&self, name: &str) -> Option<&T>;
23+
244 | fn get_field<T: Reflect>(&self, name: &str) -> Option<&T>;
2324
| ^^^^^^^ required by this bound in `GetField::get_field`
2425

25-
error[E0277]: the trait bound `NoReflect: GetTypeRegistration` is not satisfied
26+
error[E0277]: `NoReflect` does not provide type registration information
2627
--> tests/reflect_derive/generics_fail.rs:14:36
2728
|
2829
14 | let mut foo: Box<dyn Struct> = Box::new(Foo::<NoReflect> { a: NoReflect(42.0) });
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GetTypeRegistration` is not implemented for `NoReflect`, which is required by `Foo<NoReflect>: bevy_reflect::Struct`
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid Type
3031
|
32+
= help: the trait `GetTypeRegistration` is not implemented for `NoReflect`, which is required by `Foo<NoReflect>: bevy_reflect::Struct`
33+
= note: Try using `#[derive(Reflect)]`
3134
= help: the following other types implement trait `GetTypeRegistration`:
3235
bool
3336
char

crates/bevy_reflect/src/from_reflect.rs

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ use crate::{FromType, Reflect};
2121
/// [derive macro]: bevy_reflect_derive::FromReflect
2222
/// [`DynamicStruct`]: crate::DynamicStruct
2323
/// [crate-level documentation]: crate
24+
#[diagnostic::on_unimplemented(
25+
message = "`{Self}` can not be created through reflection",
26+
note = "consider annotating `{Self}` with `#[derive(FromReflect)]`"
27+
)]
2428
pub trait FromReflect: Reflect + Sized {
2529
/// Constructs a concrete instance of `Self` from a reflected value.
2630
fn from_reflect(reflect: &dyn Reflect) -> Option<Self>;

crates/bevy_reflect/src/path/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ impl<'a> ReflectPath<'a> for &'a str {
230230
/// [`List`]: crate::List
231231
/// [`Array`]: crate::Array
232232
/// [`Enum`]: crate::Enum
233+
#[diagnostic::on_unimplemented(
234+
message = "`{Self}` does not provide a reflection path",
235+
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
236+
)]
233237
pub trait GetPath: Reflect {
234238
/// Returns a reference to the value specified by `path`.
235239
///

crates/bevy_reflect/src/reflect.rs

+4
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ impl std::fmt::Display for ReflectKind {
179179
/// [`bevy_reflect`]: crate
180180
/// [derive macro]: bevy_reflect_derive::Reflect
181181
/// [crate-level documentation]: crate
182+
#[diagnostic::on_unimplemented(
183+
message = "`{Self}` can not be reflected",
184+
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
185+
)]
182186
pub trait Reflect: DynamicTypePath + Any + Send + Sync {
183187
/// Returns the [`TypeInfo`] of the type _represented_ by this value.
184188
///

crates/bevy_reflect/src/type_info.rs

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ use std::fmt::Debug;
7070
/// ```
7171
///
7272
/// [utility]: crate::utility
73+
#[diagnostic::on_unimplemented(
74+
message = "`{Self}` can not provide type information through reflection",
75+
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
76+
)]
7377
pub trait Typed: Reflect + TypePath {
7478
/// Returns the compile-time [info] for the underlying type.
7579
///

crates/bevy_reflect/src/type_path.rs

+8
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ use std::fmt;
7979
/// [`crate_name`]: TypePath::crate_name
8080
/// [`module_path`]: TypePath::module_path
8181
/// [`type_ident`]: TypePath::type_ident
82+
#[diagnostic::on_unimplemented(
83+
message = "`{Self}` does not have a type path",
84+
note = "consider annotating `{Self}` with `#[derive(Reflect)]` or `#[derive(TypePath)]`"
85+
)]
8286
pub trait TypePath: 'static {
8387
/// Returns the fully qualified path of the underlying type.
8488
///
@@ -129,6 +133,10 @@ pub trait TypePath: 'static {
129133
/// Since this is a supertrait of [`Reflect`] its methods can be called on a `dyn Reflect`.
130134
///
131135
/// [`Reflect`]: crate::Reflect
136+
#[diagnostic::on_unimplemented(
137+
message = "`{Self}` can not be used as a dynamic type path",
138+
note = "consider annotating `{Self}` with `#[derive(Reflect)]` or `#[derive(TypePath)]`"
139+
)]
132140
pub trait DynamicTypePath {
133141
/// See [`TypePath::type_path`].
134142
fn reflect_type_path(&self) -> &str;

crates/bevy_reflect/src/type_registry.rs

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ impl Debug for TypeRegistryArc {
5656
/// See the [crate-level documentation] for more information on type registration.
5757
///
5858
/// [crate-level documentation]: crate
59+
#[diagnostic::on_unimplemented(
60+
message = "`{Self}` does not provide type registration information",
61+
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
62+
)]
5963
pub trait GetTypeRegistration: 'static {
6064
/// Returns the default [`TypeRegistration`] for this type.
6165
fn get_type_registration() -> TypeRegistration;

crates/bevy_state/src/state/freely_mutable_state.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use super::transitions::*;
99
///
1010
/// While ordinary states are freely mutable (and implement this trait as part of their derive macro),
1111
/// computed states are not: instead, they can *only* change when the states that drive them do.
12+
#[diagnostic::on_unimplemented(note = "consider annotating `{Self}` with `#[derive(States)]`")]
1213
pub trait FreelyMutableState: States {
1314
/// This function registers all the necessary systems to apply state changes and run transition schedules
1415
fn register_state(schedule: &mut Schedule) {

crates/bevy_state/src/state/states.rs

+5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ use std::hash::Hash;
5454
/// app.add_systems(Update, handle_escape_pressed.run_if(in_state(GameState::MainMenu)));
5555
/// app.add_systems(OnEnter(GameState::SettingsMenu), open_settings_menu);
5656
/// ```
57+
#[diagnostic::on_unimplemented(
58+
message = "`{Self}` can not be used as a state",
59+
label = "invalid state",
60+
note = "consider annotating `{Self}` with `#[derive(States)]`"
61+
)]
5762
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug {
5863
/// How many other states this state depends on.
5964
/// Used to help order transitions and de-duplicate [`ComputedStates`](crate::state::ComputedStates), as well as prevent cyclical

crates/bevy_state/src/state/sub_states.rs

+5
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ pub use bevy_state_macros::SubStates;
143143
///
144144
/// impl FreelyMutableState for GamePhase {}
145145
/// ```
146+
#[diagnostic::on_unimplemented(
147+
message = "`{Self}` can not be used as a sub-state",
148+
label = "invalid sub-state",
149+
note = "consider annotating `{Self}` with `#[derive(SubStates)]`"
150+
)]
146151
pub trait SubStates: States + FreelyMutableState {
147152
/// The set of states from which the [`Self`] is derived.
148153
///

0 commit comments

Comments
 (0)