Improve TypeUuid's derive macro error messages (#9315)
# Objective
- Better error message
- More idiomatic code
## Solution
Refactorize `TypeUuid` macros to use `syn::Result` instead of panic.
## Before/After error messages
### Missing `#[uuid]` attribtue
#### Before
```
error: proc-macro derive panicked
--> src\main.rs:1:10
|
1 | #[derive(TypeUuid)]
| ^^^^^^^^
|
= help: message: No `#[uuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"` attribute found.
```
#### After
```
error: No `#[uuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]` attribute found.
--> src\main.rs:3:10
|
3 | #[derive(TypeUuid)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `TypeUuid` (in Nightly builds, run with -Z macro-backtrace for more info)
```
### Malformed attribute
#### Before
```
error: proc-macro derive panicked
--> src\main.rs:3:10
|
3 | #[derive(TypeUuid)]
| ^^^^^^^^
|
= help: message: `uuid` attribute must take the form `#[uuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"`.
```
#### After
```
error: `uuid` attribute must take the form `#[uuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]`.
--> src\main.rs:4:1
|
4 | #[uuid = 42]
| ^^^^^^^^^^^^
```
### UUID parse fail
#### Before
```
error: proc-macro derive panicked
--> src\main.rs:3:10
|
3 | #[derive(TypeUuid)]
| ^^^^^^^^
|
= help: message: Value specified to `#[uuid]` attribute is not a valid UUID.: Error(SimpleLength { len: 3 })
```
#### After
```
error: Invalid UUID: invalid length: expected length 32 for simple format, found 3
--> src\main.rs:4:10
|
4 | #[uuid = "000"]
| ^^^^^
```
### With [Error
Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens)
#### Before

#### After

---
## Changelog
- `#[derive(TypeUuid)]` provide better error messages.
This commit is contained in:
parent
21518de0de
commit
44c769f7b9
@ -30,7 +30,6 @@ mod type_uuid;
|
||||
mod utility;
|
||||
|
||||
use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct};
|
||||
use crate::type_uuid::gen_impl_type_uuid;
|
||||
use container_attributes::ReflectTraits;
|
||||
use derive_data::ReflectTypePath;
|
||||
use proc_macro::TokenStream;
|
||||
@ -39,7 +38,6 @@ use reflect_value::ReflectValueDef;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use type_path::NamedTypePathDef;
|
||||
use type_uuid::TypeUuidDef;
|
||||
use utility::WhereClauseOptions;
|
||||
|
||||
pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect";
|
||||
@ -288,7 +286,10 @@ pub fn derive_type_path(input: TokenStream) -> TokenStream {
|
||||
// From https://github.com/randomPoison/type-uuid
|
||||
#[proc_macro_derive(TypeUuid, attributes(uuid))]
|
||||
pub fn derive_type_uuid(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
type_uuid::type_uuid_derive(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// A macro that automatically generates type data for traits, which their implementors can then register.
|
||||
@ -588,6 +589,6 @@ pub fn impl_type_path(input: TokenStream) -> TokenStream {
|
||||
/// Derives `TypeUuid` for the given type. This is used internally to implement `TypeUuid` on foreign types, such as those in the std. This macro should be used in the format of `<[Generic Params]> [Type (Path)], [Uuid (String Literal)]`.
|
||||
#[proc_macro]
|
||||
pub fn impl_type_uuid(input: TokenStream) -> TokenStream {
|
||||
let def = parse_macro_input!(input as TypeUuidDef);
|
||||
gen_impl_type_uuid(def)
|
||||
let def = parse_macro_input!(input as type_uuid::TypeUuidDef);
|
||||
type_uuid::gen_impl_type_uuid(def).into()
|
||||
}
|
||||
|
||||
@ -1,53 +1,54 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use bevy_macro_utils::BevyManifest;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::token::Comma;
|
||||
use syn::*;
|
||||
use syn::{DeriveInput, Expr, ExprLit, Generics, Ident, Lit, LitInt, LitStr, Meta};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Parses input from a derive of `TypeUuid`.
|
||||
pub(crate) fn type_uuid_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
// Construct a representation of Rust code as a syntax tree
|
||||
// that we can manipulate
|
||||
let ast: DeriveInput = syn::parse(input).unwrap();
|
||||
// Build the trait implementation
|
||||
let type_ident = ast.ident;
|
||||
|
||||
pub(crate) fn type_uuid_derive(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let mut uuid = None;
|
||||
for attribute in ast.attrs.iter().filter(|attr| attr.path().is_ident("uuid")) {
|
||||
for attribute in input
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path().is_ident("uuid"))
|
||||
{
|
||||
let Meta::NameValue(ref name_value) = attribute.meta else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let uuid_str = match &name_value.value {
|
||||
Expr::Lit(ExprLit{lit: Lit::Str(lit_str), ..}) => lit_str,
|
||||
_ => panic!("`uuid` attribute must take the form `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"`."),
|
||||
_ => return Err(syn::Error::new_spanned(attribute, "`uuid` attribute must take the form `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"]`.")),
|
||||
};
|
||||
|
||||
uuid = Some(
|
||||
Uuid::parse_str(&uuid_str.value())
|
||||
.expect("Value specified to `#[uuid]` attribute is not a valid UUID."),
|
||||
);
|
||||
uuid =
|
||||
Some(Uuid::parse_str(&uuid_str.value()).map_err(|err| {
|
||||
syn::Error::new_spanned(uuid_str, format!("Invalid UUID: {err}"))
|
||||
})?);
|
||||
}
|
||||
|
||||
let uuid =
|
||||
uuid.expect("No `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"` attribute found.");
|
||||
gen_impl_type_uuid(TypeUuidDef {
|
||||
type_ident,
|
||||
generics: ast.generics,
|
||||
let uuid = uuid.ok_or_else(|| {
|
||||
syn::Error::new(
|
||||
Span::call_site(),
|
||||
"No `#[uuid = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"]` attribute found.",
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(gen_impl_type_uuid(TypeUuidDef {
|
||||
type_ident: input.ident,
|
||||
generics: input.generics,
|
||||
uuid,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// Generates an implementation of `TypeUuid`. If there any generics, the `TYPE_UUID` will be a composite of the generic types' `TYPE_UUID`.
|
||||
pub(crate) fn gen_impl_type_uuid(def: TypeUuidDef) -> proc_macro::TokenStream {
|
||||
pub(crate) fn gen_impl_type_uuid(def: TypeUuidDef) -> TokenStream {
|
||||
let uuid = def.uuid;
|
||||
let mut generics = def.generics;
|
||||
let ty = def.type_ident;
|
||||
|
||||
let bevy_reflect_path: Path = BevyManifest::default().get_path("bevy_reflect");
|
||||
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
|
||||
|
||||
generics.type_params_mut().for_each(|param| {
|
||||
param
|
||||
@ -74,12 +75,11 @@ pub(crate) fn gen_impl_type_uuid(def: TypeUuidDef) -> proc_macro::TokenStream {
|
||||
}
|
||||
});
|
||||
|
||||
let gen = quote! {
|
||||
quote! {
|
||||
impl #impl_generics #bevy_reflect_path::TypeUuid for #ty #type_generics #where_clause {
|
||||
const TYPE_UUID: #bevy_reflect_path::Uuid = #type_uuid;
|
||||
}
|
||||
};
|
||||
gen.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct containing the data required to generate an implementation of `TypeUuid`. This can be generated by either [`impl_type_uuid!`][crate::impl_type_uuid!] or [`type_uuid_derive`].
|
||||
@ -90,7 +90,7 @@ pub(crate) struct TypeUuidDef {
|
||||
}
|
||||
|
||||
impl Parse for TypeUuidDef {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let type_ident = input.parse::<Ident>()?;
|
||||
let generics = input.parse::<Generics>()?;
|
||||
input.parse::<Comma>()?;
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
#[test]
|
||||
fn test() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/type_uuid_derive/*.rs");
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
use bevy_reflect::TypeUuid;
|
||||
|
||||
fn main() {}
|
||||
|
||||
// Missing #[uuid] attribute
|
||||
#[derive(TypeUuid)]
|
||||
struct A;
|
||||
|
||||
// Malformed attribute
|
||||
#[derive(TypeUuid)]
|
||||
#[uuid = 42]
|
||||
struct B;
|
||||
|
||||
// UUID parse fail
|
||||
#[derive(TypeUuid)]
|
||||
#[uuid = "000"]
|
||||
struct C;
|
||||
@ -0,0 +1,19 @@
|
||||
error: No `#[uuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]` attribute found.
|
||||
--> tests/type_uuid_derive/derive_type_uuid.rs:6:10
|
||||
|
|
||||
6 | #[derive(TypeUuid)]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `TypeUuid` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: `uuid` attribute must take the form `#[uuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]`.
|
||||
--> tests/type_uuid_derive/derive_type_uuid.rs:11:1
|
||||
|
|
||||
11 | #[uuid = 42]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: Invalid UUID: invalid length: expected length 32 for simple format, found 3
|
||||
--> tests/type_uuid_derive/derive_type_uuid.rs:16:10
|
||||
|
|
||||
16 | #[uuid = "000"]
|
||||
| ^^^^^
|
||||
Loading…
Reference in New Issue
Block a user