-
-
Notifications
You must be signed in to change notification settings - Fork 190
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
base: master
Are you sure you want to change the base?
Conversation
7f164d2
to
623aa41
Compare
API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-920 |
There was a problem hiding this 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>, |
There was a problem hiding this comment.
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 🙂).
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>>`
This makes rust actually show where the errors are when the proc macro errors. Some examples:
and