Skip to content

C#: fields and functions in a NamespaceDeclaration are written directly into the namespace, so the output fails with CS0116 (CS0106 at file scope) #155

Description

@matt-edmondson

What's wrong

CSharpGenerator.GenerateNamespace writes every member of a namespace directly into the braced namespace: CSharpGenerator.cs#L319-L336.

Two generators then write ordinary members there:

C# has no namespace-scope fields or methods. Every other target has somewhere to put these declarations:

  • C++: inline constexpr and free functions
  • C, Rust and Go: module items
  • Python and JavaScript: module scope

Reproduction

var ns = new NamespaceDeclaration("Geometry");
ns.Members.Add(new FieldDeclaration { Name = "Origins", Type = "int", IsConstant = true, InitialValue = new LiteralExpression<int>(2) });
var f = new FunctionDeclaration("twice") { ReturnType = "int" };
f.Parameters.Add(new Parameter("n", "int"));
f.Body.Add(new ReturnStatement(new BinaryExpression(new VariableReference("n"), BinaryOperator.Multiply, new LiteralExpression<int>(2))));
ns.Members.Add(f);

Output:

namespace Geometry
{
    public static readonly int Origins = 2;

    public int twice(int n)
    {
        return (n * 2);
    }
}

Building this gives error CS0116: A namespace cannot directly contain members such as fields, methods or statements. Putting the same members directly in a SourceFile, with no namespace, gives error CS0106: The modifier 'public' is not valid for this item instead.

The repo's own reflection-table exemplar has this shape. ExemplarReflectionTableTests.CSharp_WritesAnObjectInitialiserAndAStaticReadonlyField asserts static readonly for a FieldDeclaration inside NamespaceDeclaration("holo::components::reflection"), so the C# output that test pins does not compile (L62, L147-L153). C# has no compile test, unlike C, C++, Rust and Go, which is why this went unnoticed.

Suggested fix

  • Collect a namespace's (and a file's) non-type members into a generated public static class, and mark them static. Non-type members are FieldDeclaration, FunctionDeclaration and VariableDeclaration.
  • Name the class after the namespace's last segment, or use a fixed name such as Globals. Any naming convention works as long as the output compiles.
  • Leave types, enums, aliases and nested namespaces where they are.

Acceptance criteria

  • C# output for a namespace containing a constant table and a free function compiles.
  • Add a CSharpGeneratedSourceCompilesTests, matching the C/C++/Rust/Go ones, that builds the compiled exemplar with Roslyn or dotnet build.

Activity

  1. matt-edmondson commented on Oct 5, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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