Fix Component require() IDE integration (#18165)

# Objective

Component `require()` IDE integration is fully broken, as of #16575.

## Solution

This reverts us back to the previous "put the docs on Component trait"
impl. This _does_ reduce the accessibility of the required components in
rust docs, but the complete erasure of "required component IDE
experience" is not worth the price of slightly increased prominence of
requires in docs.

Additionally, Rust Analyzer has recently started including derive
attributes in suggestions, so we aren't losing that benefit of the
proc_macro attribute impl.
This commit is contained in:
Carter Anderson 2025-03-05 18:44:47 -08:00 committed by GitHub
parent 54701a844e
commit 06cb5c5fd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 40 additions and 89 deletions

View File

@ -9,10 +9,7 @@ use crate::{
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_ecs::{
component::{require, Component},
query::With,
reflect::ReflectComponent,
schedule::IntoSystemConfigs,
component::Component, query::With, reflect::ReflectComponent, schedule::IntoSystemConfigs,
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{

View File

@ -8,7 +8,7 @@ use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_diagnostic::FrameCount;
use bevy_ecs::{
prelude::{require, Component, Entity, ReflectComponent},
prelude::{Component, Entity, ReflectComponent},
query::{QueryItem, With},
resource::Resource,
schedule::IntoSystemConfigs,

View File

@ -1,4 +1,4 @@
use proc_macro::{TokenStream, TokenTree};
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote, ToTokens};
use std::collections::HashSet;
@ -207,6 +207,18 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
let struct_name = &ast.ident;
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
let required_component_docs = attrs.requires.map(|r| {
let paths = r
.iter()
.map(|r| format!("[`{}`]", r.path.to_token_stream()))
.collect::<Vec<_>>()
.join(", ");
let doc = format!("**Required Components**: {paths}. \n\n A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order.");
quote! {
#[doc = #doc]
}
});
let mutable_type = (attrs.immutable || relationship.is_some())
.then_some(quote! { #bevy_ecs_path::component::Immutable })
.unwrap_or(quote! { #bevy_ecs_path::component::Mutable });
@ -223,6 +235,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
// This puts `register_required` before `register_recursive_requires` to ensure that the constructors of _all_ top
// level components are initialized first, giving them precedence over recursively defined constructors for the same component type
TokenStream::from(quote! {
#required_component_docs
impl #impl_generics #bevy_ecs_path::component::Component for #struct_name #type_generics #where_clause {
const STORAGE_TYPE: #bevy_ecs_path::component::StorageType = #storage;
type Mutability = #mutable_type;
@ -414,34 +427,6 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member {
)
}
pub fn document_required_components(attr: TokenStream, item: TokenStream) -> TokenStream {
let paths = parse_macro_input!(attr with Punctuated::<Require, Comma>::parse_terminated)
.iter()
.map(|r| format!("[`{}`]", r.path.to_token_stream()))
.collect::<Vec<_>>()
.join(", ");
let bevy_ecs_path = crate::bevy_ecs_path()
.to_token_stream()
.to_string()
.replace(' ', "");
let required_components_path = bevy_ecs_path + "::component::Component#required-components";
// Insert information about required components after any existing doc comments
let mut out = TokenStream::new();
let mut end_of_attributes_reached = false;
for tt in item {
if !end_of_attributes_reached & matches!(tt, TokenTree::Ident(_)) {
end_of_attributes_reached = true;
let doc: TokenStream = format!("#[doc = \"\n\n# Required Components\n{paths} \n\n A component's [required components]({required_components_path}) are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order.\"]").parse().unwrap();
out.extend(doc);
}
out.extend(Some(tt));
}
out
}
pub const COMPONENT: &str = "component";
pub const STORAGE: &str = "storage";
pub const REQUIRE: &str = "require";

View File

@ -597,20 +597,12 @@ pub fn derive_resource(input: TokenStream) -> TokenStream {
#[proc_macro_derive(
Component,
attributes(component, relationship, relationship_target, entities)
attributes(component, require, relationship, relationship_target, entities)
)]
pub fn derive_component(input: TokenStream) -> TokenStream {
component::derive_component(input)
}
/// Allows specifying a component's required components.
///
/// See `Component` docs for usage.
#[proc_macro_attribute]
pub fn require(attr: TokenStream, item: TokenStream) -> TokenStream {
component::document_required_components(attr, item)
}
#[proc_macro_derive(States)]
pub fn derive_states(input: TokenStream) -> TokenStream {
states::derive_states(input)

View File

@ -33,8 +33,6 @@ use core::{
use disqualified::ShortName;
use thiserror::Error;
pub use bevy_ecs_macros::require;
/// A data type that can be used to store data for an [entity].
///
/// `Component` is a [derivable trait]: this means that a data type can implement it by applying a `#[derive(Component)]` attribute to it.

View File

@ -846,7 +846,6 @@ mod tests {
world::{FromWorld, World},
};
use alloc::vec::Vec;
use bevy_ecs_macros::require;
use bevy_ptr::OwningPtr;
use bevy_reflect::Reflect;
use core::{alloc::Layout, ops::Deref};

View File

@ -72,7 +72,7 @@ pub mod prelude {
bundle::Bundle,
change_detection::{DetectChanges, DetectChangesMut, Mut, Ref},
children,
component::{require, Component},
component::Component,
entity::{Entity, EntityBorrow, EntityMapper},
event::{Event, EventMutator, EventReader, EventWriter, Events},
hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
@ -132,7 +132,7 @@ mod tests {
use crate::{
bundle::Bundle,
change_detection::Ref,
component::{require, Component, ComponentId, RequiredComponents, RequiredComponentsError},
component::{Component, ComponentId, RequiredComponents, RequiredComponentsError},
entity::Entity,
entity_disabling::DefaultQueryFilters,
prelude::Or,

View File

@ -2222,7 +2222,7 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> {
#[cfg(test)]
mod tests {
use crate::{
component::{require, Component},
component::Component,
resource::Resource,
system::Commands,
world::{CommandQueue, FromWorld, World},

View File

@ -7,7 +7,7 @@ use crate::{
world::World,
};
use alloc::boxed::Box;
use bevy_ecs_macros::{require, Component, Resource};
use bevy_ecs_macros::{Component, Resource};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use core::marker::PhantomData;

View File

@ -3,10 +3,7 @@
use core::ops::{Deref, DerefMut};
use bevy_asset::Handle;
use bevy_ecs::{
component::{require, Component},
reflect::ReflectComponent,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::Reflect;
use bevy_transform::components::Transform;

View File

@ -12,7 +12,6 @@ use bevy_ecs::{
entity::Entity,
event::{Event, EventReader, EventWriter},
name::Name,
prelude::require,
system::{Commands, Query},
};
use bevy_math::ops;

View File

@ -36,7 +36,7 @@ use bevy_app::{App, Plugin};
use bevy_asset::load_internal_asset;
use bevy_core_pipeline::core_3d::graph::Node3d;
use bevy_ecs::{
component::{require, Component},
component::Component,
query::{Changed, QueryItem, With},
schedule::IntoSystemConfigs,
system::{lifetimeless::Read, Query},

View File

@ -20,7 +20,7 @@ use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, AssetId, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::{require, Component},
component::Component,
entity::{hash_map::EntityHashMap, Entity},
prelude::ReflectComponent,
query::With,

View File

@ -4,7 +4,7 @@ use crate::{
};
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Asset, Assets, Handle};
use bevy_ecs::component::{require, Component};
use bevy_ecs::component::Component;
use bevy_math::{prelude::Rectangle, Quat, Vec2, Vec3};
use bevy_reflect::{Reflect, TypePath};
use bevy_render::{
@ -63,7 +63,7 @@ impl Plugin for ForwardDecalPlugin {
/// # Usage Notes
///
/// * Spawn this component on an entity with a [`crate::MeshMaterial3d`] component holding a [`ForwardDecalMaterial`].
/// * Any camera rendering a forward decal must have the [`bevy_core_pipeline::DepthPrepass`] component.
/// * Any camera rendering a forward decal must have the [`bevy_core_pipeline::prepass::DepthPrepass`] component.
/// * Looking at forward decals at a steep angle can cause distortion. This can be mitigated by padding your decal's
/// texture with extra transparent pixels on the edges.
#[derive(Component, Reflect)]

View File

@ -5,7 +5,7 @@ use bevy_asset::{load_internal_asset, weak_handle, AssetId, Handle};
use bevy_core_pipeline::core_3d::Camera3d;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::{require, Component},
component::Component,
entity::Entity,
query::With,
reflect::ReflectComponent,

View File

@ -65,7 +65,7 @@ use bevy_core_pipeline::{
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::{require, Component},
component::Component,
entity::Entity,
query::Has,
reflect::ReflectComponent,

View File

@ -7,7 +7,7 @@ use bevy_core_pipeline::{
prepass::{DepthPrepass, NormalPrepass, ViewPrepassTextures},
};
use bevy_ecs::{
prelude::{require, Component, Entity},
prelude::{Component, Entity},
query::{Has, QueryItem, With},
reflect::ReflectComponent,
resource::Resource,

View File

@ -12,7 +12,7 @@ use bevy_core_pipeline::{
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::{require, Component},
component::Component,
entity::Entity,
query::{Has, QueryItem, With},
reflect::ReflectComponent,

View File

@ -36,11 +36,7 @@ use bevy_core_pipeline::core_3d::{
graph::{Core3d, Node3d},
prepare_core_3d_depth_textures,
};
use bevy_ecs::{
component::{require, Component},
reflect::ReflectComponent,
schedule::IntoSystemConfigs as _,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoSystemConfigs as _};
use bevy_image::Image;
use bevy_math::{
primitives::{Cuboid, Plane3d},

View File

@ -25,7 +25,7 @@ use bevy_ecs::{
component::{Component, HookContext},
entity::{Entity, EntityBorrow},
event::EventReader,
prelude::{require, With},
prelude::With,
query::Has,
reflect::ReflectComponent,
resource::Resource,

View File

@ -5,7 +5,7 @@ use crate::{
use bevy_asset::{AsAssetId, AssetEvent, AssetId, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
change_detection::DetectChangesMut, component::Component, event::EventReader, prelude::require,
change_detection::DetectChangesMut, component::Component, event::EventReader,
reflect::ReflectComponent, system::Query,
};
use bevy_platform_support::{collections::HashSet, hash::FixedHasher};

View File

@ -1,9 +1,6 @@
use bevy_asset::Handle;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::{require, Component},
prelude::ReflectComponent,
};
use bevy_ecs::{component::Component, prelude::ReflectComponent};
use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_transform::components::Transform;
use derive_more::derive::From;

View File

@ -1,9 +1,6 @@
use bevy_asset::{Assets, Handle};
use bevy_color::Color;
use bevy_ecs::{
component::{require, Component},
reflect::ReflectComponent,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_image::{Image, TextureAtlas, TextureAtlasLayout};
use bevy_math::{Rect, UVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};

View File

@ -10,7 +10,7 @@ use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::hash_set::EntityHashSet;
use bevy_ecs::{
change_detection::{DetectChanges, Ref},
component::{require, Component},
component::Component,
entity::Entity,
prelude::{ReflectComponent, With},
query::{Changed, Without},

View File

@ -3,7 +3,7 @@ use bevy_math::{Affine3A, Dir3, Isometry3d, Mat3, Mat4, Quat, Vec3};
use core::ops::Mul;
#[cfg(feature = "bevy-support")]
use bevy_ecs::{component::Component, prelude::require};
use bevy_ecs::component::Component;
#[cfg(feature = "bevy_reflect")]
use {bevy_ecs::reflect::ReflectComponent, bevy_reflect::prelude::*};

View File

@ -1,10 +1,7 @@
use crate::Node;
use bevy_asset::{Asset, AssetId, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::{require, Component},
reflect::ReflectComponent,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_render::{
extract_component::ExtractComponent,

View File

@ -1,8 +1,5 @@
use crate::{FocusPolicy, Interaction, Node};
use bevy_ecs::{
prelude::{require, Component},
reflect::ReflectComponent,
};
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
/// Marker struct for buttons

View File

@ -7,8 +7,8 @@ use bevy_color::Color;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
change_detection::DetectChanges,
component::Component,
entity::Entity,
prelude::{require, Component},
query::With,
reflect::ReflectComponent,
system::{Query, Res, ResMut},