Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More accurately provide spans to errors in the GodotClass macro #920

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

lilizoey
Copy link
Member

This makes rust actually show where the errors are when the proc macro errors. Some examples:
image
image

and

image
image

@lilizoey lilizoey added quality-of-life No new functionality, but improves ergonomics/internals c: register Register classes, functions and other symbols to GDScript labels Oct 15, 2024
@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-920

Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, this is a huge quality-of-life improvement!

This reminds me of #641, which could in the future probably be reviewed in a similar way?

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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually tried to keep venial types qualified, since they often have very generic names (like here 🙂).

Suggested change
pub errors: Vec<Error>,
pub errors: Vec<venial::Error>,

@@ -62,8 +64,8 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
let godot_exports_impl = make_property_impl(class_name, &fields);

let godot_withbase_impl = if let Some(Field { name, .. }) = &fields.base_field {
quote! {
impl ::godot::obj::WithBaseField for #class_name {
let implementation = if fields.well_formed_base {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment that we still need to generate code in case of bad base syntax, so that spans are retained/propagated to the compiler error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's more about avoiding confusing/misleading errors. for instance if you make the base field an OnReady then you'll get errors pointing at the GodotClass macro that OnReady is missing a to_gd() function. but on top of that you'll also get the error appropriately pointing at the base field, telling you you can't use OnReady for the base field. this code ensures that only the latter error triggers, as that's the more important and useful error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though this does actually cause an issue here:
Screenshot_20241016_191646
where there is only an error at the #[var], and no error corresponding to the type being wrong like here:
Screenshot_20241016_191714
i wonder if there's a good way to fix both

Copy link
Member Author

@lilizoey lilizoey Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've mostly figured out a nicer way to fix this actually, but one problem i keep having is that i can't find a good way to get rust to report a nice error when the type is wrong. or rather to stop reporting a bad error. because the init auto generated code creates code like this (simplified some long paths for readability):

impl GodotDefault for Foo {
    fn __godot_user_init(base: Base<Self::Base>) -> Self {
        Self {
            base: base,
        }
    }
}

and if the user declares the base field to have the type say Option<Gd<Node2D>>, then rust will report an error that an Option<Gd<Node2D>> was expected (since that's the type of the base field) but got a Base<Node2D> (the type of the base argument). which is kinda backwards, we want rust to report that a Base<Node2D> was expected but got an Option<Gd<Node2D>>. we can relatively easily add an error message of that kind, but the original error remains anyway.

Copy link
Member Author

@lilizoey lilizoey Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually i think there's a way to do it, it involves making a custom trait like this:

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

which is used like this in the codegen:

impl GodotDefault for Foo {
    fn __godot_user_init(
        base: Base<Node2D>,
    ) -> Self {
        Self {
            base: <Base<Node2D> as IsBase<Node2D, Option<Gd<Node2D>>>>::conv(base),
        }
    }
}

this gives this error (it's pointing at the wrong place atm but i think that's fixable):

error[E0277]: expected base `Base<godot::prelude::Node2D>` got `Option<godot::prelude::Gd<godot::prelude::Node2D>>`
 --> src/creature.rs:7:10
  |
7 | #[derive(GodotClass)]
  |          ^^^^^^^^^^ expected base `Base<godot::prelude::Node2D>` got `Option<godot::prelude::Gd<godot::prelude::Node2D>>`
  |
  = help: the trait `IsBase<godot::prelude::Node2D, Option<godot::prelude::Gd<godot::prelude::Node2D>>>` is not implemented for `Base<godot::prelude::Node2D>`
  = help: the trait `IsBase<godot::prelude::Node2D, Base<godot::prelude::Node2D>>` is implemented for `Base<godot::prelude::Node2D>`
  = help: for that trait implementation, expected `Base<godot::prelude::Node2D>`, found `Option<godot::prelude::Gd<godot::prelude::Node2D>>`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: register Register classes, functions and other symbols to GDScript quality-of-life No new functionality, but improves ergonomics/internals
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants