From 28e566816047f0c3d088d9e2e94f06e54bf44616 Mon Sep 17 00:00:00 2001 From: xgopilot Date: Thu, 12 Feb 2026 08:41:52 +0000 Subject: [PATCH 1/3] convert: lazily create ConstGroup in createEnumItems to avoid empty const nodes Use lazy initialization for ConstGroup in createEnumItems: only create p.NewConstGroup() when an enum item is actually emitted. This avoids unnecessary empty const declarations/nodes when all enum items are skipped due to name conflicts or filtering. Closes #643 Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com> --- cl/internal/convert/package.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cl/internal/convert/package.go b/cl/internal/convert/package.go index cb5234f0..ebcc6311 100644 --- a/cl/internal/convert/package.go +++ b/cl/internal/convert/package.go @@ -597,7 +597,13 @@ func (p *Package) createEnumType(goName string, enumName *ast.Ident, pnc nc.Node } func (p *Package) createEnumItems(pnc nc.NodeConverter, decl *ast.EnumTypeDecl, enumType types.Type) error { - defs := p.NewConstGroup() + var defs *ConstGroup + ensureDefs := func() *ConstGroup { + if defs == nil { + defs = p.NewConstGroup() + } + return defs + } for _, item := range decl.Type.Items { goName, err := pnc.ConvEnumItem(decl, item) if err != nil { @@ -623,7 +629,7 @@ func (p *Package) createEnumItems(pnc nc.NodeConverter, decl *ast.EnumTypeDecl, if err != nil { return fmt.Errorf("createEnumItems:fail to convert %T to int: %w", item.Value, err) } - defs.New(val, enumType, name) + ensureDefs().New(val, enumType, name) } return nil } From 3dfc85881514881643bbd3984d9634fbf7f19776 Mon Sep 17 00:00:00 2001 From: xgopilot Date: Thu, 12 Feb 2026 09:02:51 +0000 Subject: [PATCH 2/3] convert: add unit test for lazy ConstGroup when all enum items are skipped Adds TestNoEmptyConstGroupWhenAllEnumItemsSkipped to verify that no empty const() block is generated when all enum items are already registered and thus skipped in createEnumItems. Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com> --- cl/internal/convert/package_bulitin_test.go | 56 +++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/cl/internal/convert/package_bulitin_test.go b/cl/internal/convert/package_bulitin_test.go index 8df21d41..4fcb18a5 100644 --- a/cl/internal/convert/package_bulitin_test.go +++ b/cl/internal/convert/package_bulitin_test.go @@ -1,8 +1,10 @@ package convert import ( + "bytes" "go/token" "go/types" + "strings" "testing" "github.com/goplus/gogen" @@ -197,3 +199,57 @@ func TestProcessSymbol(t *testing.T) { } } } + +func TestNoEmptyConstGroupWhenAllEnumItemsSkipped(t *testing.T) { + pnc := cltest.NC(&llcppg.Config{}, nil, cltest.NewConvSym()) + pkg := emptyPkg(pnc) + tempFile := &ncimpl.HeaderFile{ + File: "temp.h", + FileType: llcppg.Inter, + } + pkg.p.SetCurFile(tempFile.ToGoFileName("testpkg"), true) + + items := []*ast.EnumItem{ + {Name: &ast.Ident{Name: "Red"}, Value: &ast.BasicLit{Kind: ast.IntLit, Value: "0"}}, + {Name: &ast.Ident{Name: "Green"}, Value: &ast.BasicLit{Kind: ast.IntLit, Value: "1"}}, + } + + // First enum: registers the items normally + err := pkg.NewEnumTypeDecl("Color", &ast.EnumTypeDecl{ + Object: ast.Object{ + Loc: &ast.Location{File: "temp.h"}, + Name: &ast.Ident{Name: "Color"}, + }, + Type: &ast.EnumType{Items: items}, + }, pnc) + if err != nil { + t.Fatal(err) + } + + // Second enum: all items already registered, so all are skipped. + // With lazy ConstGroup, no empty const() should be created. + err = pkg.NewEnumTypeDecl("Color2", &ast.EnumTypeDecl{ + Object: ast.Object{ + Loc: &ast.Location{File: "temp.h"}, + Name: &ast.Ident{Name: "Color2"}, + }, + Type: &ast.EnumType{Items: items}, + }, pnc) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + err = pkg.p.WriteTo(&buf, "temp.go") + if err != nil { + t.Fatal(err) + } + output := buf.String() + + // There should be exactly one const block from the first enum. + // No empty const() from the second enum. + constCount := strings.Count(output, "const (") + if constCount != 1 { + t.Errorf("expected exactly 1 const block, got %d.\nOutput:\n%s", constCount, output) + } +} From 845eb48e4576c79801f98e9b96ef02cc368aaec3 Mon Sep 17 00:00:00 2001 From: xgopilot Date: Thu, 12 Feb 2026 09:10:53 +0000 Subject: [PATCH 3/3] convert: add AST-level check for empty const GenDecl in unit test Enhance TestNoEmptyConstGroupWhenAllEnumItemsSkipped to also verify at the Go AST level that no const GenDecl with empty Specs exists in the package's corresponding ast.File, covering the behavior that lazy ConstGroup initialization avoids empty const nodes in the AST. Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com> --- cl/internal/convert/package_bulitin_test.go | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cl/internal/convert/package_bulitin_test.go b/cl/internal/convert/package_bulitin_test.go index 4fcb18a5..eb7f7408 100644 --- a/cl/internal/convert/package_bulitin_test.go +++ b/cl/internal/convert/package_bulitin_test.go @@ -2,6 +2,7 @@ package convert import ( "bytes" + goast "go/ast" "go/token" "go/types" "strings" @@ -252,4 +253,25 @@ func TestNoEmptyConstGroupWhenAllEnumItemsSkipped(t *testing.T) { if constCount != 1 { t.Errorf("expected exactly 1 const block, got %d.\nOutput:\n%s", constCount, output) } + + // Also verify at the AST level: no const GenDecl with empty Specs + // should exist in the package's corresponding ast.File. + goFile := pkg.p.ASTFile("temp.go") + if goFile == nil { + t.Fatal("expected ast.File for temp.go, got nil") + } + constDeclCount := 0 + for _, decl := range goFile.Decls { + genDecl, ok := decl.(*goast.GenDecl) + if !ok || genDecl.Tok != token.CONST { + continue + } + constDeclCount++ + if len(genDecl.Specs) == 0 { + t.Error("found empty const declaration in AST (no Specs)") + } + } + if constDeclCount != 1 { + t.Errorf("expected exactly 1 const GenDecl in AST, got %d", constDeclCount) + } }