Skip to content

C# and Go visitors emit non-compiling class renderings (self-assignment on shadowed fields; constructor naming mismatch; pointer mismatches in assignments) #3772

Description

@omarqureshi

Context

While investigating aws/aws-cdk#38452 (the aws-s3/aws-kms README example implementing IResourceWithPolicyV2 as an object literal, which translates into broken code in every non-TS language), rewriting the example as a named class — the fix confirmed there, see aws/aws-cdk#38461 — exposed renderer bugs in the C# and Go visitors that apply to any translated class, not just that README. Filing here as the rosetta-side follow-up suggested in that issue's triage.

Minimal repro (a snippet-local class whose constructor parameter shares a name with a private field):

class MyResource {
  public readonly env: string;
  private readonly resource: string;

  constructor(resource: string) {
    this.resource = resource;
    this.env = 'production';
  }

  public description(): string {
    return this.resource;
  }
}

const r = new MyResource('bucket');

C#: dropped this. produces a self-assignment

The C# visitor suppresses this. unconditionally. Public members are PascalCased so they can never collide with a camelCase parameter, but private fields keep their camelCase name — and then the constructor assignment degrades into a self-assignment (compiler warning CS1717; the field is never set).

Before:

public MyResource(string resource)
{
    resource = resource;        // CS1717 — field never assigned
    Env = "production";
}

New (keep this. only when the member's rendered name is shadowed by a parameter of an enclosing function; all other output unchanged):

public MyResource(string resource)
{
    this.resource = resource;
    Env = "production";
}

Go: constructor call/definition naming mismatch

constructorDeclaration renders new/New according to the class's exportedness, but newExpression (the call site) hardcodes New — the code even carries a comment asking // Should this be "new" if the class is unexported?. Result: the definition says func newMyResource while the call site invokes the non-existent NewMyResource.

Before:

r := NewMyResource(jsii.String("bucket"))   // NewMyResource does not exist

New:

r := newMyResource(jsii.String("bucket"))

Caveat handled in the fix: corpus/test snippets declare unexported /// fake-from-jsii classes that stand in for library types — those must keep the exported NewVpc(...) call form, since only the call site renders. The fix distinguishes genuinely snippet-local classes (unexported and not jsii-resolvable) from classes modeling library types.

Go: assignments put the wrong side of the pointer on the right

Parameters and properties are both pointer-valued in Go, but they do not render alike:

  • a property renders as the pointer field itself — this.env, a *string;
  • a parameter renders dereferenced — *name, a string.

So the two need opposite right-hand sides: a property target wants a pointer (leave a pointer alone, wrap a literal), a parameter target wants a value (dereference a pointer, leave a literal raw). The visitor treats them as one case — it suppresses dereferencing on the RHS for both (isPtrAssignmentRValue) and wraps literals for neither — so each is broken in one direction:

TypeScript Before Why it fails
this.env = 'production' this.env = "production" *string = string
next = other (both parameters) *next = other string = *string

New — both flags key off whether the assignment target renders as a pointer, which only a property does:

TypeScript New
this.env = 'production' this.env = jsii.String("production")
this.env = env this.env = env
name = 'fallback' *name = "fallback"
next = other *next = *other

Python and Java render all of the above correctly today.

Fixes

All three, developed test-first with translations-corpus snippets pinning all four languages (so the diff proves no change alters another target's output), are up as PR #3773. Visitor VERSIONs are bumped to invalidate cached translations.

Metadata

Metadata

Assignees

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