bevy/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs
Gino Valente a658bfef19 bevy_reflect: Reflect doc comments (#6234)
# Objective

Resolves #6197

Make it so that doc comments can be retrieved via reflection.

## Solution

Adds the new `documentation` feature to `bevy_reflect` (disabled by default).

When enabled, documentation can be found using `TypeInfo::doc` for reflected types:

```rust
/// Some struct.
///
/// # Example
///
/// ```ignore
/// let some_struct = SomeStruct;
/// ```
#[derive(Reflect)]
struct SomeStruct;

let info = <SomeStruct as Typed>::type_info();
assert_eq!(
    Some(" Some struct.\n\n # Example\n\n ```ignore\n let some_struct = SomeStruct;\n ```"),
    info.docs()
);
```

### Notes for Reviewers

The bulk of the files simply added the same 16 lines of code (with slightly different documentation). Most of the real changes occur in the `bevy_reflect_derive` files as well as in the added tests.

---

## Changelog

* Added `documentation` feature to `bevy_reflect`
* Added `TypeInfo::docs` method (and similar methods for all info types)
2022-10-18 13:49:57 +00:00

187 lines
6.6 KiB
Rust

//! This crate contains macros used by Bevy's `Reflect` API.
//!
//! The main export of this crate is the derive macro for [`Reflect`]. This allows
//! types to easily implement `Reflect` along with other `bevy_reflect` traits,
//! such as `Struct`, `GetTypeRegistration`, and more— all with a single derive!
//!
//! Some other noteworthy exports include the derive macros for [`FromReflect`] and
//! [`TypeUuid`], as well as the [`reflect_trait`] attribute macro.
//!
//! [`Reflect`]: crate::derive_reflect
//! [`FromReflect`]: crate::derive_from_reflect
//! [`TypeUuid`]: crate::derive_type_uuid
//! [`reflect_trait`]: macro@reflect_trait
extern crate proc_macro;
mod container_attributes;
mod derive_data;
#[cfg(feature = "documentation")]
mod documentation;
mod enum_utility;
mod field_attributes;
mod from_reflect;
mod impls;
mod reflect_value;
mod registration;
mod trait_reflection;
mod type_uuid;
mod utility;
use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct};
use proc_macro::TokenStream;
use quote::quote;
use reflect_value::ReflectValueDef;
use syn::spanned::Spanned;
use syn::{parse_macro_input, DeriveInput};
pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect";
pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value";
#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))]
pub fn derive_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let derive_data = match ReflectDerive::from_input(&ast) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => {
impls::impl_struct(&struct_data)
}
ReflectDerive::TupleStruct(struct_data) => impls::impl_tuple_struct(&struct_data),
ReflectDerive::Enum(meta) => impls::impl_enum(&meta),
ReflectDerive::Value(meta) => impls::impl_value(&meta),
}
}
/// Derives the `FromReflect` trait.
///
/// This macro supports the following field attributes:
/// * `#[reflect(ignore)]`: Ignores the field. This requires the field to implement [`Default`].
/// * `#[reflect(default)]`: If the field's value cannot be read, uses its [`Default`] implementation.
/// * `#[reflect(default = "some_func")]`: If the field's value cannot be read, uses the function with the given name.
///
#[proc_macro_derive(FromReflect, attributes(reflect))]
pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let derive_data = match ReflectDerive::from_input(&ast) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
match derive_data {
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => {
from_reflect::impl_struct(&struct_data)
}
ReflectDerive::TupleStruct(struct_data) => from_reflect::impl_tuple_struct(&struct_data),
ReflectDerive::Enum(meta) => from_reflect::impl_enum(&meta),
ReflectDerive::Value(meta) => from_reflect::impl_value(&meta),
}
}
// From https://github.com/randomPoison/type-uuid
#[proc_macro_derive(TypeUuid, attributes(uuid))]
pub fn derive_type_uuid(input: TokenStream) -> TokenStream {
type_uuid::type_uuid_derive(input)
}
#[proc_macro_attribute]
pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
trait_reflection::reflect_trait(&args, input)
}
#[proc_macro]
pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as ReflectValueDef);
let meta = ReflectMeta::new(
&def.type_name,
&def.generics,
def.traits.unwrap_or_default(),
);
#[cfg(feature = "documentation")]
let meta = meta.with_docs(documentation::Documentation::from_attributes(&def.attrs));
impls::impl_value(&meta)
}
/// A replacement for `#[derive(Reflect)]` to be used with foreign types which
/// the definitions of cannot be altered.
///
/// This macro is an alternative to [`impl_reflect_value!`] and [`impl_from_reflect_value!`]
/// which implement foreign types as Value types. Note that there is no `impl_from_reflect_struct`,
/// as this macro will do the job of both. This macro implements them as `Struct` types,
/// which have greater functionality. The type being reflected must be in scope, as you cannot
/// qualify it in the macro as e.g. `bevy::prelude::Vec3`.
///
/// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible
/// foreign types. Without `Default` reflected for such types, you will usually get an arcane
/// error message and fail to compile. If the type does not implement `Default`, it may not
/// be possible to reflect without extending the macro.
///
/// # Example
/// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type:
/// ```ignore
/// use bevy::prelude::Vec3;
///
/// impl_reflect_struct!(
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// struct Vec3 {
/// x: f32,
/// y: f32,
/// z: f32
/// }
/// );
/// ```
#[proc_macro]
pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let derive_data = match ReflectDerive::from_input(&ast) {
Ok(data) => data,
Err(err) => return err.into_compile_error().into(),
};
match derive_data {
ReflectDerive::Struct(struct_data) => {
let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&struct_data).into();
let impl_from_struct: proc_macro2::TokenStream =
from_reflect::impl_struct(&struct_data).into();
TokenStream::from(quote! {
#impl_struct
#impl_from_struct
})
}
ReflectDerive::TupleStruct(..) => syn::Error::new(
ast.span(),
"impl_reflect_struct does not support tuple structs",
)
.into_compile_error()
.into(),
ReflectDerive::UnitStruct(..) => syn::Error::new(
ast.span(),
"impl_reflect_struct does not support unit structs",
)
.into_compile_error()
.into(),
_ => syn::Error::new(ast.span(), "impl_reflect_struct only supports structs")
.into_compile_error()
.into(),
}
}
#[proc_macro]
pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
let def = parse_macro_input!(input as ReflectValueDef);
from_reflect::impl_value(&ReflectMeta::new(
&def.type_name,
&def.generics,
def.traits.unwrap_or_default(),
))
}