Skip to content

Commit

Permalink
Add IsBase macro for better error reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
lilizoey committed Oct 18, 2024
1 parent 623aa41 commit 2849809
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 46 deletions.
15 changes: 15 additions & 0 deletions godot-core/src/obj/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ impl GodotClass for NoBase {
const INIT_LEVEL: InitLevel = InitLevel::Core; // arbitrary; never read.
}

#[diagnostic::on_unimplemented(
message = "expected base `{Self}` found `{A}`",
label = "expected base `{Self}` found `{A}`"
)]
#[doc(hidden)]
pub trait IsBase<T: GodotClass, A> {
#[doc(hidden)]
fn conv(b: Base<T>) -> A;
}
impl<T: GodotClass> IsBase<T, Base<T>> for Base<T> {
fn conv(b: Base<T>) -> Base<T> {
b
}
}

unsafe impl Bounds for NoBase {
type Memory = bounds::MemManual;
type DynMemory = bounds::MemManual;
Expand Down
8 changes: 1 addition & 7 deletions godot-macros/src/class/data_models/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use crate::class::{FieldExport, FieldVar};
use proc_macro2::{Ident, Span, TokenStream};
use quote::ToTokens;
use venial::Error;

pub struct Field {
pub name: Ident,
Expand Down Expand Up @@ -45,16 +44,11 @@ pub struct Fields {
/// The field with type `Base<T>`, if available.
pub base_field: Option<Field>,

/// The base field is either absent or is correctly formatted.
///
/// When this is false, there will always be a compile error ensuring the program fails to compile.
pub well_formed_base: bool,

/// Deprecation warnings.
pub deprecations: Vec<TokenStream>,

/// Errors during macro evaluation that shouldn't abort the execution of the macro.
pub errors: Vec<Error>,
pub errors: Vec<venial::Error>,
}

#[derive(Clone)]
Expand Down
57 changes: 18 additions & 39 deletions godot-macros/src/class/derive_godot_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,33 +63,21 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
let prv = quote! { ::godot::private };
let godot_exports_impl = make_property_impl(class_name, &fields);

let godot_withbase_impl = if let Some(Field { name, .. }) = &fields.base_field {
let implementation = if fields.well_formed_base {
quote! {
fn to_gd(&self) -> ::godot::obj::Gd<Self> {
self.#name.to_gd().cast()
let godot_withbase_impl = if let Some(Field { name, ty, .. }) = &fields.base_field {
// Apply the span of the field's type so that errors show up on the field's type.
quote_spanned! { ty.span()=>
impl ::godot::obj::WithBaseField for #class_name {
fn to_gd(&self) -> ::godot::obj::Gd<#class_name> {
// By not referencing the base field directly here we ensure that the user only gets one error when the base
// field's type is wrong.
let base = <#class_name as ::godot::obj::WithBaseField>::base_field(self);
base.to_gd().cast()
}

fn base_field(&self) -> &::godot::obj::Base<<Self as ::godot::obj::GodotClass>::Base> {
fn base_field(&self) -> &::godot::obj::Base<<#class_name as ::godot::obj::GodotClass>::Base> {
&self.#name
}
}
} else {
quote! {
fn to_gd(&self) -> ::godot::obj::Gd<Self> {
todo!()
}

fn base_field(&self) -> &::godot::obj::Base<<Self as ::godot::obj::GodotClass>::Base> {
todo!()
}
}
};

quote! {
impl ::godot::obj::WithBaseField for #class_name {
#implementation
}
}
} else {
TokenStream::new()
Expand Down Expand Up @@ -241,14 +229,15 @@ impl ClassAttributes {
}

fn make_godot_init_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
let base_init = if let Some(Field { name, .. }) = &fields.base_field {
let base = if fields.well_formed_base {
quote! { base }
} else {
quote! { ::std::todo!("The base field is currently broken") }
let base_init = if let Some(Field { name, ty, .. }) = &fields.base_field {
let base_type =
quote_spanned! { ty.span()=> <#class_name as ::godot::obj::GodotClass>::Base };
let base_field_type = quote_spanned! { ty.span()=> ::godot::obj::Base<#base_type> };
let base = quote_spanned! { ty.span()=>
<#base_field_type as ::godot::obj::IsBase<#base_type, #ty>>::conv(base)
};

quote! { #name: #base, }
quote_spanned! { ty.span()=> #name: #base, }
} else {
TokenStream::new()
};
Expand All @@ -267,9 +256,7 @@ fn make_godot_init_impl(class_name: &Ident, fields: &Fields) -> TokenStream {

quote! {
impl ::godot::obj::cap::GodotDefault for #class_name {
fn __godot_user_init(base: ::godot::obj::Base<Self::Base>) -> Self {
// If the base field is broken then we may get unreachable code due to the `todo`.
#[allow(unreachable_code)]
fn __godot_user_init(base: ::godot::obj::Base<<#class_name as ::godot::obj::GodotClass>::Base>) -> Self {
Self {
#( #rest_init )*
#base_init
Expand Down Expand Up @@ -451,8 +438,6 @@ fn parse_fields(
let mut base_field = Option::<Field>::None;
let mut deprecations = vec![];
let mut errors = vec![];
// Base field is either absent or exists and has no errors.
let mut well_formed_base = true;

// Attributes on struct fields
for (named_field, _punct) in named_fields {
Expand Down Expand Up @@ -573,39 +558,34 @@ fn parse_fields(
// Extra validation; eventually assign to base_fields or all_fields.
if is_base {
if field.is_onready {
well_formed_base = false;
errors.push(error!(
field.ty.clone(),
"base field cannot have type `OnReady<T>`"
));
}

if let Some(var) = field.var.as_ref() {
well_formed_base = false;
errors.push(error!(
var.span,
"base field cannot have the attribute #[var]"
));
}

if let Some(export) = field.export.as_ref() {
well_formed_base = false;
errors.push(error!(
export.span,
"base field cannot have the attribute #[export]"
));
}

if let Some(default_val) = field.default_val.as_ref() {
well_formed_base = false;
errors.push(error!(
default_val.span,
"base field cannot have the attribute #[init]"
));
}

if let Some(prev_base) = base_field.replace(field) {
well_formed_base = false;
// Ensure at most one Base<T>.
errors.push(error!(
// base_field.unwrap().name,
Expand All @@ -621,7 +601,6 @@ fn parse_fields(
Ok(Fields {
all_fields,
base_field,
well_formed_base,
deprecations,
errors,
})
Expand Down

0 comments on commit 2849809

Please sign in to comment.