Skip to content

Commit d4a52fd

Browse files
committed
Annotate #[cfg(feature = "experimental-godot-api")] for all generated experimental classes
This is not strictly necessary, because those classes are already excluded by codegen when the feature is disabled. However, explicitly adding the #[cfg(...)] helps our doc pipeline to recognize and annotate such classes. This commit is a bit involved, because it first adds an is_experimental property to the domain model, and then generates #[cfg] for all the public symbols -- not just the class itself, but its module, interface trait, related enums and ex-builder structs.
1 parent b259a81 commit d4a52fd

File tree

11 files changed

+58
-15
lines changed

11 files changed

+58
-15
lines changed

godot-codegen/src/generator/builtins.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn make_builtin_class(class: &BuiltinClass, ctx: &mut Context) -> GeneratedBuilt
101101
} = make_builtin_methods(class, &class.methods, ctx);
102102

103103
let imports = util::make_imports();
104-
let enums = enums::make_enums(&class.enums);
104+
let enums = enums::make_enums(&class.enums, &TokenStream::new());
105105
let special_constructors = make_special_builtin_methods(class.name(), ctx);
106106

107107
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
@@ -245,5 +245,6 @@ fn make_builtin_method_definition(
245245
ptrcall_invocation,
246246
},
247247
safety_doc,
248+
&TokenStream::new(),
248249
)
249250
}

godot-codegen/src/generator/classes.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,31 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
9494
let api_level = class.api_level;
9595
let init_level = api_level.to_init_level();
9696

97+
// This is not strictly needed because the classes are included/excluded by codegen depending on that feature, however it helps the
98+
// docs pipeline which converts #[ cfg(...) ] to #[ doc(cfg(...)) ], resulting in "only available in ..." labels in the HTML output.
99+
// (The above has spaces because there's a primitive sed expression at work :) )
100+
let (cfg_attributes, cfg_inner_attributes);
101+
if class.is_experimental {
102+
cfg_attributes = quote! { #[cfg(feature = "experimental-godot-api")] };
103+
cfg_inner_attributes = quote! { #![cfg(feature = "experimental-godot-api")] };
104+
} else {
105+
cfg_attributes = TokenStream::new();
106+
cfg_inner_attributes = TokenStream::new();
107+
};
108+
97109
let FnDefinitions {
98110
functions: methods,
99111
builders,
100-
} = make_class_methods(class, &class.methods, ctx);
112+
} = make_class_methods(class, &class.methods, &cfg_attributes, ctx);
101113

102-
let enums = enums::make_enums(&class.enums);
114+
let enums = enums::make_enums(&class.enums, &cfg_attributes);
103115
let constants = constants::make_constants(&class.constants);
104116
let inherits_macro = format_ident!("unsafe_inherits_transitive_{}", class_name.rust_ty);
105117
let deref_impl = make_deref_impl(class_name, &base_ty);
106118

107119
let all_bases = ctx.inheritance_tree().collect_all_bases(class_name);
108120
let (notification_enum, notification_enum_name) =
109-
notifications::make_notification_enum(class_name, &all_bases, ctx);
121+
notifications::make_notification_enum(class_name, &all_bases, &cfg_attributes, ctx);
110122

111123
// Associated "sidecar" module is made public if there are other symbols related to the class, which are not
112124
// in top-level godot::engine module (notification enums are not in the sidecar, but in godot::engine::notify).
@@ -120,11 +132,13 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
120132
has_sidecar_module,
121133
);
122134
let module_doc = docs::make_module_doc(class_name);
135+
123136
let virtual_trait = virtual_traits::make_virtual_methods_trait(
124137
class,
125138
&all_bases,
126139
&virtual_trait_str,
127140
&notification_enum_name,
141+
&cfg_attributes,
128142
view,
129143
);
130144

@@ -156,6 +170,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
156170
let imports = util::make_imports();
157171
let tokens = quote! {
158172
#![doc = #module_doc]
173+
#cfg_inner_attributes
159174

160175
#imports
161176
use crate::engine::notify::*;
@@ -166,6 +181,7 @@ fn make_class(class: &Class, ctx: &mut Context, view: &ApiView) -> GeneratedClas
166181

167182
#[doc = #class_doc]
168183
#[doc = #construct_doc]
184+
#cfg_attributes
169185
#[derive(Debug)]
170186
#[repr(C)]
171187
pub struct #class_name {
@@ -406,12 +422,17 @@ fn make_bounds(class: &Class) -> (Ident, Ident) {
406422
(assoc_memory, assoc_dyn_memory)
407423
}
408424

409-
fn make_class_methods(class: &Class, methods: &[ClassMethod], ctx: &mut Context) -> FnDefinitions {
425+
fn make_class_methods(
426+
class: &Class,
427+
methods: &[ClassMethod],
428+
cfg_attributes: &TokenStream,
429+
ctx: &mut Context,
430+
) -> FnDefinitions {
410431
let get_method_table = class.api_level.table_global_getter();
411432

412-
let definitions = methods
413-
.iter()
414-
.map(|method| make_class_method_definition(class, method, &get_method_table, ctx));
433+
let definitions = methods.iter().map(|method| {
434+
make_class_method_definition(class, method, &get_method_table, cfg_attributes, ctx)
435+
});
415436

416437
FnDefinitions::expand(definitions)
417438
}
@@ -420,6 +441,7 @@ fn make_class_method_definition(
420441
class: &Class,
421442
method: &ClassMethod,
422443
get_method_table: &Ident,
444+
cfg_attributes: &TokenStream,
423445
ctx: &mut Context,
424446
) -> FnDefinition {
425447
let FnDirection::Outbound { hash } = method.direction() else {
@@ -489,5 +511,6 @@ fn make_class_method_definition(
489511
ptrcall_invocation,
490512
},
491513
None,
514+
cfg_attributes,
492515
)
493516
}

godot-codegen/src/generator/default_parameters.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub fn make_function_definition_with_defaults(
1717
sig: &dyn Function,
1818
code: &FnCode,
1919
full_fn_name: &Ident,
20+
cfg_attributes: &TokenStream,
2021
) -> (TokenStream, TokenStream) {
2122
let (default_fn_params, required_fn_params): (Vec<_>, Vec<_>) = sig
2223
.params()
@@ -62,6 +63,7 @@ pub fn make_function_definition_with_defaults(
6263
let builders = quote! {
6364
#[doc = #builder_doc]
6465
#[must_use]
66+
#cfg_attributes
6567
pub struct #builder_ty #builder_lifetime {
6668
// #builder_surround_ref
6769
#( #builder_fields, )*

godot-codegen/src/generator/enums.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use crate::models::domain::{Enum, Enumerator};
1313
use proc_macro2::TokenStream;
1414
use quote::{quote, ToTokens};
1515

16-
pub fn make_enums(enums: &[Enum]) -> TokenStream {
16+
pub fn make_enums(enums: &[Enum], cfg_attributes: &TokenStream) -> TokenStream {
1717
let definitions = enums.iter().map(make_enum_definition);
1818

1919
quote! {
20-
#( #definitions )*
20+
#( #cfg_attributes #definitions )*
2121
}
2222
}
2323

godot-codegen/src/generator/functions_common.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,12 @@ pub fn make_function_definition(
8686
sig: &dyn Function,
8787
code: &FnCode,
8888
safety_doc: Option<TokenStream>,
89+
cfg_attributes: &TokenStream,
8990
) -> FnDefinition {
9091
let has_default_params = default_parameters::function_uses_default_params(sig);
9192
let vis = if has_default_params {
9293
// Public API mapped by separate function.
93-
// Needs to be crate-public because default-arg builder lives outside of the module.
94+
// Needs to be crate-public because default-arg builder lives outside the module.
9495
quote! { pub(crate) }
9596
} else {
9697
make_vis(sig.is_private())
@@ -127,7 +128,12 @@ pub fn make_function_definition(
127128
};
128129

129130
let (default_fn_code, default_structs_code) = if has_default_params {
130-
default_parameters::make_function_definition_with_defaults(sig, code, &primary_fn_name)
131+
default_parameters::make_function_definition_with_defaults(
132+
sig,
133+
code,
134+
&primary_fn_name,
135+
cfg_attributes,
136+
)
131137
} else {
132138
(TokenStream::new(), TokenStream::new())
133139
};

godot-codegen/src/generator/notifications.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub fn make_notify_methods(class_name: &TyName, ctx: &mut Context) -> TokenStrea
6161
pub fn make_notification_enum(
6262
class_name: &TyName,
6363
all_bases: &Vec<TyName>,
64+
cfg_attributes: &TokenStream,
6465
ctx: &mut Context,
6566
) -> (Option<TokenStream>, Ident) {
6667
let Some(all_constants) = ctx.notification_constants(class_name) else {
@@ -98,6 +99,7 @@ pub fn make_notification_enum(
9899
/// notifications defined in base classes.
99100
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
100101
#[repr(i32)]
102+
#cfg_attributes
101103
pub enum #enum_name {
102104
#(
103105
#notification_enumerators_pascal = #notification_enumerators_ord,

godot-codegen/src/generator/utility_functions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub(crate) fn make_utility_function_definition(function: &UtilityFunction) -> To
7979
ptrcall_invocation,
8080
},
8181
None,
82+
&TokenStream::new(),
8283
);
8384

8485
// Utility functions have no builders.

godot-codegen/src/generator/virtual_traits.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,21 @@ pub fn make_virtual_methods_trait(
1919
all_base_names: &[TyName],
2020
trait_name_str: &str,
2121
notification_enum_name: &Ident,
22+
cfg_attributes: &TokenStream,
2223
view: &ApiView,
2324
) -> TokenStream {
2425
let trait_name = ident(trait_name_str);
2526

2627
let virtual_method_fns = make_all_virtual_methods(class, all_base_names, view);
27-
let special_virtual_methods = special_virtual_methods(notification_enum_name);
28+
let special_virtual_methods = make_special_virtual_methods(notification_enum_name);
2829

2930
let trait_doc = docs::make_virtual_trait_doc(trait_name_str, class.name());
3031

3132
quote! {
3233
#[doc = #trait_doc]
3334
#[allow(unused_variables)]
3435
#[allow(clippy::unimplemented)]
36+
#cfg_attributes
3537
pub trait #trait_name: crate::obj::GodotClass + crate::private::You_forgot_the_attribute__godot_api {
3638
#special_virtual_methods
3739
#( #virtual_method_fns )*
@@ -42,7 +44,7 @@ pub fn make_virtual_methods_trait(
4244
// ----------------------------------------------------------------------------------------------------------------------------------------------
4345
// Implementation
4446

45-
fn special_virtual_methods(notification_enum_name: &Ident) -> TokenStream {
47+
fn make_special_virtual_methods(notification_enum_name: &Ident) -> TokenStream {
4648
quote! {
4749
#[doc(hidden)]
4850
fn register_class(builder: &mut crate::builder::ClassBuilder<Self>) {
@@ -125,6 +127,7 @@ fn make_virtual_method(method: &ClassMethod) -> Option<TokenStream> {
125127
ptrcall_invocation: TokenStream::new(),
126128
},
127129
None,
130+
&TokenStream::new(),
128131
);
129132

130133
// Virtual methods have no builders.

godot-codegen/src/models/domain.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ pub struct Class {
160160
pub common: ClassCommons,
161161
pub is_refcounted: bool,
162162
pub is_instantiable: bool,
163+
pub is_experimental: bool,
163164
pub inherits: Option<String>,
164165
pub api_level: ClassCodegenLevel,
165166
pub constants: Vec<ClassConstant>,

godot-codegen/src/models/domain_mapping.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ impl Class {
8787
return None;
8888
}
8989

90+
// Already checked in is_class_deleted(), but code remains more maintainable if those are separate, and it's cheap to validate.
91+
let is_experimental = special_cases::is_class_experimental(&ty_name.godot_ty);
92+
9093
let mod_name = ModName::from_godot(&ty_name.godot_ty);
9194

9295
let constants = option_as_slice(&json.constants)
@@ -117,6 +120,7 @@ impl Class {
117120
},
118121
is_refcounted: json.is_refcounted,
119122
is_instantiable: json.is_instantiable,
123+
is_experimental,
120124
inherits: json.inherits.clone(),
121125
api_level: get_api_level(json),
122126
constants,

0 commit comments

Comments
 (0)