What version of OpenRewrite are you using?
Current main (27baa806).
What is the smallest, simplest way to reproduce the problem?
Parse and print any Go file containing a struct field tag that isn't in gofmt's canonical key:"value" shape. Each of these fails to round-trip:
| Input field |
Printed back as |
Name string "json:\"name\"" (interpreted-string tag) |
Name string — tag gone |
Name string `notakeyvalue` |
Name string — tag gone |
Name string `` (empty tag) |
Name string — tag gone |
Name string `json:"name" garbage` |
Name string `json:"name"` — trailing text gone |
ID int64 ` json:"id" ` |
ID int64 `json:"id"` — inner padding gone |
All five are accepted by the Go compiler, and the first one is read by reflect.StructTag exactly like the backtick form.
What did you expect to see?
Parse/print is lossless for every tag a Go compiler accepts.
What did you see instead?
Silent source corruption: any recipe that edits a file containing such a tag rewrites or deletes the tag as a side effect, in a hunk the recipe never intended to touch.
What is the full stack trace of any errors you encountered?
No error — that's the problem, it's silent.
Root cause
mapStructTag (rewrite-go/pkg/parser/go_parser.go:2985) consumes the literal with ctx.skip(len(tag.Value)) and then returns early when parseStructTagPairs yields no pairs, so nothing at all survives in the LST. It also strips the wrapping quotes by hand rather than unquoting, so an interpreted-string tag's escapes are never resolved.
parseStructTagPairs (rewrite-go/pkg/parser/go_parser.go:3158) breaks at the first unparseable pair without keeping the remainder.
- The printer (
rewrite-go/pkg/printer/go_printer.go:488) rebuilds the tag purely from LeadingAnnotations, always in backtick form with zero inner padding, so anything the annotations can't express is lost.
org.openrewrite.golang.marker.StructTag already exists for exactly this purpose (and already implements RpcCodec) but is currently unreferenced.
Fix
Under way — the parser attaches the verbatim literal via the existing StructTag marker whenever LeadingAnnotations can't faithfully reproduce it, and the printer prefers that literal but only while it still agrees with the annotations, so recipes that edit annotations still take effect. PR to follow.
Follow-up
Once this is released, the parse/print validation suppressions added downstream in the Go recipe repository to work around this corruption can be removed, and the affected recipes re-validated without them.
What version of OpenRewrite are you using?
Current
main(27baa806).What is the smallest, simplest way to reproduce the problem?
Parse and print any Go file containing a struct field tag that isn't in gofmt's canonical
key:"value"shape. Each of these fails to round-trip:Name string "json:\"name\""(interpreted-string tag)Name string— tag goneName string `notakeyvalue`Name string— tag goneName string`` (empty tag)Name string— tag goneName string `json:"name" garbage`Name string `json:"name"`— trailing text goneID int64 ` json:"id" `ID int64 `json:"id"`— inner padding goneAll five are accepted by the Go compiler, and the first one is read by
reflect.StructTagexactly like the backtick form.What did you expect to see?
Parse/print is lossless for every tag a Go compiler accepts.
What did you see instead?
Silent source corruption: any recipe that edits a file containing such a tag rewrites or deletes the tag as a side effect, in a hunk the recipe never intended to touch.
What is the full stack trace of any errors you encountered?
No error — that's the problem, it's silent.
Root cause
mapStructTag(rewrite-go/pkg/parser/go_parser.go:2985) consumes the literal withctx.skip(len(tag.Value))and then returns early whenparseStructTagPairsyields no pairs, so nothing at all survives in the LST. It also strips the wrapping quotes by hand rather than unquoting, so an interpreted-string tag's escapes are never resolved.parseStructTagPairs(rewrite-go/pkg/parser/go_parser.go:3158) breaks at the first unparseable pair without keeping the remainder.rewrite-go/pkg/printer/go_printer.go:488) rebuilds the tag purely fromLeadingAnnotations, always in backtick form with zero inner padding, so anything the annotations can't express is lost.org.openrewrite.golang.marker.StructTagalready exists for exactly this purpose (and already implementsRpcCodec) but is currently unreferenced.Fix
Under way — the parser attaches the verbatim literal via the existing
StructTagmarker wheneverLeadingAnnotationscan't faithfully reproduce it, and the printer prefers that literal but only while it still agrees with the annotations, so recipes that edit annotations still take effect. PR to follow.Follow-up
Once this is released, the parse/print validation suppressions added downstream in the Go recipe repository to work around this corruption can be removed, and the affected recipes re-validated without them.