Skip to content

Reserve keyword function names; carve out host-invoked entry points (E153) - #1194

Merged
aallan merged 1 commit into
mainfrom
fix/1187-keyword-fn-names
Aug 4, 2026
Merged

Reserve keyword function names; carve out host-invoked entry points (E153)#1194
aallan merged 1 commit into
mainfrom
fix/1187-keyword-fn-names

Conversation

@aallan

@aallan aallan commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

A function named after a grammar keyword is now rejected at its declaration site with E153, extending the #1181 gate (contract state forms old/new) to the keyword class Lark's contextual lexer admits as a function name: assert, assume, forall, exists, match, if, let, fn, true, false.

Each of these declares cleanly today — the contextual lexer re-lexes the keyword as an ordinary identifier after fn — and none can ever be called: in expression position the spelling is always the keyword, so a bare match(3) fails to parse ([E005]) and assert(3)/assume(3) are read as the statement forms and collide ([E121] + [E172]/[E173]). Every one is a declarable trap. The reservation refuses the mistake at its source, under the same one-canonical-form rule as E151 (built-in functions), E152 (built-in effects), and #1181.

handle is carved out. public fn handle(@Request -> @Response) is the host-invoked vera serve / wasi:http entry point (spec §9.5.6, examples/http_server.vera) — being uncallable from Vera source does not make it dead code. The reserved set is derived as (state forms | keywords) − host-invoked, three named frozensets with the justification recorded on each, so a future host-invoked entry point joins the carve-out deliberately rather than by silently shrinking the keyword list.

Breaking: a module-qualified mod::match(...) parses through the module-call rule rather than any keyword rule, so a module export under one of these names was callable cross-module (and only cross-module) — probed on the pre-fix tree, the shape checked and ran. Such an export must now be renamed; the breakage is loud and located at the module's declaration. Modules surface the rejection into their importers (the established E151E154 surfacing list).

Mechanism

  • vera/checker/registration.py: _RESERVED_FN_NAMES rebuilt from _STATE_FORM_FN_NAMES | _KEYWORD_FN_NAMES − _HOST_INVOKED_FN_NAMES; _check_reserved_fn_name branches the E153 rationale by which piece matched (a keyword is not described as a "contract state form") while the fix stays "rename" on both. Recurses into where-helpers as before.
  • vera/checker/modules.py: comment updated — the E153 importer-surfacing rationale now covers both halves.
  • New conformance negative ch05_reserved_keyword_fn_rejected (expected_error: E153; conformance 176, was 175) and the hand-list updates in CLAUDE/AGENTS/TESTING.
  • 38 new tests in tests/test_checker_modules.py (104 total): each of the ten keywords at top level, where-helper, and module-surfaced positions; the handle carve-out at all three positions; probe records in docstrings for why the set is exactly this.

Verification

  • Full floor: 8,712 passed + 102 skipped + 26 stress deselected = 8,840; mypy clean.
  • All 176 conformance programs hold; 42 examples pass check + verify; 224 corpus programs canonical.
  • Doc-counts oracle green (8,840 tests / 136 files / 176 conformance / 42 examples); site assets regenerated and coherent.
  • Live probes on this branch: fn matchE153 at the declaration; fn handle standalone → clean; a module exporting fn letE153 surfaced into the importer, located at the module's own file/line.

Closes #1187

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Function declarations using reserved contract-state identifiers or grammar keywords are now rejected with clear E153 diagnostics.
    • Validation covers imported, private, generic and helper functions while preserving valid names such as older and matched.
    • The host-invoked handle entry point remains permitted.
  • Documentation

    • Updated language guidance, FAQs, changelog and project metrics.
    • Conformance coverage increased to 176 programmes, with updated testing statistics.

…E153)

Lark's contextual lexer re-lexes `assert`, `assume`, `forall`, `exists`,
`match`, `if`, `let`, `fn`, `true` and `false` as ordinary identifiers after
`fn`, so each declares cleanly — and none can be written in expression
position, where the spelling is always the keyword: a bare `match(3)` does not
parse at all ([E005]), and `assert(3)` / `assume(3)` are read as the statement
forms and collide ([E121] plus [E172]/[E173]). Every one is a declarable trap,
so E153 now refuses it at the declaration, extending the #1181 gate for the
contract state forms under the same one-canonical-form rule as E151 and E152.

`handle` is carved out: `public fn handle(@request -> @response)` is the
host-invoked `vera serve` / `wasi:http` entry point (spec §9.5.6), so being
uncallable from Vera source does not make it dead code. The reserved set is
derived as (state forms) | (keywords) - (host-invoked), each piece named and
commented, so a future host-invoked entry point joins the carve-out
deliberately.

Breaking: a module-qualified `mod::match(...)` parses through the module-call
rule rather than any keyword rule, so a module export under one of these names
was callable cross-module (and only cross-module) — probed on the pre-fix
tree, the shape checked and ran. Such an export must now be renamed; the
breakage is loud and located at the module's declaration.

The E153 rationale branches with the reason (a keyword is not described as a
contract state form) while the fix stays "rename" on both. New conformance
program ch05_reserved_keyword_fn_rejected (176, was 175).

Co-Authored-By: Claude <noreply@anthropic.invalid>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The checker now rejects reserved grammar-keyword function names with E153, while allowing handle for host invocation. Tests cover declarations, helpers, modules, diagnostics, and parsing. The specification, conformance manifest, and project metrics now document the expanded rule and 176-program suite.

Changes

Reserved function name validation

Layer / File(s) Summary
Reservation contract and name sets
spec/05-functions.md, vera/checker/registration.py
The specification and checker reserve old, new, and selected grammar keywords. handle remains permitted, and prefixed identifiers remain valid.
E153 diagnostic flow
vera/checker/registration.py, vera/checker/modules.py
Function registration validates reserved names and emits separate E153 explanations for state-form names and grammar keywords.
Validation and conformance updates
tests/test_checker_modules.py, tests/conformance/manifest.json, CHANGELOG.md, SKILL.md, TESTING.md, AGENTS.md, CLAUDE.md, README.md, FAQ.md, ROADMAP.md, vera/README.md
Tests cover visibility, helpers, modules, qualified calls, exact matching, diagnostics, effect operations, and handle. Documentation records the new conformance fixture and updated suite metrics.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FunctionDeclaration
  participant _register_all
  participant _check_reserved_fn_name
  participant E153Diagnostic
  FunctionDeclaration->>_register_all: register function declaration
  _register_all->>_check_reserved_fn_name: validate function name
  _check_reserved_fn_name->>E153Diagnostic: emit category-specific E153
Loading

Possibly related PRs

  • aallan/vera#817: Both changes add checker diagnostics for forbidden function names.
  • aallan/vera#1188: This change extends the same E153 validation from old and new to grammar keywords.
  • aallan/vera#1191: Both changes update reserved-name validation and module diagnostics.

Suggested labels: compiler, tests, spec, docs

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes rejecting keyword-named functions and preserving host-invoked entry points.
Linked Issues check ✅ Passed The changes reject the ten affected keyword names with E153 and explicitly preserve the host-invoked handle entry point.
Out of Scope Changes check ✅ Passed The code, tests, conformance data, and documentation changes support the linked issue and stated implementation objectives.
Docstring Coverage ✅ Passed Docstring coverage is 93.75% which is sufficient. The required threshold is 80.00%.
Changelog Covers Public-Surface Changes ✅ Passed CHANGELOG.md explicitly describes the E153 keyword-name change, handle exception, module surfacing, exact-name scope, and links it to Spec §5.2; it covers the public diagnostic and specification...
Spec And Implementation Move Together ✅ Passed spec/05-functions.md lists the same 10 keyword names plus old/new; registration computes that set minus handle, with matching where and module E153 handling.
Diagnostics Carry An Error Code ✅ Passed The changed declaration diagnostic emits registered E153, matching [EW]\d{3}; no new or changed warning diagnostic was found.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1187-keyword-fn-names

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.79%. Comparing base (ea628bd) to head (2f1dc4a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1194   +/-   ##
=======================================
  Coverage   93.79%   93.79%           
=======================================
  Files          99       99           
  Lines       34126    34134    +8     
  Branches      458      458           
=======================================
+ Hits        32008    32017    +9     
+ Misses       2105     2104    -1     
  Partials       13       13           
Flag Coverage Δ
javascript 78.61% <ø> (ø)
python 95.48% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aallan

aallan commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

Adversarial review record

Verdict: implementation matches the #1187 decision exactly; no defects found. Checks performed on this head (2f1dc4a):

Set membership traced to the recorded probe. The issue's 33-candidate probe found eleven declarable-but-uncallable names; the decision was reject-ten-plus-carve-out-handle. _KEYWORD_FN_NAMES contains exactly those eleven, _HOST_INVOKED_FN_NAMES subtracts handle, and the twenty names the probe found callable (resume, with, then, else, …) are correctly absent — no over-reservation. vera/grammar.lark has zero diff since the probe was taken, so the completeness claim still holds on this tree.

Three doors probed live on this branch:

Decision-trail cross-check. The issue's second maintainer comment (module-qualified route) is honoured: the whole identifier is reserved and the CHANGELOG carries the Breaking note for the mod::keyword(...) route. The third comment's Vera-prefix escalation was handled separately as E154 (ch08_reserved_vera_prefix_rejected, already on main) — nothing dangling for this PR.

Spec §5.2 states both groups, the whole-identifier rule (older/renew/matched remain legal), and scopes the exception precisely ("A future host-invoked entry point is exempted on the same grounds; nothing else is"). The E153 registry line ("Function name is reserved by the grammar") already spans both classes; correctly left untouched.

Rebase reconciliation (fixed in this head, worth flagging for the record): the branch as originally prepared carried stale documentation numbers — conformance totals left at 175 across ~13 sites, and a test_checker_modules.py row of 96 tests / 1,573 lines where the collected reality is 104 / 1,653. All counts in this head are recomputed from a live run (8,712 passed + 102 skipped + 26 stress = 8,840; doc-counts oracle and site-assets gate both green).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
vera/checker/registration.py (1)

330-374: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the E153 reachability rationale.

Both branches state that the declaration cannot be reached “from anywhere in the program”. Module-qualified calls could reach exported old and keyword-named functions before this reservation. State that unqualified calls cannot reach the declaration, and that E153 deliberately closes the previously half-usable module-qualified route.

Proposed correction
-                    f"resolves to a function, so this declaration could not "
-                    f"be reached from anywhere in the program — it is dead "
-                    f"code the compiler would otherwise accept in silence."
+                    f"resolves to a function. An unqualified call therefore "
+                    f"cannot reach this declaration. Module-qualified calls "
+                    f"could reach an exported declaration, so Vera reserves "
+                    f"the name rather than leaving it half-usable."
...
-                    f"never resolves to a function. This declaration could "
-                    f"not be reached from anywhere in the program — it is "
-                    f"dead code the compiler would otherwise accept in "
-                    f"silence. Vera provides exactly one way to express each "
+                    f"never resolves to a function. An unqualified call "
+                    f"therefore cannot reach this declaration. "
+                    f"Module-qualified calls could reach an exported "
+                    f"declaration, so Vera reserves the name rather than "
+                    f"leaving it half-usable. Vera provides exactly one way "
+                    f"to express each "
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@vera/checker/registration.py` around lines 330 - 374, Update the E153
rationale and fix text in both branches of the registration logic to state that
unqualified calls cannot reach the declaration, rather than claiming it is
unreachable from anywhere in the program. Explicitly note that module-qualified
calls could previously reach exported reserved-name functions and that E153
closes this formerly half-usable route, while preserving the existing rename
guidance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 36-38: Correct the conformance-program count in the changelog
entry for ch05_reserved_keyword_fn_rejected from “(175, was 174)” to “(176, was
175)”, without changing the separate ch08_reserved_vera_prefix_rejected count.

In `@tests/test_checker_modules.py`:
- Around line 1249-1253: Update the test around KEYWORDS to compare
set(KEYWORDS) against _KEYWORD_FN_NAMES - _HOST_INVOKED_FN_NAMES, so
checker-reserved keywords require corresponding E153 coverage while preserving
the intentional omission of handle.

In `@vera/README.md`:
- Line 83: Update the README table entry for registration.py to remove the
leading spaces from inside the inline code span, keeping any intended
indentation outside the span so markdownlint MD038 passes.

---

Outside diff comments:
In `@vera/checker/registration.py`:
- Around line 330-374: Update the E153 rationale and fix text in both branches
of the registration logic to state that unqualified calls cannot reach the
declaration, rather than claiming it is unreachable from anywhere in the
program. Explicitly note that module-qualified calls could previously reach
exported reserved-name functions and that E153 closes this formerly half-usable
route, while preserving the existing rename guidance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: effc42b1-9fd9-401d-ad9e-a31fdd036b8b

📥 Commits

Reviewing files that changed from the base of the PR and between ea628bd and 2f1dc4a.

⛔ Files ignored due to path filters (6)
  • docs/SKILL.md is excluded by !docs/**
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • tests/conformance/ch05_reserved_keyword_fn_rejected.vera is excluded by !**/*.vera
📒 Files selected for processing (14)
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • FAQ.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • spec/05-functions.md
  • tests/conformance/manifest.json
  • tests/test_checker_modules.py
  • vera/README.md
  • vera/checker/modules.py
  • vera/checker/registration.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • aallan/vera-bench (manual)

Comment thread CHANGELOG.md
Comment thread tests/test_checker_modules.py
Comment thread vera/README.md
@aallan
aallan merged commit 65a94bd into main Aug 4, 2026
29 checks passed
@aallan
aallan deleted the fix/1187-keyword-fn-names branch August 4, 2026 00:49
@aallan

aallan commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai On the outside-diff finding at vera/checker/registration.py (the E153 rationale in both branches claiming the declaration "could not be reached from anywhere in the program"): agreed — that overclaimed, since a module-qualified mod::old(...) / mod::match(...) on an exported function could previously reach it, which is exactly the half-usable route the reservation closes and what the CHANGELOG and spec §5.2 already said. Fixed on the open release branch (PR #1195, commit 9d5fa1a): both branches now state that no unqualified call site can reach the declaration, and that the one route that previously could — a module-qualified call on an exported function — is deliberately closed by the reservation. The rename fix text is preserved unchanged, and the diagnostic-fields gate stays green.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@aallan Acknowledged. The revised rationale is accurate.

It now distinguishes unreachable unqualified calls from the former reachable mod::old(...) and mod::match(...) route. It also states that E153 deliberately closes that module-qualified route. Keeping the rename fix unchanged is appropriate.


✏️ Learnings added
Learnt from: aallan
URL: https://github.com/aallan/vera/pull/1194

Timestamp: 2026-08-04T06:54:48.565Z
Learning: In `vera/checker/registration.py`, E153 diagnostics for reserved function names must state that no unqualified call site can reach the declaration. Before E153 reserves the name, an exported function could be reached through a module-qualified call such as `mod::old(...)` or `mod::match(...)`; E153 deliberately closes that route.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@aallan aallan mentioned this pull request Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decide: keyword-named functions beyond old/new — eleven are declarable but uncallable, yet handle is legitimate

1 participant