Skip to content

Container events are half-wired: handlers can never be registered or run #685

Description

@logbie

Summary

While auditing Docs/04-advanced-features/containers-oop.md against the implementation (branch claude/containers-md-wiring-cleanup-8k19ys, which wired up interface contracts), I found that the container event system is only half-wired. Events parse and trigger executes, but the handler side is unreachable end to end — so event/trigger are effectively no-ops that only validate the event exists. Events are not covered by the containers doc, so this was left out of that change as a follow-up.

What works today

  • event on_click inside a container body parses (parse_event_definition_full), including needs parameters, and is stored in ContainerDefinitionValue.events.
  • trigger on_click inside a container action parses and executes: method calls inject the definition's events into the method environment, and the EventTrigger arm runs all registered handlers.
  • TestPrograms/containers_comprehensive.wfl exercises exactly this much — its triggers succeed because running zero handlers is a no-op, so the test never proves a handler fires.

What is broken

  1. The on <source> <event> handler statement cannot be written. parse_event_handler (src/parser/stmt/containers.rs) calls parse_expression() for the event source, which consumes both the source and the event name as one natural-language expression, then fails with Expected identifier for event name, found Eol:

    create container Button:
        property label: Text
        event on_click
        action click:
            display "clicking"
            trigger on_click
        end
    end
    
    create new Button as btn:
        label is "Submit"
    end
    
    on btn on_click
        display "handler ran"
    
    btn.click()
    
    error[ERROR]: Expected identifier for event name, found Eol
    
  2. Even if it parsed, the handler body is always dropped. parse_event_handler is a stub that returns handler_body: Vec::new() — the statements under the handler are never attached to it.

  3. Statement-level trigger never parses arguments (parse_event_trigger stub returns arguments: Vec::new()), so an event declared with needs parameters can never receive values; runtime binds parameters to Null.

  4. Handler registration and triggering don't share state. The runtime EventHandler arm clones the event, appends the handler, and defines the result as a plain environment binding under the event name (src/interpreter/mod.rs, Statement::EventHandler). But trigger inside a method looks up events freshly injected from ContainerDefinitionValue.events — whose handlers list is always empty (Rc<ContainerEventValue> is immutable, handlers: Vec::new() at definition). So even with 1–3 fixed, a registered handler would never be seen by a trigger inside a method.

Expected

Either events work end to end — define, register a handler with a body, trigger with arguments, handler runs with bound parameters — or the surface is removed/documented as unimplemented. Half-parsed syntax that silently does nothing is the worst of both (a program can trigger all day and look successful).

Suggested direction

  • Decide the handler grammar (e.g. on <instance> <event>: ... end on) and parse the body properly.
  • Store handlers somewhere shared and mutable (e.g. RefCell on the event value in the container definition, or per-instance), so registration and triggering agree.
  • Parse trigger arguments and bind event parameters.
  • Add red-first tests that assert a handler actually runs (current tests only prove triggers don't crash), including the needs-parameter path.
  • Update docs (containers doc or a dedicated events page) only once behavior is real, per the docs-honesty policy.

Code pointers

  • src/parser/stmt/containers.rsparse_event_handler, parse_event_trigger, parse_event_definition stubs ("For now, just create a simple ...").
  • src/interpreter/mod.rsStatement::EventTrigger, Statement::EventHandler arms; event injection in the MethodCall path.
  • src/interpreter/value.rsContainerEventValue / EventHandler.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions