diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index 86bdc60b3..4b0f4c316 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -193,11 +193,28 @@ func (ct *Converter) ParseCommentGroup(cursor clang.Cursor) (comentGroup *ast.Co } func (ct *Converter) ParseComment(rawComment string) *ast.CommentGroup { - lines := strings.Split(rawComment, "\n") commentGroup := &ast.CommentGroup{} - for _, line := range lines { - commentGroup.List = append(commentGroup.List, &ast.Comment{Text: line + "\n"}) + + // Block comments (/* ... */ or /** ... */) should be kept as a single Comment node. + // Go's ast.Comment documentation states: "A Comment node represents a single //-style or /*-style comment." + // This means the entire block comment must be ONE ast.Comment node, not split across multiple nodes. + if strings.HasPrefix(rawComment, "/*") { + // Ensure the comment ends with newline for proper formatting + text := rawComment + if !strings.HasSuffix(text, "\n") { + text += "\n" + } + commentGroup.List = append(commentGroup.List, &ast.Comment{Text: text}) + } else { + // Line comments (// or ///) - each line is a separate Comment node + lines := strings.Split(rawComment, "\n") + for _, line := range lines { + if line != "" { + commentGroup.List = append(commentGroup.List, &ast.Comment{Text: line + "\n"}) + } + } } + return commentGroup } diff --git a/_xtool/internal/parser/testdata/comment/expect.json b/_xtool/internal/parser/testdata/comment/expect.json index f30194afd..8e2ebe712 100755 --- a/_xtool/internal/parser/testdata/comment/expect.json +++ b/_xtool/internal/parser/testdata/comment/expect.json @@ -353,19 +353,7 @@ "Doc": { "List": [ { - "Text": "/**\n", - "_Type": "Comment" - }, - { - "Text": " * doc 1\n", - "_Type": "Comment" - }, - { - "Text": " * doc 2\n", - "_Type": "Comment" - }, - { - "Text": " */\n", + "Text": "/**\n * doc 1\n * doc 2\n */\n", "_Type": "Comment" } ], @@ -524,15 +512,7 @@ "Doc": { "List": [ { - "Text": "/**\n", - "_Type": "Comment" - }, - { - "Text": " * static field doc\n", - "_Type": "Comment" - }, - { - "Text": " */\n", + "Text": "/**\n * static field doc\n */\n", "_Type": "Comment" } ], @@ -584,15 +564,7 @@ "Doc": { "List": [ { - "Text": "/**\n", - "_Type": "Comment" - }, - { - "Text": " * field doc\n", - "_Type": "Comment" - }, - { - "Text": " */\n", + "Text": "/**\n * field doc\n */\n", "_Type": "Comment" } ], @@ -672,15 +644,7 @@ "Doc": { "List": [ { - "Text": "/**\n", - "_Type": "Comment" - }, - { - "Text": " * method doc\n", - "_Type": "Comment" - }, - { - "Text": " */\n", + "Text": "/**\n * method doc\n */\n", "_Type": "Comment" } ], diff --git a/go.mod b/go.mod index 390004898..cbd71bad3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/goplus/llcppg go 1.23.0 require ( - github.com/goplus/gogen v1.19.7 + github.com/goplus/gogen v1.20.2 github.com/goplus/lib v0.3.1 github.com/goplus/llgo v0.12.0 github.com/goplus/mod v0.19.0 diff --git a/go.sum b/go.sum index 0f00402a2..f481f0012 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/goplus/gogen v1.19.7 h1:0i30on0GwtYIJ+D9/I5QujswBU+mnnmNewoRk/uRVkw= -github.com/goplus/gogen v1.19.7/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI= +github.com/goplus/gogen v1.20.2 h1:c9wm7NzjWSrncbtH+lz4jM2j31p+6JTji8cjF1K79qg= +github.com/goplus/gogen v1.20.2/go.mod h1:87ZJD1mdljXx2pkvBrMGynGUz6m08brj9p6xhb5aq2Y= github.com/goplus/lib v0.3.1 h1:Xws4DBVvgOMu58awqB972wtvTacDbk3nqcbHjdx9KSg= github.com/goplus/lib v0.3.1/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= github.com/goplus/llgo v0.12.0 h1:UhbmwR+9fSy1y944rp6fPkSP39n4YhH4TpAN2QJ15ns= diff --git a/reproduce_comment_bug.go b/reproduce_comment_bug.go new file mode 100644 index 000000000..d7776e188 --- /dev/null +++ b/reproduce_comment_bug.go @@ -0,0 +1,119 @@ +// Minimal reproduction for issue #618: Multi-line block comment bug with gogen +// +// This demonstrates how incorrectly structured ast.Comment nodes cause +// invalid Go code generation with gogen v1.20.2. +// +// Run: go run reproduce_comment_bug.go + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/token" + "go/types" + "strings" + + "github.com/goplus/gogen" +) + +// This creates BUGGY comments - splits block comment across multiple nodes +func createBuggyComments() *ast.CommentGroup { + // This is what llcppg's buggy ParseComment() does: + // It splits multi-line block comments by newline + return &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "/* Create an iterator for traversing a domain\n"}, + {Text: " The domain NULL denotes the default domain */\n"}, + }, + } +} + +// This creates CORRECT comments - keeps block comment as single node +func createCorrectComments() *ast.CommentGroup { + // This is what should be done: + // Keep the entire block comment as a single ast.Comment node + return &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "/* Create an iterator for traversing a domain\n The domain NULL denotes the default domain */\n"}, + }, + } +} + +func generateCode(comments *ast.CommentGroup, label string) { + fmt.Println(label) + fmt.Println(strings.Repeat("=", len(label))) + + pkg := gogen.NewPackage("", "test", nil) + + // Create a type declaration with the comment + tyDecl := pkg.NewTypeDefs() + spec := tyDecl.NewType("ExampleType") + spec.InitType(pkg, types.Typ[types.Int]) + + // Attach the comment as documentation + if comments != nil { + spec.SetComments(pkg, comments) + } + + // Get the generated AST and try to format it + file := pkg.ASTFile() + + var buf bytes.Buffer + fset := token.NewFileSet() + err := format.Node(&buf, fset, file) + if err != nil { + fmt.Printf("❌ ERROR: %v\n\n", err) + fmt.Println("This demonstrates the bug with gogen v1.20.2!") + } else { + fmt.Println("✅ Generated Go code:") + fmt.Println(buf.String()) + } + fmt.Println() +} + +func main() { + fmt.Println("=== Gogen Comment Bug Reproduction (Issue #618) ===\n") + + fmt.Println("This reproduces the bug WITHOUT involving llcppg - pure gogen usage.\n") + + // Test 1: Buggy comment structure (what llcppg currently generates) + fmt.Println("Test 1: BUGGY comment structure") + fmt.Println("--------------------------------") + fmt.Println("Comment split into 2 nodes:") + buggy := createBuggyComments() + for i, c := range buggy.List { + fmt.Printf(" [%d]: %q\n", i, c.Text) + } + fmt.Println() + + generateCode(buggy, "Generated Go code (will have malformed comments):") + + // Test 2: Correct comment structure + fmt.Println("\nTest 2: CORRECT comment structure") + fmt.Println("----------------------------------") + fmt.Println("Comment as single node:") + correct := createCorrectComments() + for i, c := range correct.List { + fmt.Printf(" [%d]: %q\n", i, c.Text) + } + fmt.Println() + + generateCode(correct, "Generated Go code (properly formatted comments):") + + fmt.Println("=== Explanation ===") + fmt.Println("Go's ast.Comment documentation states each Comment represents") + fmt.Println("'a single //-style or /*-style comment' - emphasis on SINGLE.") + fmt.Println() + fmt.Println("Block comments /* ... */ must be ONE ast.Comment node, not split") + fmt.Println("across multiple nodes. When gogen v1.20.2 prints malformed comment") + fmt.Println("structures, it generates invalid Go syntax.") + fmt.Println() + fmt.Println("This is the root cause of test failures in:") + fmt.Println(" - TestFromTestdata/gettext") + fmt.Println(" - TestFromTestdata/gpgerror") + fmt.Println(" - TestFromTestdata/issue507") + fmt.Println(" - TestFromTestdata/keepcomment") +}