Skip to content

-Werror=switch is in CFLAGS but defeated by a default: arm in 37 of 38 ASTType switches — the enforcement is bought and then opted out of #738

Description

@InauguralPhysicist

Found in a modularity review of main @ 8e8970a. This is the cheapest structural fix available in the codebase: the mechanism is already paid for and simply disabled.

The measurement

Makefile:9 sets -Werror=switch. Coverage of switch (node->type) over ASTType (33 members, eigenscript.h:257-268):

File ASTType switches with default: actually exhaustive
src/compiler.c 11 11 0
src/lint.c 22 22 0
src/eigenlsp.c 2 2 0
src/parser.c 2 1 1

1 of 38. Adding an ASTType today produces zero diagnostics — the node is silently skipped by the compiler, the linter, and the LSP. free_ast (parser.c:196) names all 33 cases but still carries a default:, so a missed case there becomes a silent leak rather than a build error.

The opcode tables are worse: op_has_u16, op_verify_operands, and op_stack_effect all take uint8_t op rather than OpCode, so -Wswitch cannot apply at all. That is the mechanical reason those tables have already drifted (filed separately).

Related: the ValType build-error premise is not true

CLAUDE.md states that #709 made a new ValType a build error. I could not find that mechanism. val_type_name (eigenscript.c:212-225) ends in default: return "?";, so an 11th ValType compiles clean and prints "?". CHANGELOG.md:124 describes #709 as "json_raw values are measured instead of reported as 0" — an observer-measurement fix, not an exhaustiveness gate. A new ValType must currently reach ~14 unenforced type-switches plus gc_value_is_node, GC_FOR_EACH_CHILD, gc_clear_node, and the destructor.

Worth correcting the CLAUDE.md claim either way, since it is the kind of statement a contributor relies on rather than re-verifies.

Suggested fix

Delete the default: arm from every switch over a closed enum (ASTType, ValType, OpCode) and type the opcode-table parameters as OpCode. Where a genuine catch-all is needed at runtime, put the fallback after the switch rather than inside it, so the compiler still checks exhaustiveness:

switch (t) {
case AST_X: ... return r;
/* no default */
}
return fallback;   /* unreachable for valid enum values */

This is a mechanical change with an immediate payoff: it converts an entire class of "new node/opcode/type silently skipped" from a review responsibility into a compile error, which is the difference between the tables that stayed correct (SANDBOX_ALLOW, gated by a test — 148 entries, zero drift) and the ones that didn't (eigenlsp.c's builtin_docs, ungated — 179 of 235 builtins absent plus a phantom entry).

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