Skip to content

Add an exhaustiveness test over AST node types so a new node cannot silently miss AstSchema or AstFields #49

Description

@matt-edmondson

The gap

CLAUDE.md states the rule plainly — "Coder.Graph/AstSchema.cs — the uniform view of the AST's parent/child structure, hand-written rather than reflective. Adding a node type means adding it here." — but nothing enforces it. AstSchema and AstFields are both large hand-written switches ending in a silent default (AstSchema.cs's slot table, AstFields.cs:298 _ => [], AstFields.cs:443 _ => false), so a node type omitted from either produces no error, no warning and no test failure. It produces a node that draws an empty inspector.

There is no reflection-driven test anywhere in Coder.Test — nothing in the suite calls Assembly, GetTypes(), IsSubclassOf or IsAssignableFrom.

This is not hypothetical: MemberInitialiser and ConstructionExpression are missing from AstFields today (filed separately). They were added to the AST, AstSchema, the YAML serializer and all four generators in the same run, and only the inspector was missed — exactly the failure mode a hand-written switch with a silent default invites.

Proposal

Add a test class in Coder.Test/Graph/ that reflects over every concrete AstNode subclass in the ktsu.Coder assembly and asserts, for each:

  1. AstSchema.SlotsFor returns a decision for it — either a real slot list or a deliberate, explicitly-listed empty one, so "no children" is stated rather than defaulted.
  2. AstFields.Read returns at least one field for any node type that declares a public settable scalar property (string, bool, an enum, or a TypeReference).
  3. Every field name AstFields.Read returns is accepted by AstFields.TryWrite on the same node — a read/write symmetry check that would also catch a field added to one side only.

Node types that genuinely have nothing editable (if any) opt out through an explicit allow-list in the test, so the exemption is a line of code someone had to write rather than an omission nobody noticed.

Rationale

The hand-written schema is a deliberate design choice and worth keeping — it is what lets AstGraph walk any node without a switch over its type, and reflection over the AST at runtime would trade that clarity away. But the cost of that choice is that correctness depends on a human remembering four files, and the AST is under active growth: the 2026-09-10 work alone added SourceFile, NamespaceDeclaration, EnumDeclaration, EnumMember, FieldDeclaration, CompileTimeAssertion, UsingAlias, MemberInitialiser, ConstructionExpression and TypeReference.

A reflective test keeps the production code hand-written while making the omission loud. It costs one test file and turns a silent editor gap into a build failure that names the missing type.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions