Skip to content

C#: a member or local named field inside a property accessor binds to C# 14's field keyword, so the getter silently returns the synthesized backing field (0) instead of the member, and a local named field fails with CS9273 #461

Open

Description

@matt-edmondson

What's wrong

CSharpGenerator.EscapeIdentifier only escapes reserved keywords, and explicitly leaves contextual keywords alone on the grounds that they "are legal names everywhere else" (CSharpGenerator.cs L213-L240, from #441). field is the exception: since C# 14 (the default for net10.0), field inside a property accessor is the keyword for a synthesized backing field. So a reference to a member named field from a getter or setter body changes meaning without an error.

Repro (generated with CSharpGenerator, then built and run with the .NET 10 SDK)

The AST is a class Box with private int field = 42;, plus a property Field whose getter body is return field; and whose setter body is field = value;. There is also a property Twice whose getter declares a local int field = 2; return field;. The generator writes all of these verbatim:

public class Box
{
    private int field = 42;
    public int Field
    {
        get { return field; }
        set { field = value; }
    }
    public int Twice
    {
        get { int field = 2; return field; }
    }
}
  • new Box().Field prints 0, not 42. The compiler gives only warning CS9258 ("the 'field' keyword binds to a synthesized backing field ... use 'this.field' or '@field' instead"). The setter likewise writes to the hidden backing field, and the declared member is never read (CS0414).
  • Twice fails to compile: error CS9273 "'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead."

Every other target reads the declared member, so the C# output computes a different value from the same AST. The test suite would not catch this, because it is a warning and not a build failure.

Suggested fix / acceptance criteria

  • Write an identifier named field as @field in C# at both the declaration and the reference. The simplest version does this everywhere, since @field is the same identifier wherever it appears. A narrower version does it only inside property accessor bodies. Either way, the escape goes through the existing EscapeIdentifier/EscapeReference hook so declarations and uses agree.
  • Update the remark on ReservedWords so it no longer says every contextual keyword is safe.
  • Add a test in CSharpGeneratedSourceCompilesTests covering the class above. It should build with no CS9258/CS9273, and running it should show the getter returning the member's value (42) and the setter updating that member.

Related: #106 (reserved-word escaping in general) and #441 (C# escaping, which deliberately excluded contextual keywords).

Activity

  1. matt-edmondson commented on Nov 6, 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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions