@@ -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,10 +69,17 @@ 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
76
+ struct Construction {
77
+ constructor : TokenStream ,
78
+ construct_doc : & ' static str ,
79
+ final_doc : Option < & ' static str > ,
80
+ godot_default_impl : TokenStream ,
81
+ }
82
+
76
83
fn make_class ( class : & Class , ctx : & mut Context , view : & ApiView ) -> GeneratedClass {
77
84
let class_name = class. name ( ) ;
78
85
@@ -90,8 +97,16 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
90
97
None => ( quote ! { crate :: obj:: NoBase } , None ) ,
91
98
} ;
92
99
93
- let ( constructor, construct_doc, godot_default_impl) = make_constructor_and_default ( class, ctx) ;
94
- let construct_doc = construct_doc. replace ( "Self" , & class_name. rust_ty . to_string ( ) ) ;
100
+ let Construction {
101
+ constructor,
102
+ construct_doc,
103
+ final_doc,
104
+ godot_default_impl,
105
+ } = make_constructor_and_default ( class, ctx) ;
106
+
107
+ let mut extended_class_doc = construct_doc. replace ( "Self" , & class_name. rust_ty . to_string ( ) ) ;
108
+ extended_class_doc. push_str ( final_doc. unwrap_or_default ( ) ) ;
109
+
95
110
let api_level = class. api_level ;
96
111
let init_level = api_level. to_init_level ( ) ;
97
112
@@ -130,10 +145,10 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
130
145
131
146
let enums = enums:: make_enums ( & class. enums , & cfg_attributes) ;
132
147
let constants = constants:: make_constants ( & class. constants ) ;
133
- let inherits_macro = format_ident ! ( "unsafe_inherits_transitive_{}" , class_name. rust_ty) ;
134
148
let deref_impl = make_deref_impl ( class_name, & base_ty) ;
135
149
136
150
let all_bases = ctx. inheritance_tree ( ) . collect_all_bases ( class_name) ;
151
+ let ( inherits_macro_ident, inherits_macro_code) = make_inherits_macro ( class, & all_bases) ;
137
152
let ( notification_enum, notification_enum_name) =
138
153
notifications:: make_notification_enum ( class_name, & all_bases, & cfg_attributes, ctx) ;
139
154
@@ -180,11 +195,6 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
180
195
}
181
196
} ;
182
197
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
198
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub.
189
199
let imports = util:: make_imports ( ) ;
190
200
let tokens = quote ! {
@@ -199,7 +209,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
199
209
use super :: * ;
200
210
201
211
#[ doc = #class_doc]
202
- #[ doc = #construct_doc ]
212
+ #[ doc = #extended_class_doc ]
203
213
#cfg_attributes
204
214
#[ derive( Debug ) ]
205
215
#[ repr( C ) ]
@@ -247,20 +257,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
247
257
248
258
#godot_default_impl
249
259
#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
- }
260
+ #inherits_macro_code
264
261
}
265
262
266
263
#builders
@@ -275,11 +272,69 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
275
272
name : notification_enum_name,
276
273
declared_by_own_class : notification_enum. is_some ( ) ,
277
274
} ,
278
- inherits_macro_ident : inherits_macro ,
275
+ inherits_macro_ident,
279
276
has_sidecar_module,
280
277
}
281
278
}
282
279
280
+ /// If the class can be inherited from (non-final), create a macro that can be accessed in subclasses to implement the `Inherits` trait.
281
+ ///
282
+ /// Returns empty tokens if the class is final.
283
+ fn make_inherits_macro ( class : & Class , all_bases : & [ TyName ] ) -> ( Option < Ident > , TokenStream ) {
284
+ let class_name = class. name ( ) ;
285
+
286
+ // Create a macro that can be accessed in subclasses to implement the Inherits trait.
287
+ // Use this name because when typing a non-existent class, users will be met with the following error:
288
+ // could not find `inherit_from_OS__ensure_class_exists` in `class_macros`
289
+ //
290
+ // Former macro name was `unsafe_inherits_transitive_*`.
291
+ let inherits_macro_ident =
292
+ format_ident ! ( "inherit_from_{}__ensure_class_exists" , class_name. rust_ty) ;
293
+
294
+ // For final classes, we can directly create a meaningful compile error.
295
+ if class. is_final {
296
+ let error_msg = format ! (
297
+ "Class `{}` is final and cannot be inherited from." ,
298
+ class_name. rust_ty
299
+ ) ;
300
+
301
+ let code = quote ! {
302
+ #[ macro_export]
303
+ #[ allow( non_snake_case) ]
304
+ macro_rules! #inherits_macro_ident {
305
+ ( $Class : ident) => {
306
+ compile_error!( #error_msg) ;
307
+ }
308
+ }
309
+ } ;
310
+
311
+ return ( None , code) ;
312
+ }
313
+
314
+ let inherits_macro_safety_doc = format ! (
315
+ "The provided class must be a subclass of all the superclasses of [`{}`]" ,
316
+ class_name. rust_ty
317
+ ) ;
318
+
319
+ let code = quote ! {
320
+ /// # Safety
321
+ ///
322
+ #[ doc = #inherits_macro_safety_doc]
323
+ #[ macro_export]
324
+ #[ allow( non_snake_case) ]
325
+ macro_rules! #inherits_macro_ident {
326
+ ( $Class : ident) => {
327
+ unsafe impl :: godot:: obj:: Inherits <:: godot:: classes:: #class_name> for $Class { }
328
+ #(
329
+ unsafe impl :: godot:: obj:: Inherits <:: godot:: classes:: #all_bases> for $Class { }
330
+ ) *
331
+ }
332
+ }
333
+ } ;
334
+
335
+ ( Some ( inherits_macro_ident) , code)
336
+ }
337
+
283
338
fn make_class_module_file ( classes_and_modules : Vec < GeneratedClassModule > ) -> TokenStream {
284
339
let mut class_decls = Vec :: new ( ) ;
285
340
let mut notify_decls = Vec :: new ( ) ;
@@ -318,6 +373,11 @@ fn make_class_module_file(classes_and_modules: Vec<GeneratedClassModule>) -> Tok
318
373
..
319
374
} = m;
320
375
376
+ // For final classes, do nothing.
377
+ let Some ( inherits_macro_ident) = inherits_macro_ident else {
378
+ return TokenStream :: new ( ) ;
379
+ } ;
380
+
321
381
// We cannot re-export the following, because macro is in the crate root
322
382
// pub use #module_ident::re_export::#inherits_macro_ident;
323
383
quote ! {
@@ -341,17 +401,15 @@ fn make_class_module_file(classes_and_modules: Vec<GeneratedClassModule>) -> Tok
341
401
}
342
402
}
343
403
344
- fn make_constructor_and_default (
345
- class : & Class ,
346
- ctx : & Context ,
347
- ) -> ( 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.
404
+ fn make_constructor_and_default ( class : & Class , ctx : & Context ) -> Construction {
405
+ let class_name = class. name ( ) ;
406
+
407
+ let godot_class_stringname = make_string_name ( & class_name. godot_ty ) ;
408
+ // Note: this could use class_name() but is not yet done due to potential future lazy-load refactoring.
351
409
//let class_name_obj = quote! { <Self as crate::obj::GodotClass>::class_name() };
352
410
353
411
let ( constructor, construct_doc, has_godot_default_impl) ;
354
- if ctx. is_singleton ( godot_class_name ) {
412
+ if ctx. is_singleton ( class_name ) {
355
413
// Note: we cannot return &'static mut Self, as this would be very easy to mutably alias.
356
414
// &'static Self would be possible, but we would lose the whole mutability information (even if that is best-effort and
357
415
// not strict Rust mutability, it makes the API much more usable).
@@ -391,6 +449,15 @@ fn make_constructor_and_default(
391
449
has_godot_default_impl = true ;
392
450
}
393
451
452
+ let final_doc = if class. is_final {
453
+ Some (
454
+ "\n \n # Final class\n \n \
455
+ This class is _final_, meaning you cannot inherit from it.",
456
+ )
457
+ } else {
458
+ None
459
+ } ;
460
+
394
461
let godot_default_impl = if has_godot_default_impl {
395
462
let class_name = & class. name ( ) . rust_ty ;
396
463
quote ! {
@@ -404,7 +471,12 @@ fn make_constructor_and_default(
404
471
TokenStream :: new ( )
405
472
} ;
406
473
407
- ( constructor, construct_doc, godot_default_impl)
474
+ Construction {
475
+ constructor,
476
+ construct_doc,
477
+ final_doc,
478
+ godot_default_impl,
479
+ }
408
480
}
409
481
410
482
fn make_deref_impl ( class_name : & TyName , base_ty : & TokenStream ) -> TokenStream {
0 commit comments