@@ -60,7 +60,7 @@ pub fn generate_class_files(
60
60
struct GeneratedClass {
61
61
code : TokenStream ,
62
62
notification_enum : NotificationEnum ,
63
- inherits_macro_ident : Ident ,
63
+ inherits_macro_ident : Option < Ident > ,
64
64
/// Sidecars are the associated modules with related enum/flag types, such as `node_3d` for `Node3D` class.
65
65
has_sidecar_module : bool ,
66
66
}
@@ -69,7 +69,7 @@ struct GeneratedClassModule {
69
69
class_name : TyName ,
70
70
module_name : ModName ,
71
71
own_notification_enum_name : Option < Ident > ,
72
- inherits_macro_ident : Ident ,
72
+ inherits_macro_ident : Option < Ident > ,
73
73
is_pub_sidecar : bool ,
74
74
}
75
75
@@ -130,10 +130,10 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
130
130
131
131
let enums = enums:: make_enums ( & class. enums , & cfg_attributes) ;
132
132
let constants = constants:: make_constants ( & class. constants ) ;
133
- let inherits_macro = format_ident ! ( "unsafe_inherits_transitive_{}" , class_name. rust_ty) ;
134
133
let deref_impl = make_deref_impl ( class_name, & base_ty) ;
135
134
136
135
let all_bases = ctx. inheritance_tree ( ) . collect_all_bases ( class_name) ;
136
+ let ( inherits_macro_ident, inherits_macro_code) = make_inherits_macro ( class, & all_bases) ;
137
137
let ( notification_enum, notification_enum_name) =
138
138
notifications:: make_notification_enum ( class_name, & all_bases, & cfg_attributes, ctx) ;
139
139
@@ -180,11 +180,6 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
180
180
}
181
181
} ;
182
182
183
- let inherits_macro_safety_doc = format ! (
184
- "The provided class must be a subclass of all the superclasses of [`{}`]" ,
185
- class_name. rust_ty
186
- ) ;
187
-
188
183
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub.
189
184
let imports = util:: make_imports ( ) ;
190
185
let tokens = quote ! {
@@ -247,20 +242,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
247
242
248
243
#godot_default_impl
249
244
#deref_impl
250
-
251
- /// # Safety
252
- ///
253
- #[ doc = #inherits_macro_safety_doc]
254
- #[ macro_export]
255
- #[ allow( non_snake_case) ]
256
- macro_rules! #inherits_macro {
257
- ( $Class : ident) => {
258
- unsafe impl :: godot:: obj:: Inherits <:: godot:: classes:: #class_name> for $Class { }
259
- #(
260
- unsafe impl :: godot:: obj:: Inherits <:: godot:: classes:: #all_bases> for $Class { }
261
- ) *
262
- }
263
- }
245
+ #inherits_macro_code
264
246
}
265
247
266
248
#builders
@@ -275,11 +257,69 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
275
257
name : notification_enum_name,
276
258
declared_by_own_class : notification_enum. is_some ( ) ,
277
259
} ,
278
- inherits_macro_ident : inherits_macro ,
260
+ inherits_macro_ident,
279
261
has_sidecar_module,
280
262
}
281
263
}
282
264
265
+ /// If the class can be inherited from (non-final), create a macro that can be accessed in subclasses to implement the `Inherits` trait.
266
+ ///
267
+ /// Returns empty tokens if the class is final.
268
+ fn make_inherits_macro ( class : & Class , all_bases : & [ TyName ] ) -> ( Option < Ident > , TokenStream ) {
269
+ let class_name = class. name ( ) ;
270
+
271
+ // Create a macro that can be accessed in subclasses to implement the Inherits trait.
272
+ // Use this name because when typing a non-existent class, users will be met with the following error:
273
+ // could not find `inherit_from_OS__ensure_class_exists` in `class_macros`
274
+ //
275
+ // Former macro name was `unsafe_inherits_transitive_*`.
276
+ let inherits_macro_ident =
277
+ format_ident ! ( "inherit_from_{}__ensure_class_exists" , class_name. rust_ty) ;
278
+
279
+ // For final classes, we can directly create a meaningful compile error.
280
+ if class. is_final {
281
+ let error_msg = format ! (
282
+ "Class `{}` is final and cannot be inherited from." ,
283
+ class_name. rust_ty
284
+ ) ;
285
+
286
+ let code = quote ! {
287
+ #[ macro_export]
288
+ #[ allow( non_snake_case) ]
289
+ macro_rules! #inherits_macro_ident {
290
+ ( $Class : ident) => {
291
+ compile_error!( #error_msg) ;
292
+ }
293
+ }
294
+ } ;
295
+
296
+ return ( None , code) ;
297
+ }
298
+
299
+ let inherits_macro_safety_doc = format ! (
300
+ "The provided class must be a subclass of all the superclasses of [`{}`]" ,
301
+ class_name. rust_ty
302
+ ) ;
303
+
304
+ let code = quote ! {
305
+ /// # Safety
306
+ ///
307
+ #[ doc = #inherits_macro_safety_doc]
308
+ #[ macro_export]
309
+ #[ allow( non_snake_case) ]
310
+ macro_rules! #inherits_macro_ident {
311
+ ( $Class : ident) => {
312
+ unsafe impl :: godot:: obj:: Inherits <:: godot:: classes:: #class_name> for $Class { }
313
+ #(
314
+ unsafe impl :: godot:: obj:: Inherits <:: godot:: classes:: #all_bases> for $Class { }
315
+ ) *
316
+ }
317
+ }
318
+ } ;
319
+
320
+ ( Some ( inherits_macro_ident) , code)
321
+ }
322
+
283
323
fn make_class_module_file ( classes_and_modules : Vec < GeneratedClassModule > ) -> TokenStream {
284
324
let mut class_decls = Vec :: new ( ) ;
285
325
let mut notify_decls = Vec :: new ( ) ;
@@ -318,6 +358,11 @@ fn make_class_module_file(classes_and_modules: Vec<GeneratedClassModule>) -> Tok
318
358
..
319
359
} = m;
320
360
361
+ // For final classes, do nothing.
362
+ let Some ( inherits_macro_ident) = inherits_macro_ident else {
363
+ return TokenStream :: new ( ) ;
364
+ } ;
365
+
321
366
// We cannot re-export the following, because macro is in the crate root
322
367
// pub use #module_ident::re_export::#inherits_macro_ident;
323
368
quote ! {
@@ -345,13 +390,14 @@ fn make_constructor_and_default(
345
390
class : & Class ,
346
391
ctx : & Context ,
347
392
) -> ( TokenStream , & ' static str , TokenStream ) {
348
- let godot_class_name = & class. name ( ) . godot_ty ;
349
- let godot_class_stringname = make_string_name ( godot_class_name) ;
350
- // Note: this could use class_name() but is not yet done due to upcoming lazy-load refactoring.
393
+ let class_name = class. name ( ) ;
394
+
395
+ let godot_class_stringname = make_string_name ( & class_name. godot_ty ) ;
396
+ // Note: this could use class_name() but is not yet done due to potential future lazy-load refactoring.
351
397
//let class_name_obj = quote! { <Self as crate::obj::GodotClass>::class_name() };
352
398
353
399
let ( constructor, construct_doc, has_godot_default_impl) ;
354
- if ctx. is_singleton ( godot_class_name ) {
400
+ if ctx. is_singleton ( class_name ) {
355
401
// Note: we cannot return &'static mut Self, as this would be very easy to mutably alias.
356
402
// &'static Self would be possible, but we would lose the whole mutability information (even if that is best-effort and
357
403
// not strict Rust mutability, it makes the API much more usable).
0 commit comments