@@ -14,6 +14,7 @@ use crate::special_cases;
14
14
use crate :: util:: ident;
15
15
use proc_macro2:: { Ident , TokenStream } ;
16
16
use quote:: quote;
17
+ use std:: fmt:: Write ;
17
18
18
19
pub fn make_virtual_methods_trait (
19
20
class : & Class ,
@@ -26,19 +27,20 @@ pub fn make_virtual_methods_trait(
26
27
let trait_name = ident ( trait_name_str) ;
27
28
let class_name = & class. name ( ) . rust_ty ;
28
29
29
- let virtual_method_fns = make_all_virtual_methods ( class, all_base_names, view) ;
30
+ let ( virtual_method_fns, extra_docs ) = make_all_virtual_methods ( class, all_base_names, view) ;
30
31
let special_virtual_methods = make_special_virtual_methods ( notification_enum_name) ;
31
32
32
33
let trait_doc = docs:: make_virtual_trait_doc ( trait_name_str, class. name ( ) ) ;
33
34
34
35
quote ! {
35
36
#[ doc = #trait_doc]
37
+ #[ doc = #extra_docs]
36
38
#[ allow( unused_variables) ]
37
39
#[ allow( clippy:: unimplemented) ]
38
40
#cfg_attributes
39
41
pub trait #trait_name: crate :: obj:: GodotClass <Base = #class_name> + crate :: private:: You_forgot_the_attribute__godot_api {
40
42
#special_virtual_methods
41
- #( # virtual_method_fns ) *
43
+ #virtual_method_fns
42
44
}
43
45
}
44
46
}
@@ -193,16 +195,18 @@ fn make_all_virtual_methods(
193
195
class : & Class ,
194
196
all_base_names : & [ TyName ] ,
195
197
view : & ApiView ,
196
- ) -> Vec < TokenStream > {
197
- let mut all_tokens = vec ! [ ] ;
198
+ ) -> ( TokenStream , String ) {
199
+ let mut all_tokens = TokenStream :: new ( ) ;
198
200
199
201
for method in class. methods . iter ( ) {
200
202
// Assumes that inner function filters on is_virtual.
201
203
if let Some ( tokens) = make_virtual_method ( method, VirtualMethodPresence :: Inherit ) {
202
- all_tokens. push ( tokens) ;
204
+ all_tokens. extend ( tokens) ;
203
205
}
204
206
}
205
207
208
+ let mut changes_from_base = String :: new ( ) ;
209
+
206
210
for base_name in all_base_names {
207
211
let base_class = view. get_engine_class ( base_name) ;
208
212
for method in base_class. methods . iter ( ) {
@@ -211,14 +215,56 @@ fn make_all_virtual_methods(
211
215
let derived_presence =
212
216
special_cases:: get_derived_virtual_method_presence ( class. name ( ) , method. name ( ) ) ;
213
217
218
+ // Collect all changes in a Markdown table.
219
+ let new = match derived_presence {
220
+ VirtualMethodPresence :: Inherit => None ,
221
+ VirtualMethodPresence :: Override { is_required } => {
222
+ Some ( format_required ( is_required) )
223
+ }
224
+ VirtualMethodPresence :: Remove => Some ( "removed" ) ,
225
+ } ;
226
+ if let Some ( new) = new {
227
+ let orig = format_required ( method. is_virtual_required ( ) ) ;
228
+ let base_name = & base_name. rust_ty ;
229
+ let method_name = format_method_name ( method) ;
230
+
231
+ write ! (
232
+ changes_from_base,
233
+ "\n | [`I{base_name}::{method_name}`](crate::classes::I{base_name}::{method_name}) | {orig} | {new} |"
234
+ )
235
+ . unwrap ( ) ;
236
+ }
237
+
214
238
if let Some ( tokens) = make_virtual_method ( method, derived_presence) {
215
- all_tokens. push ( tokens) ;
239
+ all_tokens. extend ( tokens) ;
216
240
}
217
241
}
218
242
}
219
243
220
- all_tokens
244
+ let extra_docs = if changes_from_base. is_empty ( ) {
245
+ String :: new ( )
246
+ } else {
247
+ let class_name = & class. name ( ) . rust_ty ;
248
+ format ! (
249
+ "\n \n # Changes from base interface traits\n \
250
+ The following virtual methods originally declared in direct/indirect base classes have their presence (required/optional) changed \
251
+ in `I{class_name}`. This can happen if Godot already overrides virtual methods, discouraging the user from further overriding them.\n \
252
+ \n \n | Base method | Original | New |\n | --- | --- | --- |{changes_from_base}"
253
+ )
254
+ } ;
255
+
256
+ ( all_tokens, extra_docs)
221
257
}
222
258
223
- // ----------------------------------------------------------------------------------------------------------------------------------------------
224
- // Helper types
259
+ fn format_required ( is_required : bool ) -> & ' static str {
260
+ if is_required {
261
+ "required"
262
+ } else {
263
+ "optional"
264
+ }
265
+ }
266
+
267
+ fn format_method_name ( method : & ClassMethod ) -> & str {
268
+ // TODO when we have `_unsafe` or similar postfix with raw pointers, update this here.
269
+ method. name ( )
270
+ }
0 commit comments