Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions _xtool/internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
44 changes: 4 additions & 40 deletions _xtool/internal/parser/testdata/comment/expect.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
],
Expand Down Expand Up @@ -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"
}
],
Expand Down Expand Up @@ -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"
}
],
Expand Down Expand Up @@ -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"
}
],
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand Down
119 changes: 119 additions & 0 deletions reproduce_comment_bug.go
Original file line number Diff line number Diff line change
@@ -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")
}
Loading