Skip to content

bug(codegen): reject or implement silently dropped catch/finally and named action sections #355

Description

@tinovyatkin

Summary

antlr4-rust-gen accepts several authored target-code surfaces but silently
omits them from generated Rust:

  • parser-rule catch [...] { ... };
  • parser-rule finally { ... };
  • grammar/scoped @header { ... };
  • grammar/scoped @...::definitions { ... }.

This happens in both --actions embedded and --actions templates.
--require-full-semantics still exits successfully, and semantics.json
contains no entry explaining that the authored code was discarded.

The direct grammar frontend already retains these constructs:

The production render path, however, only materializes rule @init/@after
and grammar @members:

This violates the fail-loud semantic contract established by #9 and #35:
authored target code must either execute, be routed through a documented hook,
or fail generation with a source-positioned diagnostic.

Reproduction

finally

grammar Finally;

@parser::members {
    fn mark_finally(&mut self) {}
}

start: A EOF;
finally {
    self.mark_finally();
}

A: 'a';

catch

grammar Catch;

@parser::members {
    fn mark_catch(&mut self) {}
}

start: A EOF;
catch [error] {
    let _ = error;
    self.mark_catch();
}

A: 'a';

named file/module sections

grammar Header;

@header {
    const HEADER_SENTINEL: i32 = 7;
}

@parser::definitions {
    fn definition_sentinel() -> i32 { HEADER_SENTINEL }
}

start: A EOF;
A: 'a';

Run each with:

antlr4-rust-gen T.g4 \
  --actions embedded \
  --require-generated-parser \
  --require-full-semantics \
  --out-dir generated

Observed on main@5caa7d3e:

  • exit status is zero;
  • the @members method is present;
  • the catch, finally, @header, and @definitions bodies are absent;
  • semantics.json reports no affected coordinate or source section.

The same omission occurs with --actions templates.

Goal

Make every authored target-code surface accountable.

The first correctness step is to reject unsupported source sections with a
precise diagnostic. Implementing a section may then replace that diagnostic,
but silent omission must not remain an accepted disposition.

Inventory

  • Inventory grammar-level/scoped named actions and rule exception groups after
    import resolution and grammar transforms.
  • Give each source-owned section a deterministic disposition such as
    embedded, translated, hooked, synthetic, or unsupported.
  • Extend semantics.json, or a companion deterministic manifest section, so
    strict semantic auditing covers constructs which do not have ATN
    action/predicate coordinates.
  • Make --require-full-semantics reject every unsupported authored section.

Embedded Rust support

  • Emit supported @header sections at a stable module position before imports
    or other generated declarations.
  • Emit supported @definitions sections at a documented module/impl position
    matching their scope.
  • Define duplicate/scoped-section behavior for combined, parser, lexer, and
    imported grammars.
  • Translate token aliases and other generated names using the same structural
    machinery as @members, rather than textual special cases.

Rule exception lifecycle

  • Preserve ANTLR ordering between the rule body, @after, catch, finally,
    default recovery, context finalization, and listener events.
  • @after must remain success-only.
  • finally must execute exactly once on every path for which ANTLR executes it,
    including recoverable and propagated failures.
  • A grammar catch clause must not accidentally run during speculative
    prediction or generated-path retries.
  • Ordinary and left-recursive rules must share one lifecycle contract.

If catch/finally support requires a new generated-rule macro section or runtime
helper, update the generated-code API revision and compatibility surfaces
required by AGENTS.md.

Acceptance criteria

  • No authored named action, catch clause, or finally clause can disappear
    without an explicit disposition.
  • Unsupported source sections fail generation with source path, line,
    column, section kind, and remediation guidance.
  • --require-full-semantics rejects unsupported non-ATN target-code
    sections.
  • Supported embedded @header and @definitions bodies appear exactly
    once at documented generated positions.
  • Supported catch/finally bodies follow ANTLR success, recovery, propagated
    error, and ordering semantics.
  • Focused tests cover combined/split/imported grammars, ordinary and
    left-recursive rules, success, recoverable error, fatal error, and
    duplicate scoped sections.
  • Manifest snapshots make every source-owned target-code section
    observable.
  • The full runtime testsuite and exact CI clippy command pass.

Related

Non-goals

  • Translating arbitrary Java, C++, JavaScript, or other target-language bodies
    into Rust.
  • Encoding grammar-specific exception types or rule names in generic runtime or
    codegen paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions