openrewrite / openrewrite/rewrite

Go: struct field tags lose information on parse/print

Open
#8,452 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

go parser
Dominant language
Java
Stars
3.7k
Forks
570
Avg merge
13h 12m
Merged PRs (30d)
261

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read rewrite-go/pkg/parser/go_parser.go around mapStructTag and parseStructTagPairs, then inspect rewrite-go/pkg/printer/go_printer.go around the struct-tag printer and the existing StructTag marker. Reproduce the five parse/print cases, then verify round-tripping preserves their literals while annotation edits still affect output.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
compilers, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.