diff --git a/_xtool/internal/parser/parser.go b/_xtool/internal/parser/parser.go index 86bdc60b3..44b66a162 100644 --- a/_xtool/internal/parser/parser.go +++ b/_xtool/internal/parser/parser.go @@ -177,26 +177,34 @@ func createLoc(cursor clang.Cursor) *ast.Location { // // Note: In cases where both documentation comments and line comments conceptually exist, // only the line comment will be preserved. -func (ct *Converter) ParseCommentGroup(cursor clang.Cursor) (comentGroup *ast.CommentGroup, isDoc bool) { +func (ct *Converter) ParseCommentGroup(cursor clang.Cursor) (*ast.CommentGroup, bool) { rawComment := toStr(cursor.RawCommentText()) - commentGroup := &ast.CommentGroup{} - if rawComment != "" { - commentRange := cursor.CommentRange() - cursorRange := cursor.Extent() - isDoc := getOffset(commentRange.RangeStart()) < getOffset(cursorRange.RangeStart()) - commentGroup = ct.ParseComment(rawComment) - if len(commentGroup.List) > 0 { - return commentGroup, isDoc - } + if rawComment == "" { + return nil, false + } + commentRange := cursor.CommentRange() + cursorRange := cursor.Extent() + isDoc := getOffset(commentRange.RangeStart()) < getOffset(cursorRange.RangeStart()) + commentGroup := ct.ParseComment(rawComment) + if len(commentGroup.List) > 0 { + return commentGroup, isDoc } return nil, false } 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"}) + if strings.HasPrefix(rawComment, "/*") { + // Block comments (/* ... */) are kept as a single ast.Comment node. + text := strings.TrimRight(rawComment, "\n") + commentGroup.List = []*ast.Comment{{Text: text}} + } else { + // Line comments (// ...) are split into one node per line. + for _, line := range strings.Split(rawComment, "\n") { + if line != "" { + commentGroup.List = append(commentGroup.List, &ast.Comment{Text: line}) + } + } } return commentGroup } diff --git a/_xtool/internal/parser/testdata/comment/expect.json b/_xtool/internal/parser/testdata/comment/expect.json index f30194afd..f057585e7 100755 --- a/_xtool/internal/parser/testdata/comment/expect.json +++ b/_xtool/internal/parser/testdata/comment/expect.json @@ -5,7 +5,7 @@ "Doc": { "List": [ { - "Text": "// not read doc 1\n", + "Text": "// not read doc 1", "_Type": "Comment" } ], @@ -47,7 +47,7 @@ "Doc": { "List": [ { - "Text": "/* not read doc 2 */\n", + "Text": "/* not read doc 2 */", "_Type": "Comment" } ], @@ -89,7 +89,7 @@ "Doc": { "List": [ { - "Text": "/// doc\n", + "Text": "/// doc", "_Type": "Comment" } ], @@ -131,7 +131,7 @@ "Doc": { "List": [ { - "Text": "/** doc */\n", + "Text": "/** doc */", "_Type": "Comment" } ], @@ -173,7 +173,7 @@ "Doc": { "List": [ { - "Text": "/*! doc */\n", + "Text": "/*! doc */", "_Type": "Comment" } ], @@ -215,11 +215,11 @@ "Doc": { "List": [ { - "Text": "/// doc 1\n", + "Text": "/// doc 1", "_Type": "Comment" }, { - "Text": "/// doc 2\n", + "Text": "/// doc 2", "_Type": "Comment" } ], @@ -261,11 +261,7 @@ "Doc": { "List": [ { - "Text": "/*! doc 1 */\n", - "_Type": "Comment" - }, - { - "Text": "/*! doc 2 */\n", + "Text": "/*! doc 1 */\n/*! doc 2 */", "_Type": "Comment" } ], @@ -307,11 +303,7 @@ "Doc": { "List": [ { - "Text": "/** doc 1 */\n", - "_Type": "Comment" - }, - { - "Text": "/** doc 1 */\n", + "Text": "/** doc 1 */\n/** doc 1 */", "_Type": "Comment" } ], @@ -353,19 +345,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 */", "_Type": "Comment" } ], @@ -423,7 +403,7 @@ "Doc": { "List": [ { - "Text": "/// doc\n", + "Text": "/// doc", "_Type": "Comment" } ], @@ -448,7 +428,7 @@ "Comment": { "List": [ { - "Text": "///\u003c comment\n", + "Text": "///\u003c comment", "_Type": "Comment" } ], @@ -474,7 +454,7 @@ "Comment": { "List": [ { - "Text": "/*!\u003c comment */\n", + "Text": "/*!\u003c comment */", "_Type": "Comment" } ], @@ -524,15 +504,7 @@ "Doc": { "List": [ { - "Text": "/**\n", - "_Type": "Comment" - }, - { - "Text": " * static field doc\n", - "_Type": "Comment" - }, - { - "Text": " */\n", + "Text": "/**\n * static field doc\n */", "_Type": "Comment" } ], @@ -557,7 +529,7 @@ "Comment": { "List": [ { - "Text": "/*!\u003c static field comment */\n", + "Text": "/*!\u003c static field comment */", "_Type": "Comment" } ], @@ -584,15 +556,7 @@ "Doc": { "List": [ { - "Text": "/**\n", - "_Type": "Comment" - }, - { - "Text": " * field doc\n", - "_Type": "Comment" - }, - { - "Text": " */\n", + "Text": "/**\n * field doc\n */", "_Type": "Comment" } ], @@ -617,7 +581,7 @@ "Comment": { "List": [ { - "Text": "///\u003c field comment\n", + "Text": "///\u003c field comment", "_Type": "Comment" } ], @@ -643,7 +607,7 @@ "Comment": { "List": [ { - "Text": "/*!\u003c protected field comment */\n", + "Text": "/*!\u003c protected field comment */", "_Type": "Comment" } ], @@ -672,15 +636,7 @@ "Doc": { "List": [ { - "Text": "/**\n", - "_Type": "Comment" - }, - { - "Text": " * method doc\n", - "_Type": "Comment" - }, - { - "Text": " */\n", + "Text": "/**\n * method doc\n */", "_Type": "Comment" } ], diff --git a/_xtool/internal/parser/testdata/forwarddecl1/expect.json b/_xtool/internal/parser/testdata/forwarddecl1/expect.json index ed56af943..ec52db27a 100755 --- a/_xtool/internal/parser/testdata/forwarddecl1/expect.json +++ b/_xtool/internal/parser/testdata/forwarddecl1/expect.json @@ -384,7 +384,7 @@ "Comment": { "List": [ { - "Text": "/* Methods for an open file */\n", + "Text": "/* Methods for an open file */", "_Type": "Comment" } ], @@ -663,11 +663,11 @@ "Doc": { "List": [ { - "Text": "// char in ast will got unsigned char \u0026 signed char and they are same in go\n", + "Text": "// char in ast will got unsigned char \u0026 signed char and they are same in go", "_Type": "Comment" }, { - "Text": " // but in ast,will have different,but with compare test,we need avoid these senario\n", + "Text": " // but in ast,will have different,but with compare test,we need avoid these senario", "_Type": "Comment" } ], @@ -700,7 +700,7 @@ "Comment": { "List": [ { - "Text": "/* active function */\n", + "Text": "/* active function */", "_Type": "Comment" } ], diff --git a/_xtool/internal/parser/testdata/typeof/expect.json b/_xtool/internal/parser/testdata/typeof/expect.json index 542ca7d97..00053e908 100755 --- a/_xtool/internal/parser/testdata/typeof/expect.json +++ b/_xtool/internal/parser/testdata/typeof/expect.json @@ -5,7 +5,7 @@ "Doc": { "List": [ { - "Text": "// https://github.com/goplus/llcppg/issues/497\n", + "Text": "// https://github.com/goplus/llcppg/issues/497", "_Type": "Comment" } ], diff --git a/cl/internal/convert/_testdata/gettext/gogensig.expect b/cl/internal/convert/_testdata/gettext/gogensig.expect index ae8de4534..24ebdfb0c 100644 --- a/cl/internal/convert/_testdata/gettext/gogensig.expect +++ b/cl/internal/convert/_testdata/gettext/gogensig.expect @@ -17,7 +17,7 @@ type MessageIteratorType struct { type MessageIteratorT *MessageIteratorType /* Create an iterator for traversing a domain of a PO file in memory. - The domain NULL denotes the default domain. */ + The domain NULL denotes the default domain. */ // //go:linkname MessageIterator C.po_message_iterator func MessageIterator(file FileT, domain *c.Char) MessageIteratorT diff --git a/cl/internal/convert/_testdata/gpgerror/gogensig.expect b/cl/internal/convert/_testdata/gpgerror/gogensig.expect index 0b2e67c6b..0fbb9a411 100644 --- a/cl/internal/convert/_testdata/gpgerror/gogensig.expect +++ b/cl/internal/convert/_testdata/gpgerror/gogensig.expect @@ -9,7 +9,7 @@ import ( type ErrorT c.Uint /* Return a pointer to a string containing a description of the error - * code in the error value ERR. This function is not thread-safe. */ + * code in the error value ERR. This function is not thread-safe. */ // // llgo:link ErrorT.Strerror C.gpg_strerror func (recv_ ErrorT) Strerror() *c.Char { return nil @@ -21,14 +21,14 @@ func (recv_ ErrorT) Strerror() *c.Char { * the system. If the function succeeds, 0 is returned and BUF * contains the string describing the error. If the buffer was not * large enough, ERANGE is returned and BUF contains as much of the - * beginning of the error string as fits into the buffer. */ + * beginning of the error string as fits into the buffer. */ // // llgo:link ErrorT.StrerrorR C.gpg_strerror_r func (recv_ ErrorT) StrerrorR(buf *c.Char, buflen c.SizeT) c.Int { return 0 } /* Return a pointer to a string containing a description of the error - * source in the error value ERR. */ + * source in the error value ERR. */ // // llgo:link ErrorT.Strsource C.gpg_strsource func (recv_ ErrorT) Strsource() *c.Char { return nil @@ -51,7 +51,7 @@ type GpgrtLockT struct { } /* NB: If GPGRT_LOCK_DEFINE is not used, zero out the lock variable - before passing it to gpgrt_lock_init. */ + before passing it to gpgrt_lock_init. */ // // llgo:link (*GpgrtLockT).LockInit C.gpgrt_lock_init func (recv_ *GpgrtLockT) LockInit() CodeT { return 0 diff --git a/cl/internal/convert/_testdata/issue507/gogensig.expect b/cl/internal/convert/_testdata/issue507/gogensig.expect index 31c764180..ffece9fa1 100644 --- a/cl/internal/convert/_testdata/issue507/gogensig.expect +++ b/cl/internal/convert/_testdata/issue507/gogensig.expect @@ -31,36 +31,26 @@ type Ip4Addr struct { } type Ip4AddrT Ip4Addr -/** Args to LWIP_NSC_LINK_CHANGED callback */ - -type LinkChangedS struct { +/** Args to LWIP_NSC_LINK_CHANGED callback */ type LinkChangedS struct { State c.Int } -/** Args to LWIP_NSC_STATUS_CHANGED callback */ - -type StatusChangedS struct { +/** Args to LWIP_NSC_STATUS_CHANGED callback */ type StatusChangedS struct { State c.Int } -/** Args to LWIP_NSC_IPV4_ADDRESS_CHANGED|LWIP_NSC_IPV4_GATEWAY_CHANGED|LWIP_NSC_IPV4_NETMASK_CHANGED|LWIP_NSC_IPV4_SETTINGS_CHANGED callback */ - -type Ipv4ChangedS struct { +/** Args to LWIP_NSC_IPV4_ADDRESS_CHANGED|LWIP_NSC_IPV4_GATEWAY_CHANGED|LWIP_NSC_IPV4_NETMASK_CHANGED|LWIP_NSC_IPV4_SETTINGS_CHANGED callback */ type Ipv4ChangedS struct { OldAddress *IpAddrT OldNetmask *IpAddrT OldGw *IpAddrT } -/** Args to LWIP_NSC_IPV6_SET callback */ - -type Ipv6SetS struct { +/** Args to LWIP_NSC_IPV6_SET callback */ type Ipv6SetS struct { AddrIndex c.Int OldAddress *IpAddrT } -/** Args to LWIP_NSC_IPV6_ADDR_STATE_CHANGED callback */ - -type Ipv6AddrStateChangedS struct { +/** Args to LWIP_NSC_IPV6_ADDR_STATE_CHANGED callback */ type Ipv6AddrStateChangedS struct { AddrIndex c.Int OldState c.Int Address *IpAddrT diff --git a/cl/internal/convert/_testdata/keepcomment/gogensig.expect b/cl/internal/convert/_testdata/keepcomment/gogensig.expect index 89f8af89c..c53cfd220 100644 --- a/cl/internal/convert/_testdata/keepcomment/gogensig.expect +++ b/cl/internal/convert/_testdata/keepcomment/gogensig.expect @@ -13,11 +13,9 @@ import ( _ "unsafe" ) -/* -* +/** Foo comment -*/ -type Foo1 struct { +*/type Foo1 struct { A c.Int B c.Double C c.Int @@ -25,8 +23,7 @@ type Foo1 struct { /* Foo comment -*/ -type Foo2 struct { +*/type Foo2 struct { A c.Int B c.Double C c.Int @@ -48,13 +45,13 @@ type Foo4 struct { /** ExecuteFoo comment -*/ +*/ // //go:linkname CustomExecuteFoo1 C.ExecuteFoo1 func CustomExecuteFoo1(a c.Int, b Foo1) c.Int /* ExecuteFoo comment -*/ +*/ // //go:linkname CustomExecuteFoo2 C.ExecuteFoo2 func CustomExecuteFoo2(a c.Int, b Foo2) c.Int diff --git a/cl/internal/convert/_testdata/xml2/gogensig.expect b/cl/internal/convert/_testdata/xml2/gogensig.expect index 7db1b5444..3d1117c5a 100644 --- a/cl/internal/convert/_testdata/xml2/gogensig.expect +++ b/cl/internal/convert/_testdata/xml2/gogensig.expect @@ -180,7 +180,7 @@ import ( /* * Originally declared in xmlversion.h which is generated - */ + */ // //go:linkname CheckVersion C.xmlCheckVersion func CheckVersion(version c.Int) @@ -196,7 +196,7 @@ type Char c.Char /* * xmlChar handling - */ + */ // // llgo:link (*Char).Strdup C.xmlStrdup func (recv_ *Char) Strdup() *Char { return nil diff --git a/cl/internal/convert/comments.go b/cl/internal/convert/comments.go index 8a634bf7c..c4be383bb 100644 --- a/cl/internal/convert/comments.go +++ b/cl/internal/convert/comments.go @@ -33,14 +33,39 @@ func NewCommentGroup(comments ...*goast.Comment) *goast.CommentGroup { func NewCommentGroupFromC(doc *ast.CommentGroup) *goast.CommentGroup { goDoc := &goast.CommentGroup{} - if doc != nil && doc.List != nil { + if doc == nil || len(doc.List) == 0 { + return goDoc + } + if strings.HasPrefix(doc.List[0].Text, "/*") { + // Block comment: reassemble into a single Go ast.Comment node. + // This handles both correctly formed single-node block comments + // and legacy split block comments from older llcppsigfetch builds. + var b strings.Builder + for _, comment := range doc.List { + b.WriteString(comment.Text) + } + text := strings.TrimRight(b.String(), "\n") + goDoc.List = []*goast.Comment{{Slash: token.NoPos, Text: text}} + } else { + // Line comments: each comment is a separate node. for _, comment := range doc.List { - goDoc.List = append(goDoc.List, - &goast.Comment{ - Slash: token.NoPos, Text: comment.Text, - }, - ) + text := strings.TrimRight(comment.Text, "\n") + if text != "" { + goDoc.List = append(goDoc.List, &goast.Comment{ + Slash: token.NoPos, Text: text, + }) + } } } return goDoc } + +// hasBlockComment reports whether the comment group contains a block comment (/* ... */). +func hasBlockComment(doc *goast.CommentGroup) bool { + for _, c := range doc.List { + if strings.HasPrefix(c.Text, "/*") { + return true + } + } + return false +} diff --git a/cl/internal/convert/comments_test.go b/cl/internal/convert/comments_test.go new file mode 100644 index 000000000..c741003c2 --- /dev/null +++ b/cl/internal/convert/comments_test.go @@ -0,0 +1,230 @@ +package convert_test + +import ( + goast "go/ast" + "go/parser" + "go/token" + "strings" + "testing" + + "github.com/goplus/gogen" + "github.com/goplus/llcppg/ast" + "github.com/goplus/llcppg/cl/internal/convert" +) + +func TestNewCommentGroupFromC(t *testing.T) { + tests := []struct { + name string + doc *ast.CommentGroup + expected []string + }{ + { + name: "nil_input", + doc: nil, + expected: nil, + }, + { + name: "empty_list", + doc: &ast.CommentGroup{List: nil}, + expected: nil, + }, + { + name: "single_block_comment", + doc: &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "/* This is a block comment */"}, + }, + }, + expected: []string{"/* This is a block comment */"}, + }, + { + name: "multi_line_block_comment", + doc: &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "/* Create an iterator for traversing a domain\n The domain NULL denotes the default domain */"}, + }, + }, + expected: []string{"/* Create an iterator for traversing a domain\n The domain NULL denotes the default domain */"}, + }, + { + name: "legacy_split_block_two_nodes", + doc: &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "/* Create an iterator for traversing a domain\n"}, + {Text: " The domain NULL denotes the default domain. */\n"}, + }, + }, + expected: []string{"/* Create an iterator for traversing a domain\n The domain NULL denotes the default domain. */"}, + }, + { + name: "legacy_split_block_three_nodes", + doc: &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "/* Return the error string for ERR in the user-supplied buffer BUF of\n"}, + {Text: " * size BUFLEN. This function is, in contrast to gpg_strerror,\n"}, + {Text: " * thread-safe. */\n"}, + }, + }, + expected: []string{"/* Return the error string for ERR in the user-supplied buffer BUF of\n * size BUFLEN. This function is, in contrast to gpg_strerror,\n * thread-safe. */"}, + }, + { + name: "legacy_javadoc_style", + doc: &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "/**\n"}, + {Text: "Foo comment\n"}, + {Text: "*/\n"}, + }, + }, + expected: []string{"/**\nFoo comment\n*/"}, + }, + { + name: "single_line_comment", + doc: &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "// Foo comment"}, + }, + }, + expected: []string{"// Foo comment"}, + }, + { + name: "multiple_line_comments", + doc: &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "// First line\n"}, + {Text: "// Second line\n"}, + }, + }, + expected: []string{"// First line", "// Second line"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + goDoc := convert.NewCommentGroupFromC(tt.doc) + if len(goDoc.List) != len(tt.expected) { + t.Fatalf("expected %d comments, got %d", len(tt.expected), len(goDoc.List)) + } + for i, want := range tt.expected { + if goDoc.List[i].Text != want { + t.Errorf("comment[%d]: got %q, want %q", i, goDoc.List[i].Text, want) + } + } + }) + } +} + +// TestMultiLineBlockCommentWithGogen verifies that multi-line block comments +// generate valid Go code when processed through gogen. +func TestMultiLineBlockCommentWithGogen(t *testing.T) { + tests := []struct { + name string + comment string + }{ + { + name: "two_line_block", + comment: "/* Create an iterator for traversing a domain\n The domain NULL denotes the default domain */", + }, + { + name: "multi_line_with_asterisks", + comment: "/* Return a pointer to a string containing a description of the error\n * code in the error value ERR. This function is not thread-safe. */", + }, + { + name: "single_line_block", + comment: "/* A simple comment */", + }, + { + name: "javadoc_style", + comment: "/**\nFoo comment\n*/", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkg := gogen.NewPackage("", "demo", nil) + + commentGroup := &goast.CommentGroup{ + List: []*goast.Comment{ + {Slash: token.NoPos, Text: tt.comment}, + }, + } + + fn := pkg.NewFunc(nil, "ExampleFunction", nil, nil, false) + fn.SetComments(pkg, commentGroup) + fn.BodyStart(pkg).End() + + var buf strings.Builder + err := gogen.WriteTo(&buf, pkg, "") + if err != nil { + t.Fatalf("gogen.WriteTo error: %v", err) + } + + code := buf.String() + fset := token.NewFileSet() + _, err = parser.ParseFile(fset, "test.go", code, parser.ParseComments) + if err != nil { + t.Errorf("generated invalid Go code: %v\nCode:\n%s", err, code) + } + }) + } +} + +// TestBlockCommentWithLinkname verifies that block comments followed by +// //go:linkname directives produce valid Go code with the directive on its +// own line (not concatenated after the block comment closing "*/"). +func TestBlockCommentWithLinkname(t *testing.T) { + tests := []struct { + name string + comment string + }{ + { + name: "single_line_block", + comment: "/* A simple comment */", + }, + { + name: "multi_line_block", + comment: "/* Create an iterator for traversing a domain\n The domain NULL denotes the default domain */", + }, + { + name: "javadoc_style", + comment: "/**\nFoo comment\n*/", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkg := gogen.NewPackage("", "demo", nil) + + commentGroup := &goast.CommentGroup{ + List: []*goast.Comment{ + {Slash: token.NoPos, Text: tt.comment}, + {Slash: token.NoPos, Text: "//"}, + {Slash: token.NoPos, Text: "//go:linkname Foo C.Foo"}, + }, + } + + fn := pkg.NewFunc(nil, "Foo", nil, nil, false) + fn.SetComments(pkg, commentGroup) + fn.BodyStart(pkg).End() + + var buf strings.Builder + err := gogen.WriteTo(&buf, pkg, "") + if err != nil { + t.Fatalf("gogen.WriteTo error: %v", err) + } + + code := buf.String() + // The //go:linkname must be on its own line to avoid + // "misplaced compiler directive" errors. + if strings.Contains(code, "*///go:linkname") { + t.Errorf("//go:linkname should not be on the same line as */\nCode:\n%s", code) + } + + fset := token.NewFileSet() + _, err = parser.ParseFile(fset, "test.go", code, parser.ParseComments) + if err != nil { + t.Errorf("generated invalid Go code: %v\nCode:\n%s", err, code) + } + }) + } +} diff --git a/cl/internal/convert/package.go b/cl/internal/convert/package.go index cb5234f0e..901314618 100644 --- a/cl/internal/convert/package.go +++ b/cl/internal/convert/package.go @@ -197,6 +197,9 @@ func (p *Package) handleFuncDecl(fnSpec *GoFuncSpec, sig *types.Signature, funcD } doc := NewCommentGroupFromC(funcDecl.Doc) + if hasBlockComment(doc) { + doc.List = append(doc.List, &goast.Comment{Slash: token.NoPos, Text: "//"}) + } doc.List = append(doc.List, NewFuncDocComment(funcDecl.Name.Name, fnPubName)) decl.SetComments(p.p, doc) return nil 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=