Skip to content

Void as a member or wrapper element passes validation and generates void x{}; / std::optional<void> #172

Description

@matt-edmondson

What happens

ValidateParameter refuses a top-level Void parameter, but nothing refuses Void as a member type or inside a wrapper. ValidateType's WrapperType arm simply recurses, and its default: arm swallows both Void and None:

// Schema/Models/Schema.Validation.cs:723-729
// Every wrapper resolves to whatever it wraps, so a class named inside a Span,
// Handle, Result or Optional is checked exactly as one named directly is.
case WrapperType wrapper:
    ValidateType(issues, wrapper.ElementType, path, element);
    break;

default:
    break;

Member-level checks (Schema.Validation.cs:102-112) warn only on member.Type is NoneVoid is not mentioned. CppTypeMapper then maps it unconditionally (Schema.Cpp/CppTypeMapper.cs:48): SchemaTypes.Void => new TypeReference("void").

Failure scenario

member type schema.Validate() generated C++
Void clean void x{};
Optional<Void> clean std::optional<void> x{};
Span<Void> clean std::span<void> x{};
Optional<None> clean CppGenerationException

All four C++ outputs are ill-formed — std::optional<void> and std::span<void> are explicitly not instantiable.

Why it matters

CLAUDE.md states the invariant as:

Schema.Validation.cs enforces the corollaries — no Array, Result or Void parameters, no None anywhere generatable.

Neither half holds below the top level.

Optional<None> at least fails loudly at generation time. Void fails silently, and the broken header only surfaces at the consumer's compiler — pointing at generated code rather than at the schema that produced it.

A Void member is also a plausible editing state rather than a contrived one: the type picker offers Void for return types, and the same picker serves members.

Suggested fix

Report Void at any position other than a function return type (and Result's element), and report None wherever ValidateType descends rather than only at the top of a member or parameter.

The WrapperType arm is the single place both can be caught on the way down, so the fix is one check rather than one per call site.

Activity

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

Metadata

Metadata

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