Skip to content

AstSchema refuses a MemberInitialiser in ConstructionExpression.Arguments, which the AST, serializer and all four generators support #52

Description

@matt-edmondson

What happens

The Arguments slot is declared as AstSlotKind.Expression:

// Coder.Graph/AstSchema.cs:41
private static readonly AstSlot ArgumentsSlot = new(ArgumentsSlotName, AstSlotCardinality.Many, AstSlotKind.Expression);

and Accepts resolves that kind through IsExpression, which does not admit MemberInitialiser:

// Coder.Graph/AstSchema.cs:430
AstSlotKind.Expression => IsExpression(candidate),

// Coder.Graph/AstSchema.cs:449-454
public static bool IsExpression(AstNode node) =>
    node is Expression
        or AstLeafNode<string>
        or AstLeafNode<int>
        or AstLeafNode<bool>
        or AstLeafNode<double>;

MemberInitialiser derives from AstNode, not Expression (Coder/Ast/MemberInitialiser.cs:17).

Attachment is gated on that check:

// Coder.Graph/AstSchema.cs:133
if (!Accepts(slot, child))
{
    return false;
}

which makes the handling two cases further down unreachable for a MemberInitialiser:

// Coder.Graph/AstSchema.cs:175
case (ConstructionExpression construction, ArgumentsSlotName):
    construction.Arguments.Add(child);
    return true;

Note that this case deliberately carries no when child is Expression guard, unlike all five neighbouring cases — and ConstructionExpression.Arguments is typed Collection<AstNode> rather than Collection<Expression> (Coder/Ast/ConstructionExpression.cs:38). Both say the intent was to accept it.

Failure scenario

AstSchema.TryAttachAt(
    constructionExpr,
    ArgumentsSlot,
    0,
    new MemberInitialiser("x") { Value = Literal.Number(1) });

returns false and attaches nothing. In the editor, AstGraph.TryConnect refuses the link (Coder.Graph/AstGraph.cs:561), so a designated or named initialiser can never be built in the graph — and one loaded from YAML cannot be detached and reattached.

Why it matters

All four generators implement this node in exactly this position — CSharpGenerator.cs:252,343, CppGenerator.cs:544,567, PythonGenerator.cs:126,137, JavaScriptGenerator.cs:125,172 — and YamlSerializer/YamlDeserializer round-trip it. CLAUDE.md states it as a design rule: "a MemberInitialiser among its arguments is an element that names the member it is for — a designated initialiser in C++, an object initialiser in C#, a keyword argument in Python, an object literal in JavaScript."

The graph is documented as the uniform view of the AST. Here it cannot express a shape that the AST, the serializer and every generator all handle.

Missing coverage

Coder.Test/Graph/DeclarationSlotsTests.cs:126 covers MemberInitialiser's own Value slot only. Nothing tests attaching one into a ConstructionExpression.

Suggested fix

Either give construction arguments a dedicated slot kind accepting IsExpression(candidate) || candidate is MemberInitialiser, or widen the Expression arm for this slot specifically. Add a DeclarationSlotsTests case attaching a MemberInitialiser to ConstructionExpression.Arguments.

Related: #48 covers MemberInitialiser and ConstructionExpression having no editable fields; this is the separate problem of the node not being attachable at all.

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions