Skip to content

Validate class registrations against ClassDB#1664

Open
Bromeon wants to merge 2 commits into
masterfrom
qol/validate-registrations
Open

Validate class registrations against ClassDB#1664
Bromeon wants to merge 2 commits into
masterfrom
qol/validate-registrations

Conversation

@Bromeon

@Bromeon Bromeon commented Jul 23, 2026

Copy link
Copy Markdown
Member

Godot's classdb_register_extension_class* functions return void and only print to stderr on failure, so a rejected class, method, property, signal or constant is silently missing at runtime. The printed Godot error may vary in detail.

New registry::reg_validation module queries ClassDB before each registration and reports a precise error via godot_error! (and in a potential future builder API would allow programmatic check).

In addition, it exits Godot after collecting all errors. Validation runs only under strict safeguards (default in Debug).

#[var] and #[func] are allowed to collide in Godot, but there aren't many use cases, and an explicit #[var(override)] or so might be better. For now warn (no breaking change), will become hard error in v0.6.

Here's a small manual test.
//! Deliberately broken registrations, to see diagnostics of `godot_core::registry::reg_validation`.
//!
//! Diagnostics only go to stderr (not tested). Enable the `mod` in the parent module, run `check.sh itest` and check stderr.
//! Both classes derive `Node`, since `ClassDB` is not reachable at `Core` init level.

use godot::prelude::*;

/// Error: class name already exists. Separate class, as the failed registration aborts before its symbols.
#[derive(GodotClass)]
#[class(init, base = Node, rename = Camera2D)]
struct DemoClassClash {}

/// Errors: duplicate method, property, signal.
/// Warnings: `duplicate`, `owner` and its accessors shadow `Node`.
#[derive(GodotClass)]
#[class(init, base = Node)]
struct DemoSymbolClashes {
    base: Base<Node>,

    #[var]
    same_prop: i32,

    // Explicit accessors: `rename` also renames generated ones, which would already clash at Rust level.
    #[var(get = get_other, set = set_other, rename = same_prop)]
    other_prop: i32,

    #[var]
    owner: i32,
}

#[godot_api]
impl DemoSymbolClashes {
    #[func]
    fn same_name(&self) {}

    #[func(rename = same_name)]
    fn other_name(&self) {}

    #[func]
    fn get_other(&self) -> i32 {
        self.other_prop
    }

    #[func]
    fn set_other(&mut self, value: i32) {
        self.other_prop = value;
    }

    #[func]
    fn duplicate(&self) {}

    #[signal]
    fn ready();
}

Closes #1024.

@Bromeon Bromeon added quality-of-life No new functionality, but improves ergonomics/internals c: register Register classes, functions and other symbols to GDScript labels Jul 23, 2026
@Bromeon Bromeon added this to the 0.5.x milestone Jul 23, 2026
@GodotRust

Copy link
Copy Markdown

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1664

@Bromeon
Bromeon force-pushed the qol/validate-registrations branch 4 times, most recently from 0da5dd2 to 54d3896 Compare July 24, 2026 17:48
Bromeon added 2 commits July 24, 2026 22:28
Extends the deferred startup-message facility with a `Fatal` level: like
`Error`, but additionally marks the process for termination. Errors from all
init levels are collected first, so one run surfaces every problem.

At the terminal init point (`MainLoop` startup callback, Godot 4.5+), the
process is terminated with exit code 111 -- gracefully via `SceneTree::quit()`
if a scene tree exists, else `process::exit()`. In the editor, the error is
only reported, so the developer can read it and hot-reload a fix.
Godot's `classdb_register_extension_class*` functions return void and only print
to stderr on failure, so a rejected class, method, property, signal or constant
is silently missing at runtime. The printed Godot error may vary in detail, and
some of Godot's checks are compiled out of release builds.

New `registry::reg_validation` module queries `ClassDB` before each registration
and reports a precise error via `defer_startup_fatal!`. Errors are deferred
rather than raised inline, since panicking mid-registration would poison the
registration locks. A future builder API could reuse this for programmatic
success/error feedback.

Active only under strict safeguards (default in Debug) and Godot 4.5+, since
deferred messages are flushed at the `MainLoop` startup callback, which does not
exist before. Up to 4.6, the `ClassDB` singleton appears only after `Core`-level
extension init, so its availability is probed per symbol; 4.7 adds it earlier
and compiles the probe out.

`#[var]` and `#[func]` are allowed to shadow base-class symbols in Godot, but
there aren't many use cases, and an explicit `#[var(override)]` or so might be
better. For now warn (no breaking change), will become hard error in v0.6.
@Bromeon
Bromeon force-pushed the qol/validate-registrations branch from 54d3896 to 46dc050 Compare July 24, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: register Register classes, functions and other symbols to GDScript quality-of-life No new functionality, but improves ergonomics/internals

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Errors from godot registration functions are not detected

2 participants