diff --git a/experimental/ast/printer/bufformat_test.go b/experimental/ast/printer/bufformat_test.go index f21f74f1..447c371d 100644 --- a/experimental/ast/printer/bufformat_test.go +++ b/experimental/ast/printer/bufformat_test.go @@ -66,15 +66,6 @@ func TestBufFormat(t *testing.T) { t.Run(relPath, func(t *testing.T) { t.Parallel() - // editions/2024 is a parser error test corpus, not a - // printer test corpus: the proto files contain - // deliberately invalid edition syntax for the parser to - // diagnose. There is no formatted output to compare - // against. - if strings.Contains(relPath, "editions/2024") { - t.Skip("editions/2024 is a parser error test") - } - // deprecate/* requires adding `deprecated` options to // existing declarations as an AST transform. The legacy // formatter does this in buf's FormatModuleSet wrapper, diff --git a/experimental/ast/printer/decl.go b/experimental/ast/printer/decl.go index ab030a04..eda4d10f 100644 --- a/experimental/ast/printer/decl.go +++ b/experimental/ast/printer/decl.go @@ -91,6 +91,18 @@ func (p *printer) printImport(decl ast.DeclImport, gap gapStyle) { p.printToken(decl.Semicolon(), p.semiGap()) } +// printDefPrefixes emits modifiers (optional/required/repeated/export/local) +// that live on the underlying DeclDef's type chain. Def projections expose +// only the bare keyword (`message`, `group`, etc.), so the prefixes have to +// be drained from DeclDef before that keyword is printed. +func (p *printer) printDefPrefixes(d ast.DeclDef, gap gapStyle) gapStyle { + for prefix := range d.Prefixes() { + p.printToken(prefix.PrefixToken(), gap) + gap = gapSpace + } + return gap +} + func (p *printer) printDef(decl ast.DeclDef, gap gapStyle) { switch decl.Classify() { case ast.DefKindOption: @@ -153,42 +165,45 @@ func (p *printer) printOption(opt ast.DefOption, gap gapStyle) { } func (p *printer) printMessage(msg ast.DefMessage, gap gapStyle) { + gap = p.printDefPrefixes(msg.Decl, gap) p.printToken(msg.Keyword, gap) p.printPath(msg.Decl.Name(), gapSpace) p.printBody(msg.Body) } func (p *printer) printEnum(e ast.DefEnum, gap gapStyle) { + gap = p.printDefPrefixes(e.Decl, gap) p.printToken(e.Keyword, gap) p.printPath(e.Decl.Name(), gapSpace) p.printBody(e.Body) } func (p *printer) printService(svc ast.DefService, gap gapStyle) { + gap = p.printDefPrefixes(svc.Decl, gap) p.printToken(svc.Keyword, gap) p.printPath(svc.Decl.Name(), gapSpace) p.printBody(svc.Body) } func (p *printer) printExtend(ext ast.DefExtend, gap gapStyle) { + gap = p.printDefPrefixes(ext.Decl, gap) p.printToken(ext.Keyword, gap) p.printPath(ext.Extendee, gapSpace) p.printBody(ext.Body) } func (p *printer) printOneof(o ast.DefOneof, gap gapStyle) { + gap = p.printDefPrefixes(o.Decl, gap) p.printToken(o.Keyword, gap) p.printPath(o.Decl.Name(), gapSpace) p.printBody(o.Body) } func (p *printer) printGroup(g ast.DefGroup, gap gapStyle) { - // Print type prefixes (optional/required/repeated) from the underlying - // DeclDef, since DefGroup.Keyword is the "group" keyword itself. - for prefix := range g.Decl.Prefixes() { - p.printToken(prefix.PrefixToken(), gap) - gap = gapSpace - } + // DefGroup.Keyword is the "group" keyword itself, so the modifiers + // (optional/required/repeated) sit on the underlying DeclDef's type + // chain rather than on the projection. + gap = p.printDefPrefixes(g.Decl, gap) p.printToken(g.Keyword, gap) p.printPath(g.Decl.Name(), gapSpace)