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.
Context
While investigating aws/aws-cdk#38452 (the
aws-s3/aws-kmsREADME example implementingIResourceWithPolicyV2as 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):
C#: dropped
this.produces a self-assignmentThe 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:
New (keep
this.only when the member's rendered name is shadowed by a parameter of an enclosing function; all other output unchanged):Go: constructor call/definition naming mismatch
constructorDeclarationrendersnew/Newaccording to the class's exportedness, butnewExpression(the call site) hardcodesNew— the code even carries a comment asking// Should this be "new" if the class is unexported?. Result: the definition saysfunc newMyResourcewhile the call site invokes the non-existentNewMyResource.Before:
New:
Caveat handled in the fix: corpus/test snippets declare unexported
/// fake-from-jsiiclasses that stand in for library types — those must keep the exportedNewVpc(...)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:
this.env, a*string;*name, astring.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:this.env = 'production'this.env = "production"*string = stringnext = other(both parameters)*next = otherstring = *stringNew — both flags key off whether the assignment target renders as a pointer, which only a property does:
this.env = 'production'this.env = jsii.String("production")this.env = envthis.env = envname = 'fallback'*name = "fallback"next = other*next = *otherPython 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.