Skip to content

Hold a C# namespace's fields and functions in a static class [patch] - #226

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/155-csharp-namespace-members
Oct 14, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/155-csharp-namespace-members

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #155

What was wrong

CSharpGenerator.GenerateNamespace wrote every member straight into the namespace, so the issue's repro came out as:

namespace Geometry
{
    public static readonly int Origins = 2;

    public int twice(int n) { ... }
}

C# has no member outside a type, so this fails with CS0116. The same members directly in a SourceFile fail with CS0106.

Change

  • A namespace's FieldDeclaration, FunctionDeclaration and VariableDeclaration members are gathered into a public static class, placed where the first of them was declared:
    namespace Geometry
    {
        public static class Geometry
        {
            public static readonly int Origins = 2;
    
            public static int twice(int n) { ... }
        }
    
        public class Point { }
    }
  • Name: the namespace's last segment, or Globals at file scope. If a type, enum or alias beside it already has that name, the class gets a Globals suffix to avoid CS0101.
  • static: each held member is written static (CS0708). A held const variable is left as plain const, because static const is CS0504. Locals inside a held function are untouched: the generator tracks the member being held by reference, not with a flag, so nothing in its body is affected.
  • Types, enums, aliases and nested namespaces stay where they are.
  • The file-scope member loop moves out of LanguageGeneratorBase.GenerateSourceFile into an overridable GenerateFileMembers, so C# can do the same at file scope. Other generators behave the same as before.

Tests

  • CSharpScopeMemberTests (6 tests): the namespace holder and its ordering, locals staying locals, const without static, the name-collision fallback, file-scope members, and no holder when a namespace holds only types.
  • New CSharpGeneratedSourceCompilesTests, matching the C/C++/Rust/Go ones. It writes the generated file next to a driver that uses every declaration from outside, runs dotnet build, and is inconclusive when no dotnet is on the path.
  • With the generator change reverted, 5 of the 7 new tests fail, including the compile test with the issue's CS0106. The two that still pass are guards. With the change, all pass.
  • Full Coder.Test suite: 1207 passed, 0 failed. dotnet format --verify-no-changes is clean on the touched files.

Not covered

The compile test builds the issue's shapes, not all of CompiledExemplar. Compiling the whole exemplar as C# still hits #127 (abstract readonly on Shape, fixed separately in #225) and other gaps tracked in #162. ExemplarReflectionTableTests uses C++ type names such as holo::reflect::FieldInfo, so it can't be compiled as C# however its members are placed.

This PR and #225 both edit CSharpGenerator's function-modifier code, so whichever merges second will need a small merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_018FP1DMG4WYMABYwTfyb2Xd


Generated by Claude Code

C# has no member outside a type, but CSharpGenerator wrote a namespace's
fields, functions and variables straight into the namespace (CS0116),
and a file's straight into the file (CS0106). Every other target has
somewhere to put them, so the AST allows them.

They are now gathered into a public static class, written where the
first of them was declared and named after the namespace's last
segment, or Globals at file scope. A type of the same name beside it
gets the Globals suffix instead. Each held member is written static,
except a const, which already is; locals in a held function's body are
untouched. Types, enums, aliases and nested namespaces stay put.

The file-scope loop moves from LanguageGeneratorBase.GenerateSourceFile
into an overridable GenerateFileMembers so C# can do the same there.
CSharpGeneratedSourceCompilesTests builds the output with dotnet build,
like the C, C++, Rust and Go compile tests.

Fixes #155

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FP1DMG4WYMABYwTfyb2Xd
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 84c1ce8 into main Oct 14, 2026
15 checks passed
@matt-edmondson
matt-edmondson deleted the fix/155-csharp-namespace-members branch October 14, 2026 04:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants