Skip to content

Commit 1193b6f

Browse files
committed
Clean up signal handling code + user-facing symbols
Also disallow visibility markers in #[signal].
1 parent f00b523 commit 1193b6f

File tree

5 files changed

+36
-36
lines changed

5 files changed

+36
-36
lines changed

godot-core/src/private.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,6 @@ pub(crate) fn call_error_remove(in_error: &sys::GDExtensionCallError) -> Option<
120120
call_error
121121
}
122122

123-
// ----------------------------------------------------------------------------------------------------------------------------------------------
124-
// Functional and signal APIs
125-
126-
// pub fn emit_signal<T>(obj: &mut BaseMut<T>, varargs: &[Variant])
127-
// where
128-
// T: GodotClass<Declarer = bounds::DeclEngine> + Inherits<crate::classes::Object>,
129-
// {
130-
// obj.upcast_mut().emit_signal(varargs);
131-
// }
132-
133123
// ----------------------------------------------------------------------------------------------------------------------------------------------
134124
// Plugin and global state handling
135125

godot-macros/src/class/data_models/inherent_impl.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ fn process_godot_fns(
310310
if function.return_ty.is_some() {
311311
return bail!(
312312
&function.return_ty,
313-
"return types in #[signal] are not supported"
313+
"#[signal] does not support return types"
314314
);
315315
}
316316
if function.body.is_some() {
@@ -319,6 +319,12 @@ fn process_godot_fns(
319319
"#[signal] must not have a body; declare the function with a semicolon"
320320
);
321321
}
322+
if function.vis_marker.is_some() {
323+
return bail!(
324+
&function.vis_marker,
325+
"#[signal] must not have a visibility specifier; signals are always public"
326+
);
327+
}
322328

323329
let external_attributes = function.attributes.clone();
324330
let sig = util::reduce_to_signature(function);
@@ -576,7 +582,7 @@ where
576582
// Safe unwrap, since #[signal] must be present if we got to this point.
577583
let mut parser = KvParser::parse(attributes, "signal")?.unwrap();
578584

579-
// Private #[__signal(no_builder)]
585+
// Private #[signal(__no_builder)]
580586
let no_builder = parser.handle_alone("__no_builder")?;
581587

582588
parser.finish()?;

godot-macros/src/class/data_models/signal.rs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl<'a> SignalDetails<'a> {
7777

7878
let param_tuple = quote! { ( #( #param_types, )* ) };
7979
let signal_name = &original_decl.name;
80-
let individual_struct_name = format_ident!("{}_{}", class_name, signal_name);
80+
let individual_struct_name = format_ident!("__godot_Signal_{}_{}", class_name, signal_name);
8181

8282
Ok(Self {
8383
original_decl,
@@ -200,10 +200,11 @@ impl SignalCollection {
200200
} = details;
201201

202202
self.collection_methods.push(quote! {
203+
// Deliberately not #[doc(hidden)] for IDE completion.
203204
#(#signal_cfg_attrs)*
204205
fn #signal_name(self) -> #individual_struct_name<'a> {
205206
#individual_struct_name {
206-
typed: ::godot::register::TypedSignal::new(self.object, #signal_name_str)
207+
typed: ::godot::register::TypedSignal::new(self.__internal_obj, #signal_name_str)
207208
}
208209
}
209210
});
@@ -224,23 +225,29 @@ fn make_signal_individual_struct(details: &SignalDetails) -> TokenStream {
224225
class_name,
225226
param_names,
226227
param_tuple,
227-
// signal_name,
228228
signal_cfg_attrs,
229229
individual_struct_name,
230230
..
231231
} = details;
232232

233-
// let module_name = format_ident!("__godot_signal_{class_name}_{signal_name}");
234-
235-
// Module + re-export.
236-
// Could also keep contained in module to reduce namespace pollution, but might make docs a bit more nested.
233+
// Define the individual types + trait impls. The idea was originally to use a module to reduce namespace pollution:
234+
// let module_name = format_ident!("__godot_signal_{class_name}_{signal_name}");
235+
// #(#signal_cfg_attrs)* pub mod #module_name { use super::*; ... }
236+
// #(#signal_cfg_attrs)* pub(crate) use #module_name::#individual_struct_name;
237+
// However, there are some challenges:
238+
// - Visibility becomes a pain to handle (rustc doesn't like re-exporting private symbols as pub, and we can't know the visibility of the
239+
// surrounding class struct). Having signals always-public is much less of a headache, requires less choice on the user side
240+
// (pub/pub(crate)/nothing on #[signal]), and likely good enough for the moment.
241+
// - Not yet clear if we should have each signal + related types in separate module. If #[signal] is supported in #[godot_api(secondary)]
242+
// impl blocks, then we would have to group them by the impl block. Rust doesn't allow partial modules, so they'd need to have individual
243+
// names as well, possibly explicitly chosen by the user.
244+
//
245+
// For now, #[doc(hidden)] is used in some places to limit namespace pollution at least in IDEs + docs. This also means that the generated
246+
// code is less observable by the user. If someone comes up with a good idea to handle all this, let us know :)
237247
quote! {
238-
// #(#signal_cfg_attrs)*
239-
// mod #module_name {
240-
241-
// TODO make pub without running into "private type `MySignal` in public interface" errors.
242248
#(#signal_cfg_attrs)*
243249
#[allow(non_camel_case_types)]
250+
#[doc(hidden)] // Signal struct is hidden, but the method returning it is not (IDE completion).
244251
struct #individual_struct_name<'a> {
245252
#[doc(hidden)]
246253
typed: ::godot::register::TypedSignal<'a, #class_name, #param_tuple>,
@@ -269,27 +276,26 @@ fn make_signal_individual_struct(details: &SignalDetails) -> TokenStream {
269276
&mut self.typed
270277
}
271278
}
272-
273-
// #(#signal_cfg_attrs)*
274-
// pub(crate) use #module_name::#individual_struct_name;
275279
}
276280
}
277281

278-
// See also make_func_collection().
282+
/// Generates a unspecified-name struct holding methods to access each signal.
279283
fn make_signal_collection(class_name: &Ident, collection: SignalCollection) -> Option<TokenStream> {
280284
if collection.is_empty() {
281285
return None;
282286
}
283287

284-
let collection_struct_name = format_ident!("{}Signals", class_name);
288+
let collection_struct_name = format_ident!("__godot_Signals_{}", class_name);
285289
let collection_struct_methods = &collection.collection_methods;
286290
let individual_structs = collection.individual_structs;
287291

288292
let code = quote! {
289293
#[allow(non_camel_case_types)]
294+
#[doc(hidden)] // Only on struct, not methods, to allow completion in IDEs.
290295
pub struct #collection_struct_name<'a> {
291296
// To allow external call in the future (given Gd<T>, not self), this could be an enum with either BaseMut or &mut Gd<T>/&mut T.
292-
object: ::godot::register::ObjectRef<'a, #class_name>
297+
#[doc(hidden)] // Necessary because it's in the same scope as the user-defined class, so appearing in IDE completion.
298+
__internal_obj: ::godot::register::ObjectRef<'a, #class_name>
293299
}
294300

295301
impl<'a> #collection_struct_name<'a> {
@@ -301,14 +307,14 @@ fn make_signal_collection(class_name: &Ident, collection: SignalCollection) -> O
301307

302308
fn signals(&mut self) -> Self::SignalCollection<'_> {
303309
Self::SignalCollection {
304-
object: ::godot::register::ObjectRef::Internal { obj_mut: self }
310+
__internal_obj: ::godot::register::ObjectRef::Internal { obj_mut: self }
305311
}
306312
}
307313

308314
#[doc(hidden)]
309315
fn __signals_from_external(external: &Gd<Self>) -> Self::SignalCollection<'_> {
310316
Self::SignalCollection {
311-
object: ::godot::register::ObjectRef::External { gd: external.clone() }
317+
__internal_obj: ::godot::register::ObjectRef::External { gd: external.clone() }
312318
}
313319
}
314320
}

godot-macros/src/class/derive_godot_class.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,14 @@ fn make_user_class_impl(
329329

330330
let user_class_impl = quote! {
331331
impl ::godot::obj::UserClass for #class_name {
332+
#[doc(hidden)]
332333
fn __config() -> ::godot::private::ClassConfig {
333334
::godot::private::ClassConfig {
334335
is_tool: #is_tool,
335336
}
336337
}
337338

339+
#[doc(hidden)]
338340
fn __before_ready(&mut self) {
339341
#rpc_registrations
340342
#onready_inits

godot-macros/src/util/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,8 @@ pub fn make_funcs_collection_constant(
360360
None => func_name.to_string(),
361361
};
362362

363-
let doc_comment =
364-
format!("The Rust function `{func_name}` is registered with Godot as `{const_value}`.");
365-
366363
quote! {
367364
#(#attributes)*
368-
#[doc = #doc_comment]
369365
#[doc(hidden)]
370366
#[allow(non_upper_case_globals)]
371367
pub const #const_name: &str = #const_value;
@@ -409,5 +405,5 @@ pub fn format_funcs_collection_constant(_class_name: &Ident, func_name: &Ident)
409405

410406
/// Returns the name of the struct used as collection for all function name constants.
411407
pub fn format_funcs_collection_struct(class_name: &Ident) -> Ident {
412-
format_ident!("__gdext_{class_name}_Funcs")
408+
format_ident!("__godot_{class_name}_Funcs")
413409
}

0 commit comments

Comments
 (0)