@@ -16,8 +16,8 @@ use crate::meta::ClassId;
1616use crate :: meta:: error:: FromGodotError ;
1717use crate :: obj:: { DynGd , Gd , GodotClass , Singleton , cap} ;
1818use crate :: private:: { ClassShard , ShardItem } ;
19- use crate :: registry:: callbacks;
2019use crate :: registry:: shard:: { DynTraitImpl , ErasedRegisterFn , ITraitImpl , InherentImpl , Struct } ;
20+ use crate :: registry:: { callbacks, reg_validation} ;
2121use crate :: { godot_error, godot_warn, sys} ;
2222
2323/// Returns a lock to a global map of loaded classes, by initialization level.
@@ -585,14 +585,14 @@ fn fill_into<T>(dst: &mut Option<T>, src: Option<T>) -> Result<(), ()> {
585585fn register_class_raw ( mut info : ClassRegistrationInfo ) {
586586 // Some metadata like dynify fns are already emptied at this point. Only consider registrations for Godot.
587587
588- // First register class...
589- validate_class_constraints ( & info) ;
590-
591588 let class_name = info. class_name ;
592589 let parent_class_name = info
593590 . parent_class_name
594591 . expect ( "class defined (parent_class_name)" ) ;
595592
593+ // First register class...
594+ precheck_class_registration ( & info, parent_class_name) ;
595+
596596 // Register virtual functions -- if the user provided some via #[godot_api], take those; otherwise, use the
597597 // ones generated alongside #[derive(GodotClass)]. The latter can also be null, if no OnReady is provided.
598598 if info. godot_params . get_virtual_func . is_none ( ) {
@@ -622,14 +622,14 @@ fn register_class_raw(mut info: ClassRegistrationInfo) {
622622 ptr:: addr_of!( info. godot_params) ,
623623 ) ;
624624
625- // ...then see if it worked.
626- // This is necessary because the above registration does not report errors (apart from console output).
625+ // ...then see if it worked. The registration above does not report errors (apart from console output), and precheck may miss something.
627626 let tag = interface_fn ! ( classdb_get_class_tag) ( class_name. string_sys ( ) ) ;
628627 tag. is_null ( )
629628 } ;
630629
631630 // Do not panic here; otherwise lock is poisoned and the whole extension becomes unusable.
632631 // This can happen during hot reload if a class changes base type in an incompatible way (e.g. RefCounted -> Node).
632+ // Also, the pre-validation only runs under strict safeguards, there may be registrations that Godot rejects but godot-rust doesn't catch.
633633 if registration_failed {
634634 godot_error ! (
635635 "Failed to register class `{class_name}`; check preceding Godot stderr messages."
@@ -661,8 +661,11 @@ fn register_class_raw(mut info: ClassRegistrationInfo) {
661661 }
662662}
663663
664- fn validate_class_constraints ( _class : & ClassRegistrationInfo ) {
665- // TODO: if we add builder API, the proc-macro checks in parse_struct_attributes() etc. should be duplicated here.
664+ /// Validates a class registration before it is handed to Godot, which reports errors only to stderr.
665+ fn precheck_class_registration ( class_info : & ClassRegistrationInfo , parent_class_id : ClassId ) {
666+ // TODO(v0.7): if we add builder API, the proc-macro checks in parse_struct_attributes() etc. should be duplicated here.
667+
668+ reg_validation:: validate_class ( class_info. class_name , parent_class_id) ;
666669}
667670
668671fn unregister_class_raw ( class : LoadedClass ) {
0 commit comments